Skip to content

Commit 0ab0b67

Browse files
feat(Data Mapper): Cherry-picking January Data Mapper changes for public preview (#6470)
* Feat(Data Mapper): Panel with warning and error messages (#6315) * started moving to v9 component * api call works * added value * started test * simplifying * update to fix fluent ui bug * component test passes * fixed type error * update version of fluent 9 * updated snapshots * updated lockfile * starting docs * moved over v1 map checker * simple panel works * new error store * added docs and displayed deserialization messages * panels close when others open * added required input checeker * PR prep * PR Comments * updated yaml for tests * feat(Data Mapper): Add keyboard delete, fix circles cutting, handle existing map loading (#6329) * add keyboard delete * only use delete when funciton or edge is selected * remove backspace * update width to handle circles cutting * fix auto layouting * Feat(Data Mapper): UX fixes for Public Preview (#6363) * added new map placeholder * added some colors * custom info label * PR prep * Fix(Data Mapper): Sequence function serialization bug (#6378) * fixed serialization issue * test fix * fix(Data Mapper): Fix circles cutting off (#6379) data mapper update * Fix(Data Mapper): bracket serialization bug (#6385) * fixed bracket bug * fixed typo in test * feat(Data Mapper): Add notification to vscode from DM (#6390) * setup vscode notification * update toast to vscode * fix(Data Mapper): Update Function config panel to fix wrapping, delete logic, placement of action items, info bubble (#6393) * dm font * fix func config panel * update function config 2 * fix style cherry-pick * add remove logic * fix margin * feat(Data Mapper): Add info message and fix placeholders (#6405) info message * Fix(Data Mapper): add new files (#6396) * starting using vscode file api * fixing file fn * file copy works and with schema imports * error message works * added info label * fix(Data Mapper): Update height/width of panel and enhance Error/Warning panel (#6410) * fix height * enhance test panel * fix(Data Mapper): Update string and open all functions by default (#6413) open by default * update strings * merge main * merge * merge string changes * update string * Feat(Data Mapper): Loop message when users try to find loop in functions (#6451) message works * fix(Data Mapper): Fix unnecessary edge when node/parent is not in the search results (#6454) * fix(Data Mapper): Pass in loading as a normal prop since boolean behaves differently (#6456) loading passed * fix(Data Mapper): Remove extra styling and clear out test results (#6459) * update node position on search term change * clear out test panel * update circles * fix(Data Mapper): Add subtitle for error/warning cards (#6464) * node udpates * add subtitle --------- Co-authored-by: DanielleCogs <[email protected]>
1 parent 9d14ca2 commit 0ab0b67

File tree

80 files changed

+2694
-681
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

80 files changed

+2694
-681
lines changed

Localize/lang/strings.json

+42-10
Large diffs are not rendered by default.

apps/Standalone/src/dataMapperV1/app/DataMapperStandaloneDesignerV2.tsx

+4
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ class DataMapperFileService implements IDataMapperFileService {
5050
this.verbose = verbose;
5151
}
5252

53+
public sendNotification(title: string, text: string, level: number) {
54+
console.log(`Notification: ${title}, data: ${text}, level: ${level}`);
55+
}
56+
5357
public saveMapDefinitionCall = (dataMapDefinition: string, mapMetadata: string) => {
5458
if (this.verbose) {
5559
console.log('Saved definition: ', dataMapDefinition);

apps/Standalone/src/dataMapperV1/components/DevToolbox.tsx

+1-2
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ const mapDefinitionDropdownOptions: MapDefDropdownOption[] = [
3030
data: {
3131
mapDefinitionString: fullTranscriptMapDefinitionString,
3232
mapMetadataString: JSON.stringify(testMetadata),
33-
associatedSchemaIdx: 2,
33+
associatedSchemaIdx: 1,
3434
},
3535
},
3636
{
@@ -58,7 +58,6 @@ const sourceSchemaFileOptions: SchemaFileData[] = [
5858
];
5959
const targetSchemaFileOptions: SchemaFileData[] = [
6060
{ filename: 'PlaygroundTargetSchema.json', schemaFormat: SchemaFileFormat.XML },
61-
{ filename: 'OebsProjectRequest.json', schemaFormat: SchemaFileFormat.XML },
6261
{ filename: 'TargetSchema.json', schemaFormat: SchemaFileFormat.XML },
6362
{ filename: 'ComprehensiveTargetSchema.json', schemaFormat: SchemaFileFormat.XML },
6463
{ filename: 'TargetSchemaJson.json', schemaFormat: SchemaFileFormat.JSON },
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Features
2+
Helper docs for how some of our features work
3+
## Error Panel
4+
Checks run by MapCheckerPanel
5+
- Component MapCheckerPanel updates and runs map checks using the connections and schema
6+
- These are not currently stored in Redux, but could be if we allow users to dismiss warnings in the future
7+
- Currently we check for
8+
- Type checking for target schema nodes, but not functions until we get data from the backend
9+
- Missing required inputs on target nodes
10+
- Missing required inputs for functions
11+
12+
Deserialization Warnings
13+
- We generate warnings during deserialization if we are unable to find nodes or functions
14+
- These warnings are stored in Redux
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
2+
# Serialization
3+
4+
The process of converting the format used to represent the visual map canvas to LML that can be edited by the user and processed by the backend
5+
6+
### GenerateMapDefinitionBody
7+
- filters connections for the ones that end in a target node
8+
- loops through these in random order and
9+
- generates key-value pair array for this connection by calling createNewPathItems; this is every pair in the LML from root to final target item
10+
- calls applyValueAtPath for this array of pairs to insert it into the new LML, traversing down to where the pair belongs
11+
12+
### createNewPathItems
13+
14+
### applyValueAtPath
15+
- goes from the target connection passed, and using 'pathToRoot', adding all loops and conditionals to the path

apps/vs-code-designer/src/app/commands/dataMapper/DataMapperPanel.ts

+62-43
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
supportedSchemaFileExts,
1414
supportedCustomXsltFileExts,
1515
} from './extensionConfig';
16-
import type { SchemaType, MapMetadata, IFileSysTreeItem } from '@microsoft/logic-apps-shared';
16+
import { type SchemaType, type MapMetadata, type IFileSysTreeItem, LogEntryLevel } from '@microsoft/logic-apps-shared';
1717
import type { IActionContext } from '@microsoft/vscode-azext-utils';
1818
import { callWithTelemetryAndErrorHandlingSync } from '@microsoft/vscode-azext-utils';
1919
import type { MapDefinitionData, MessageToVsix, MessageToWebview } from '@microsoft/vscode-extension-logic-apps';
@@ -31,6 +31,7 @@ import * as path from 'path';
3131
import type { WebviewPanel } from 'vscode';
3232
import { RelativePattern, window, workspace } from 'vscode';
3333
import * as vscode from 'vscode';
34+
import { copyOverImportedSchemas } from './DataMapperPanelUtils';
3435

3536
export default class DataMapperPanel {
3637
public panel: WebviewPanel;
@@ -114,7 +115,10 @@ export default class DataMapperPanel {
114115
}
115116
case ExtensionCommand.webviewLoaded: {
116117
// Send runtime port to webview
117-
this.sendMsgToWebview({ command: ExtensionCommand.setRuntimePort, data: `${ext.designTimePort}` });
118+
this.sendMsgToWebview({
119+
command: ExtensionCommand.setRuntimePort,
120+
data: `${ext.designTimePort}`,
121+
});
118122

119123
// If loading a data map, handle that + xslt filename
120124
this.handleLoadMapDefinitionIfAny();
@@ -127,7 +131,7 @@ export default class DataMapperPanel {
127131
break;
128132
}
129133
case ExtensionCommand.addSchemaFromFile: {
130-
this.addSchemaFromFile(msg.data.path, msg.data.type);
134+
this.addSchemaFromFile(msg.data);
131135
break;
132136
}
133137
case ExtensionCommand.readLocalSchemaFileOptions: {
@@ -174,6 +178,10 @@ export default class DataMapperPanel {
174178
ext.telemetryReporter.sendTelemetryEvent(eventName, { ...msg.data });
175179
break;
176180
}
181+
case ExtensionCommand.sendNotification: {
182+
this.sendNotification(msg.data.title, msg.data.text, msg.data.level);
183+
break;
184+
}
177185
}
178186
}
179187

@@ -271,6 +279,28 @@ export default class DataMapperPanel {
271279
}
272280
}
273281

282+
public sendNotification(title: string, text: string, level: number) {
283+
const msg = localize(title, text);
284+
switch (level) {
285+
case LogEntryLevel.Error: {
286+
ext.showError(msg);
287+
break;
288+
}
289+
case LogEntryLevel.Warning: {
290+
ext.showWarning(msg);
291+
break;
292+
}
293+
case LogEntryLevel.Verbose: {
294+
ext.showInformation(msg);
295+
break;
296+
}
297+
default: {
298+
ext.log(msg);
299+
break;
300+
}
301+
}
302+
}
303+
274304
private getFilesForPath(
275305
folderPath: string,
276306
command: typeof ExtensionCommand.showAvailableSchemas | typeof ExtensionCommand.getAvailableCustomXsltPaths,
@@ -301,51 +331,40 @@ export default class DataMapperPanel {
301331
});
302332
}
303333

304-
public addSchemaFromFile(filePath: string, schemaType: SchemaType) {
334+
public addSchemaFromFile(schemaType: SchemaType) {
305335
callWithTelemetryAndErrorHandlingSync(extensionCommand.dataMapAddSchemaFromFile, (_context: IActionContext) => {
306-
fs.readFile(filePath, 'utf8').then((text: string) => {
307-
const primarySchemaFileName = path.basename(filePath); // Ex: inpSchema.xsd
308-
const expectedPrimarySchemaPath = path.join(ext.logicAppWorkspace, schemasPath, primarySchemaFileName);
309-
310-
// Examine the loaded text for the 'schemaLocation' attribute to auto-load in any dependencies too
311-
// NOTE: We only check in the same directory as the primary schema file (also, it doesn't attempt to deal with complicated paths/URLs, just filenames)
312-
const schemaFileDependencies = [...text.matchAll(/schemaLocation="[A-Za-z.]*"/g)].map((schemaFileAttributeMatch) => {
313-
// Trim down to just the filename
314-
return schemaFileAttributeMatch[0].split('"')[1];
315-
});
336+
const fileSelectOptions: vscode.OpenDialogOptions = {
337+
filters: { Schemas: ['xsd', 'json'] },
338+
canSelectMany: false,
339+
};
340+
window.showOpenDialog(fileSelectOptions).then((files) => {
341+
if (!files[0]) {
342+
return;
343+
}
344+
const selectedFile = files[0];
316345

317-
schemaFileDependencies.forEach((schemaFile) => {
318-
const schemaFilePath = path.join(path.dirname(filePath), schemaFile);
319-
320-
// Check that the schema file dependency exists in the same directory as the primary schema file
321-
if (!fileExistsSync(schemaFilePath)) {
322-
ext.showError(
323-
localize(
324-
'SchemaLoadingError',
325-
`Schema loading error: couldn't find schema file dependency
326-
"{0}" in the same directory as "{1}". "{1}" will still be copied to the Schemas folder.`,
327-
schemaFile,
328-
primarySchemaFileName
329-
)
330-
);
331-
return;
332-
}
346+
const pathToWorkspaceSchemaFolder = path.join(ext.logicAppWorkspace, schemasPath);
347+
const primarySchemaFullPath = selectedFile.path;
348+
const pathToContainingFolder = path.dirname(primarySchemaFullPath);
349+
const primarySchemaFileName = path.basename(primarySchemaFullPath);
333350

334-
// Check that the schema file dependency doesn't already exist in the Schemas folder
335-
const expectedSchemaFilePath = path.join(ext.logicAppWorkspace, schemasPath, schemaFile);
336-
if (!fileExistsSync(expectedSchemaFilePath)) {
337-
copyFileSync(schemaFilePath, expectedSchemaFilePath);
338-
}
339-
});
351+
workspace.fs.readFile(selectedFile).then((fileContents) => {
352+
const text = Buffer.from(fileContents).toString('utf-8');
340353

341-
// Check if in Artifacts/Schemas, and if not, create it and send it to DM for API call
342-
if (!fileExistsSync(expectedPrimarySchemaPath)) {
343-
copyFileSync(filePath, expectedPrimarySchemaPath);
344-
}
354+
copyOverImportedSchemas(text, primarySchemaFileName, pathToContainingFolder, pathToWorkspaceSchemaFolder, ext);
345355

346-
this.sendMsgToWebview({
347-
command: ExtensionCommand.fetchSchema,
348-
data: { fileName: primarySchemaFileName, type: schemaType as SchemaType },
356+
const newPath = path.join(pathToWorkspaceSchemaFolder, primarySchemaFileName);
357+
if (!fileExistsSync(newPath)) {
358+
copyFileSync(primarySchemaFullPath, newPath);
359+
}
360+
361+
this.sendMsgToWebview({
362+
command: ExtensionCommand.fetchSchema,
363+
data: {
364+
fileName: primarySchemaFileName,
365+
type: schemaType as SchemaType,
366+
},
367+
});
349368
});
350369
});
351370
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import path from 'path';
2+
import { copyFileSync, existsSync as fileExistsSync } from 'fs';
3+
import { localize } from '../../../localize';
4+
5+
export const copyOverImportedSchemas = (
6+
schemaText: string,
7+
primarySchemaName: string,
8+
pathToContainingFolder: string,
9+
pathToWorkspaceSchemaFolder: string,
10+
ext
11+
) => {
12+
const schemaFileDependencies = [...schemaText.matchAll(/schemaLocation="[A-Za-z.]*"/g)].map((schemaFileAttributeMatch) => {
13+
// Trim down to just the filename
14+
return schemaFileAttributeMatch[0].split('"')[1];
15+
});
16+
17+
schemaFileDependencies.forEach((importedSchemaFileName) => {
18+
const importedSchemaFileFullPath = path.join(pathToContainingFolder, importedSchemaFileName);
19+
20+
// Check that the schema file dependency exists in the same directory as the primary schema file
21+
if (!fileExistsSync(importedSchemaFileFullPath)) {
22+
ext.showError(
23+
localize(
24+
'SchemaLoadingError',
25+
`Schema loading error: couldn't find schema file import
26+
"{0}" in the same directory as "{1}". "{2}" will still be copied to the Schemas folder.`,
27+
importedSchemaFileName,
28+
pathToContainingFolder,
29+
primarySchemaName
30+
)
31+
);
32+
return;
33+
}
34+
35+
// Check that the schema file dependency doesn't already exist in the Schemas folder
36+
const newSchemaFilePath = path.join(pathToWorkspaceSchemaFolder, importedSchemaFileName);
37+
if (!fileExistsSync(newSchemaFilePath)) {
38+
copyFileSync(importedSchemaFileFullPath, newSchemaFilePath);
39+
}
40+
});
41+
};

apps/vs-code-designer/src/app/commands/dataMapper/extensionConfig.ts

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export const webviewType = 'dataMapperWebview';
33
export const supportedDataMapDefinitionFileExts = ['.lml', '.yml'];
44
export const supportedSchemaFileExts = ['.xsd', '.json'];
55
export const supportedCustomXsltFileExts = ['.xslt', '.xml'];
6+
export const supportedDataMapperFolders = ['Maps', 'MapDefinitions', 'Schemas'];
67

78
const artifactsPath = '/Artifacts/';
89
export const schemasPath = `${artifactsPath}/Schemas`;

apps/vs-code-designer/src/constants.ts

+1
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ export const extensionCommand = {
166166
dataMapSetSupportedDataMapDefinitionFileExts: 'azureLogicAppsStandard.dataMap.setSupportedDataMapDefinitionFileExts',
167167
dataMapSetSupportedSchemaFileExts: 'azureLogicAppsStandard.dataMap.setSupportedSchemaFileExts',
168168
dataMapSetSupportedFileExts: 'azureLogicAppsStandard.dataMap.setSupportedFileExts',
169+
dataMapSetDmFolders: 'azureLogicAppsStandard.dataMap.setDmFolders',
169170
dataMapSaveMapDefinition: 'azureLogicAppsStandard.dataMap.saveMapDefinition',
170171
dataMapSaveMapXslt: 'azureLogicAppsStandard.dataMap.saveMapXslt',
171172
vscodeOpenFolder: 'vscode.openFolder',

apps/vs-code-designer/src/extensionVariables.ts

+4
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@ export namespace ext {
8888
window.showWarningMessage(errMsg);
8989
};
9090

91+
export const showInformation = (msg: string) => {
92+
window.showInformationMessage(msg);
93+
};
94+
9195
export const showError = (errMsg: string, options?: MessageOptions) => {
9296
ext.log(errMsg);
9397
if (options && options.detail) {

apps/vs-code-designer/src/main.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import { LogicAppResolver } from './LogicAppResolver';
22
import { runPostWorkflowCreateStepsFromCache } from './app/commands/createCodeless/createCodelessSteps/WorkflowCreateStepBase';
3-
import { supportedDataMapDefinitionFileExts, supportedSchemaFileExts } from './app/commands/dataMapper/extensionConfig';
3+
import {
4+
supportedDataMapDefinitionFileExts,
5+
supportedDataMapperFolders,
6+
supportedSchemaFileExts,
7+
} from './app/commands/dataMapper/extensionConfig';
48
import { promptParameterizeConnections } from './app/commands/parameterizeConnections';
59
import { registerCommands } from './app/commands/registerCommands';
610
import { getResourceGroupsApi } from './app/resourcesExtension/getExtensionApi';
@@ -36,6 +40,7 @@ const perfStats = {
3640
const telemetryString = 'setInGitHubBuild';
3741

3842
export async function activate(context: vscode.ExtensionContext) {
43+
// Data Mapper context
3944
vscode.commands.executeCommand(
4045
'setContext',
4146
extensionCommand.dataMapSetSupportedDataMapDefinitionFileExts,
@@ -46,6 +51,7 @@ export async function activate(context: vscode.ExtensionContext) {
4651
...supportedDataMapDefinitionFileExts,
4752
...supportedSchemaFileExts,
4853
]);
54+
vscode.commands.executeCommand('setContext', extensionCommand.dataMapSetDmFolders, supportedDataMapperFolders);
4955

5056
ext.context = context;
5157
ext.telemetryReporter = new TelemetryReporter(telemetryString);

apps/vs-code-designer/src/package.json

+11-15
Original file line numberDiff line numberDiff line change
@@ -327,12 +327,14 @@
327327
},
328328
{
329329
"command": "azureLogicAppsStandard.dataMap.createNewDataMap",
330-
"title": "Data Mapper: Create data map",
330+
"title": "Create Data Map",
331+
"category": "Azure Logic Apps",
331332
"icon": "$(add)"
332333
},
333334
{
334335
"command": "azureLogicAppsStandard.dataMap.loadDataMapFile",
335-
"title": "Data Mapper: Load existing data map"
336+
"title": "Data Mapper: Load existing data map",
337+
"when": "resourceExtname in azureLogicAppsStandard.dataMap.setSupportedDataMapDefinitionFileExts"
336338
},
337339
{
338340
"command": "azureLogicAppsStandard.parameterizeConnections",
@@ -349,10 +351,6 @@
349351
"dark": "assets/logicapp.png"
350352
},
351353
"label": "Azure Logic Apps"
352-
},
353-
{
354-
"id": "azureLogicAppsStandard.dataMapperMenu",
355-
"label": "Data Mapper"
356354
}
357355
],
358356
"views": {
@@ -627,6 +625,11 @@
627625
}
628626
],
629627
"explorer/context": [
628+
{
629+
"command": "azureLogicAppsStandard.dataMap.createNewDataMap",
630+
"when": "resourceFilename in azureLogicAppsStandard.dataMap.setDmFolders",
631+
"group": "navigation"
632+
},
630633
{
631634
"command": "azureLogicAppsStandard.deploy",
632635
"when": "explorerResourceIsFolder == true",
@@ -678,9 +681,9 @@
678681
"group": "navigation@2"
679682
},
680683
{
681-
"submenu": "azureLogicAppsStandard.dataMapperMenu",
684+
"command": "azureLogicAppsStandard.dataMap.loadDataMapFile",
682685
"group": "navigation",
683-
"when": "resourceExtname in azureLogicAppsStandard.dataMap.setSupportedFileExts"
686+
"when": "resourceExtname in azureLogicAppsStandard.dataMap.setSupportedDataMapDefinitionFileExts"
684687
}
685688
],
686689
"commandPalette": [
@@ -716,13 +719,6 @@
716719
"command": "azureLogicAppsStandard.generateDeploymentScripts",
717720
"when": "never"
718721
}
719-
],
720-
"azureLogicAppsStandard.dataMapperMenu": [
721-
{
722-
"command": "azureLogicAppsStandard.dataMap.loadDataMapFile",
723-
"group": "navigation",
724-
"when": "resourceExtname in azureLogicAppsStandard.dataMap.setSupportedDataMapDefinitionFileExts"
725-
}
726722
]
727723
},
728724
"configuration": [

0 commit comments

Comments
 (0)