apps/Standalone/src/designer/app/AzureLogicAppsDesigner/Utilities/Workflow.ts (172 lines of code) (raw):

import type { ConnectionAndAppSetting, ConnectionsData, ParametersData } from '../Models/Workflow'; import { isOpenApiSchemaVersion, type ConnectionReferences } from '@microsoft/logic-apps-designer'; export class WorkflowUtility { public static convertToCanonicalFormat(value: string): string { return value?.toLowerCase().replaceAll(' ', ''); } public static convertConnectionsDataToReferences(connectionsData: ConnectionsData | undefined): ConnectionReferences { const references: any = {}; if (!connectionsData) { return references; } const apiManagementConnections = connectionsData.apiManagementConnections || {}; const functionConnections = connectionsData.functionConnections || {}; const connectionReferences = connectionsData.managedApiConnections || {}; const serviceProviderConnections = connectionsData.serviceProviderConnections || {}; const agentConnections = connectionsData.agentConnections || {}; for (const connectionReferenceKey of Object.keys(connectionReferences)) { const { connection, api, connectionProperties, authentication } = connectionReferences[connectionReferenceKey]; references[connectionReferenceKey] = { connection: { id: connection ? connection.id : '' }, connectionName: connection && connection.id ? connection.id.split('/').slice(-1)[0] : '', api: { id: api ? api.id : '' }, connectionProperties, authentication, }; } const apimConnectorId = '/connectionProviders/apiManagementOperation'; for (const connectionKey of Object.keys(apiManagementConnections)) { references[connectionKey] = { connection: { id: `${apimConnectorId}/connections/${connectionKey}` }, connectionName: connectionKey, api: { id: apimConnectorId }, }; } const functionConnectorId = '/connectionProviders/azureFunctionOperation'; for (const connectionKey of Object.keys(functionConnections)) { references[connectionKey] = { connection: { id: `${functionConnectorId}/connections/${connectionKey}` }, connectionName: connectionKey, api: { id: functionConnectorId }, }; } for (const connectionKey of Object.keys(serviceProviderConnections)) { const serviceProviderId = serviceProviderConnections[connectionKey].serviceProvider.id; references[connectionKey] = { connection: { id: `${serviceProviderId}/connections/${connectionKey}` }, connectionName: serviceProviderConnections[connectionKey].displayName ?? connectionKey, api: { id: serviceProviderId }, }; } const agentConnectorId = 'connectionProviders/agent'; for (const connectionKey of Object.keys(agentConnections)) { references[connectionKey] = { connection: { id: `/${agentConnectorId}/connections/${connectionKey}` }, connectionName: connectionKey, // updated to use connectionKey directly api: { id: `/${agentConnectorId}` }, }; } return references; } public static resolveConnectionsReferences( content: string, parameters: ParametersData | undefined, appsettings?: Record<string, string> | undefined ): any { let result = content; if (parameters) { for (const parameterName of Object.keys(parameters)) { const parameterValue = parameters[parameterName].value !== undefined ? parameters[parameterName].value : ''; result = replaceAllOccurrences(result, `@parameters('${parameterName}')`, parameterValue); result = replaceAllOccurrences(result, `@{parameters('${parameterName}')}`, parameterValue); } } if (appsettings) { for (const settingName of Object.keys(appsettings)) { const settingValue = appsettings[settingName] !== undefined ? appsettings[settingName] : ''; // Don't replace if the setting value is a KeyVault reference if (typeof settingValue !== 'string' || settingValue.startsWith('@Microsoft.KeyVault(')) { continue; } result = replaceAllOccurrences(result, `@appsetting('${settingName}')`, settingValue); result = replaceAllOccurrences(result, `@{appsetting('${settingName}')}`, settingValue); } } try { return JSON.parse(result); } catch { throw new Error('Failure in resolving connection parameterisation'); } } } function replaceAllOccurrences(content: string, searchValue: string, value: any): string { while (content.includes(searchValue)) { const tempResult = replaceIfFoundAndVerifyJson(content, `"${searchValue}"`, JSON.stringify(value)) ?? replaceIfFoundAndVerifyJson(content, searchValue, `${value}`) ?? content.replace(searchValue, ''); content = tempResult; } return content; } function replaceIfFoundAndVerifyJson(stringifiedJson: string, searchValue: string, value: string): string | undefined { if (!stringifiedJson.includes(searchValue)) { return undefined; } const result = stringifiedJson.replace(searchValue, () => { return value; }); try { JSON.parse(result); return result; } catch { return undefined; } } export function addConnectionInJson(connectionAndSetting: ConnectionAndAppSetting, connectionsJson: ConnectionsData): void { const { connectionData, connectionKey, pathLocation } = connectionAndSetting; let pathToSetConnectionsData: any = connectionsJson; for (const path of pathLocation) { if (!pathToSetConnectionsData[path]) { pathToSetConnectionsData[path] = {}; } pathToSetConnectionsData = pathToSetConnectionsData[path]; } if (pathToSetConnectionsData && pathToSetConnectionsData[connectionKey]) { // TODO: To show this in a notification of info bar on the blade. // const message = 'ConnectionKeyAlreadyExist - Connection key \'{0}\' already exists.'.format(connectionKey); return; } pathToSetConnectionsData[connectionKey] = connectionData; } export function addOrUpdateAppSettings(settings: Record<string, string>, originalSettings: Record<string, string>): Record<string, string> { const settingsToAdd = Object.keys(settings); for (const settingKey of settingsToAdd) { if (originalSettings[settingKey]) { // TODO: To show this in a notification of info bar on the blade that key will be overriden. } // eslint-disable-next-line no-param-reassign originalSettings[settingKey] = settings[settingKey]; } return originalSettings; } export const getDataForConsumption = (data: any) => { const properties = data?.properties as any; const definition = removeProperties(properties?.definition, ['parameters']); const connections = (isOpenApiSchemaVersion(definition) ? properties?.connectionReferences : properties?.parameters?.$connections?.value) ?? {}; const workflow = { definition, connections }; const connectionReferences = formatConnectionReferencesForConsumption(connections); const parameters: ParametersData = formatWorkflowParametersForConsumption(properties); return { workflow, connectionReferences, parameters }; }; const removeProperties = (obj: any = {}, props: string[] = []): object => { return Object.fromEntries(Object.entries(obj).filter(([key]) => !props.includes(key))); }; const formatConnectionReferencesForConsumption = (connectionReferences: Record<string, any>): any => { return Object.fromEntries( Object.entries(connectionReferences).map(([key, value]) => [key, formatConnectionReferenceForConsumption(value)]) ); }; const formatConnectionReferenceForConsumption = (connectionReference: any): any => { const connectionReferenceCopy = { ...connectionReference }; connectionReferenceCopy.connection = connectionReference.connection ?? { id: connectionReference.connectionId }; delete connectionReferenceCopy.connectionId; connectionReferenceCopy.api = connectionReference.api ?? { id: connectionReference.id }; delete connectionReferenceCopy.id; return connectionReferenceCopy; }; const formatWorkflowParametersForConsumption = (properties: any): ParametersData => { const parameters = removeProperties(properties?.definition?.parameters, ['$connections']) as ParametersData; Object.entries(properties?.parameters ?? {}).forEach(([key, parameter]: [key: string, parameter: any]) => { if (parameters[key]) { parameters[key].value = parameter?.value; } }); return parameters; };