From fdf294eb4b2b7c6db940f11956df011820c1bdba Mon Sep 17 00:00:00 2001 From: Elaina Lee Date: Tue, 4 Mar 2025 11:58:22 -0800 Subject: [PATCH 01/24] added TemplateManifest and WorkflowManifest --- .../src/utils/src/lib/models/template.ts | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/libs/logic-apps-shared/src/utils/src/lib/models/template.ts b/libs/logic-apps-shared/src/utils/src/lib/models/template.ts index 749f04d8411..67bda444169 100644 --- a/libs/logic-apps-shared/src/utils/src/lib/models/template.ts +++ b/libs/logic-apps-shared/src/utils/src/lib/models/template.ts @@ -27,6 +27,47 @@ export interface Manifest { sourceCodeUrl?: string; // Automatically generated for public templates, otherwise optional } +export interface TemplateManifest { + id: string; + title: string; + description: string; + /* This is a markdown to show features for multi-workflow */ + detailsDescription?: string; + artifacts?: Artifact[]; + skus: SkuType[]; + + /* This consists of list of workflows listed in the multi-workflow template. + The key is the folder name, followed by metadata where name is default name to be used for creation */ + workflows?: Record; + featuredConnectors: { id: string; kind: string }[]; + details: { + By: string; + Type: string; + Category: string; + Trigger?: string; // undefined for accelerator + }; + tags?: string[]; + sourceCodeUrl?: string; // Automatically generated for public templates, otherwise not present +} + +export interface WorkflowManifest { + id: string; + title: string; + description: string; + detailsDescription?: string; + prerequisites?: string; + kinds?: WorkflowKindType[]; + artifacts?: Artifact[]; + images: { + light: string; + dark: string; + }; + parameters: Parameter[]; + connections: Record; + + sourceCodeUrl?: string; // Automatically generated for public templates, otherwise optional +} + export interface Artifact { type: string; file: string; From 612161ce0a37cd828e4ceccfbb2ea49ecf3fe671 Mon Sep 17 00:00:00 2001 From: Elaina Lee Date: Tue, 4 Mar 2025 17:10:05 -0800 Subject: [PATCH 02/24] first batch of restructure --- Localize/lang/strings.json | 2 - .../src/templates/app/LocalTemplates.tsx | 34 +++--- .../lib/core/actions/bjsworkflow/templates.ts | 102 ++++++++---------- .../lib/core/state/templates/manifestSlice.ts | 4 +- .../core/state/templates/templateselectors.ts | 8 ++ .../src/lib/core/templates/utils/helper.ts | 81 +++++++------- .../createWorkflowPanel.tsx | 19 +--- .../quickViewPanel/quickViewPanel.tsx | 29 ++--- .../quickViewPanel/tabs/summaryTab.tsx | 37 +++---- .../lib/ui/templates/basics/multiworkflow.tsx | 6 +- .../lib/ui/templates/cards/templateCard.tsx | 44 +++----- .../ui/templates/connections/connector.tsx | 6 +- .../ui/templates/filters/templateFilters.tsx | 43 ++++---- .../src/lib/ui/templates/templateoverview.tsx | 33 ++---- .../lib/base/template.ts | 34 +++--- .../designer-client-services/lib/template.ts | 7 +- .../src/utils/src/lib/models/template.ts | 26 +++-- 17 files changed, 249 insertions(+), 266 deletions(-) diff --git a/Localize/lang/strings.json b/Localize/lang/strings.json index a21ee22f485..3abdb5d0105 100644 --- a/Localize/lang/strings.json +++ b/Localize/lang/strings.json @@ -669,7 +669,6 @@ "MCzWDc": "Preview", "MDbmMw": "Required. The collections to evaluate. An object must be in all collections passed in to appear in the result.", "MGZRu4": "Add an action", - "MGq28G": "Trigger", "MIX4f9": "Unsupported programming language.", "MKTdNk": "Required. The data URI to convert to binary representation.", "MLCQzX": "Managed identity", @@ -1744,7 +1743,6 @@ "_MCzWDc.comment": "Recurrence preview title", "_MDbmMw.comment": "Required collection parameters to check intersection function on", "_MGZRu4.comment": "Chatbot prompt to add action", - "_MGq28G.comment": "Column name for trigger type", "_MIX4f9.comment": "The exception for an unsupported programming language.", "_MKTdNk.comment": "Required dataURI string parameter to be converted using dataUriToBinary function", "_MLCQzX.comment": "Managed Identity Label Display Name", diff --git a/apps/Standalone/src/templates/app/LocalTemplates.tsx b/apps/Standalone/src/templates/app/LocalTemplates.tsx index d0043ea52df..f2eb3a928ed 100644 --- a/apps/Standalone/src/templates/app/LocalTemplates.tsx +++ b/apps/Standalone/src/templates/app/LocalTemplates.tsx @@ -284,29 +284,37 @@ class LocalTemplateService extends StandardTemplateService { } }; - public getResourceManifest = async (resourcePath: string): Promise => { - const templateName = resourcePath.split('/')[0]; - if (localTemplateManifestPaths.includes(templateName)) { - return loadLocalTemplateFromResourcePath(resourcePath); + public getTemplateManifest = async (templateId: string): Promise => { + if (localTemplateManifestPaths.includes(templateId)) { + return loadLocalTemplateFromResourcePath(templateId); } - return this._options.service.getResourceManifest(resourcePath); + return this._options.service.getTemplateManifest(templateId); }; - public getWorkflowDefinition = async (resourcePath: string): Promise => { - const templateName = resourcePath.split('/')[0]; - if (localTemplateManifestPaths.includes(templateName)) { - return loadLocalTemplateFromResourcePath(resourcePath, 'workflow'); + public getWorkflowManifest = async (templateId: string, workflowId: string): Promise => { + if (localTemplateManifestPaths.includes(templateId)) { + return loadLocalTemplateFromResourcePath(`${templateId}/${workflowId}`); } - return this._options.service.getWorkflowDefinition(resourcePath); + return this._options.service.getWorkflowManifest(templateId, workflowId); }; - public getContentPathUrl = (templateName: string, resourcePath: string): string => { - if (localTemplateManifestPaths.includes(templateName)) { + public getWorkflowDefinition = async (templateId: string, workflowId: string): Promise => { + if (localTemplateManifestPaths.includes(templateId)) { + return loadLocalTemplateFromResourcePath(`${templateId}/${workflowId}`, 'workflow'); + } + + return this._options.service.getWorkflowDefinition(templateId, workflowId); + }; + + public getContentPathUrl = (templatePath: string, resourcePath: string): string => { + const templateId = templatePath.split('/')[0]; + + if (localTemplateManifestPaths.includes(templateId)) { return resourcePath; } - return this._options.service.getContentPathUrl(templateName, resourcePath); + return this._options.service.getContentPathUrl(templatePath, resourcePath); }; } diff --git a/libs/designer/src/lib/core/actions/bjsworkflow/templates.ts b/libs/designer/src/lib/core/actions/bjsworkflow/templates.ts index 755535c994b..682856e4e58 100644 --- a/libs/designer/src/lib/core/actions/bjsworkflow/templates.ts +++ b/libs/designer/src/lib/core/actions/bjsworkflow/templates.ts @@ -37,10 +37,13 @@ import { export interface WorkflowTemplateData { id: string; workflowDefinition: LogicAppsV2.WorkflowDefinition; - manifest: Template.Manifest; + manifest: Template.WorkflowManifest; workflowName: string | undefined; kind: string | undefined; - images?: Record; + images?: { + light?: string; + dark?: string; + }; connectionKeys: string[]; errors: { workflow: string | undefined; @@ -49,7 +52,7 @@ export interface WorkflowTemplateData { } export interface TemplatePayload { - manifest: Template.Manifest | undefined; + manifest: Template.TemplateManifest | undefined; workflows: Record; parameterDefinitions: Record; connections: Record; @@ -80,8 +83,8 @@ export const initializeWorkflowMetadata = createAsyncThunk( } ); -export const isMultiWorkflowTemplate = (manifest: Template.Manifest): boolean => { - return !!manifest.workflows && Object.keys(manifest.workflows).length > 0; +export const isMultiWorkflowTemplate = (manifest: Template.TemplateManifest): boolean => { + return Object.keys(manifest.workflows).length > 1; }; export const initializeTemplateServices = createAsyncThunk( @@ -155,14 +158,14 @@ export const reloadTemplates = createAsyncThunk('reloadTemplates', async ({ clea dispatch(loadGithubManifestNames()); }); -export const loadManifestsFromPaths = async (resourcePaths: string[]) => { +export const loadManifestsFromPaths = async (templateIds: string[]) => { try { - const manifestPromises = resourcePaths.map(async (resourcePath) => { - return TemplateService().getResourceManifest(resourcePath); + const manifestPromises = templateIds.map(async (templateId) => { + return TemplateService().getTemplateManifest(templateId); }); - const manifestsArray = await Promise.all(manifestPromises); - return manifestsArray.reduce((result: Record, manifestFile: any, index: number) => { - result[resourcePaths[index]] = manifestFile; + const templateManifestsArray = await Promise.all(manifestPromises); + return templateManifestsArray.reduce((result: Record, manifestFile: any, index: number) => { + result[templateIds[index]] = manifestFile; return result; }, {}); } catch (error) { @@ -178,7 +181,7 @@ export const loadManifestsFromPaths = async (resourcePaths: string[]) => { export const loadTemplate = createAsyncThunk( 'loadTemplate', - async ({ preLoadedManifest }: { preLoadedManifest: Template.Manifest | undefined }, thunkAPI) => { + async ({ preLoadedManifest }: { preLoadedManifest: Template.TemplateManifest | undefined }, thunkAPI) => { const currentState: RootState = thunkAPI.getState() as RootState; const currentTemplateName = currentState.template.templateName; const viewTemplateDetails = currentState.templateOptions.viewTemplateDetails; @@ -224,11 +227,11 @@ export const validateWorkflowName = (workflowName: string | undefined, existingW }; const loadTemplateFromResourcePath = async ( - templateName: string, - manifest: Template.Manifest | undefined, + templateId: string, + preloadedTemplateManifest: Template.TemplateManifest | undefined, viewTemplateData?: Template.ViewTemplateDetails ): Promise => { - const templateManifest: Template.Manifest = manifest ?? (await TemplateService().getResourceManifest(templateName)); + const templateManifest = preloadedTemplateManifest ?? (await TemplateService().getTemplateManifest(templateId)); const workflows = templateManifest.workflows; const isMultiWorkflow = isMultiWorkflowTemplate(templateManifest); @@ -244,16 +247,12 @@ const loadTemplateFromResourcePath = async ( }; if (isMultiWorkflow && workflows) { - for (const workflowPath of Object.keys(workflows)) { - const workflowData = await loadWorkflowTemplateFromManifest( - workflowPath, - `${templateName}/${workflowPath}`, - /* manifest */ undefined, - viewTemplateData - ); + for (const workflowId of Object.keys(workflows)) { + const workflowData = await loadWorkflowTemplate(templateId, workflowId, viewTemplateData); + if (workflowData) { - workflowData.workflow.workflowName = workflows[workflowPath].name; - data.workflows[workflowPath] = workflowData.workflow; + workflowData.workflow.workflowName = workflows[workflowId].name; + data.workflows[workflowId] = workflowData.workflow; data.parameterDefinitions = { ...data.parameterDefinitions, ...Object.keys(workflowData.parameterDefinitions).reduce((acc: Record, key: string) => { @@ -281,7 +280,7 @@ const loadTemplateFromResourcePath = async ( } } else { const workflowId = 'default'; - const workflowData = await loadWorkflowTemplateFromManifest(workflowId, templateName, manifest, viewTemplateData); + const workflowData = await loadWorkflowTemplate(templateId, workflowId, viewTemplateData); if (workflowData) { data.workflows = { @@ -295,10 +294,9 @@ const loadTemplateFromResourcePath = async ( return data; }; -const loadWorkflowTemplateFromManifest = async ( +const loadWorkflowTemplate = async ( + templateId: string, workflowId: string, - templatePath: string, - manifest: Template.Manifest | undefined, viewTemplateData: Template.ViewTemplateDetails | undefined ): Promise< | { @@ -309,12 +307,12 @@ const loadWorkflowTemplateFromManifest = async ( | undefined > => { try { - const { templateManifest, templateWorkflowDefinition } = await getWorkflowAndManifest(templatePath, manifest); - const parameterDefinitions = templateManifest.parameters?.reduce((result: Record, parameter) => { + const { workflowManifest, templateWorkflowDefinition } = await getWorkflowAndManifest(templateId, workflowId); + const parameterDefinitions = workflowManifest.parameters?.reduce((result: Record, parameter) => { result[parameter.name] = { ...parameter, value: viewTemplateData?.parametersOverride?.[parameter.name]?.value?.toString() ?? parameter.default, - associatedWorkflows: [templateManifest.title], + associatedWorkflows: [workflowManifest.title], }; return result; }, {}); @@ -325,52 +323,42 @@ const loadWorkflowTemplateFromManifest = async ( workflow: { id: workflowId, workflowDefinition: templateWorkflowDefinition, - manifest: templateManifest, + manifest: workflowManifest, workflowName: viewTemplateData?.basicsOverride?.[workflowId]?.name?.value ?? '', kind: - overridenKind && templateManifest.kinds?.includes(overridenKind) + overridenKind && workflowManifest.kinds?.includes(overridenKind) ? overridenKind - : templateManifest.kinds?.length - ? templateManifest.kinds[0] + : workflowManifest.kinds?.length + ? workflowManifest.kinds[0] : 'stateful', - images: Object.keys(templateManifest.images).reduce((result: Record, key: string) => { - result[key] = TemplateService().getContentPathUrl(templatePath, templateManifest.images[key]); - return result; - }, {}), - connectionKeys: Object.keys(templateManifest.connections), + images: { + light: TemplateService().getContentPathUrl(`${templateId}/${workflowId}`, workflowManifest.images.light), + dark: TemplateService().getContentPathUrl(`${templateId}/${workflowId}`, workflowManifest.images.dark), + }, + connectionKeys: Object.keys(workflowManifest.connections), errors: { workflow: undefined, kind: undefined, }, }, parameterDefinitions, - connections: templateManifest.connections, + connections: workflowManifest.connections, }; } catch (ex: any) { LoggerService().log({ level: LogEntryLevel.Error, - message: 'Error loading template', + message: 'Error loading workflow and manifest', area: 'Templates.GithubLoadTemplate', error: ex, - args: [templatePath], + args: [`${templateId}/${workflowId}`], }); return undefined; } }; -const getWorkflowAndManifest = async (templatePath: string, manifest: Template.Manifest | undefined) => { - const templateManifest = manifest ?? ((await getTemplateResourceGivenPath(templatePath, 'manifest')) as Template.Manifest); - - const templateWorkflowDefinition = (await getTemplateResourceGivenPath(templatePath, 'workflow')) as LogicAppsV2.WorkflowDefinition; - - return { templateManifest, templateWorkflowDefinition }; -}; +const getWorkflowAndManifest = async (templateId: string, workflowId: string) => { + const workflowManifest = await TemplateService().getWorkflowManifest(templateId, workflowId); + const templateWorkflowDefinition = await TemplateService().getWorkflowDefinition(templateId, workflowId); -const getTemplateResourceGivenPath = async ( - resourcePath: string, - artifactType: string -): Promise => { - return artifactType === 'manifest' - ? TemplateService().getResourceManifest(resourcePath) - : TemplateService().getWorkflowDefinition(resourcePath); + return { workflowManifest, templateWorkflowDefinition }; }; diff --git a/libs/designer/src/lib/core/state/templates/manifestSlice.ts b/libs/designer/src/lib/core/state/templates/manifestSlice.ts index 78abf1404b8..cd333f09842 100644 --- a/libs/designer/src/lib/core/state/templates/manifestSlice.ts +++ b/libs/designer/src/lib/core/state/templates/manifestSlice.ts @@ -12,7 +12,7 @@ export interface ManifestState { availableTemplateNames?: ManifestName[]; filteredTemplateNames?: ManifestName[]; githubTemplateNames?: ManifestName[]; - availableTemplates?: Record; + availableTemplates?: Record; filters: { pageNum: number; keyword?: string; @@ -65,7 +65,7 @@ export const manifestSlice = createSlice({ setavailableTemplatesNames: (state, action: PayloadAction) => { state.availableTemplateNames = action.payload; }, - setavailableTemplates: (state, action: PayloadAction | undefined>) => { + setavailableTemplates: (state, action: PayloadAction | undefined>) => { state.availableTemplates = action.payload; }, setFilteredTemplateNames: (state, action: PayloadAction) => { diff --git a/libs/designer/src/lib/core/state/templates/templateselectors.ts b/libs/designer/src/lib/core/state/templates/templateselectors.ts index 6857e57ba9e..99e61f9ba7e 100644 --- a/libs/designer/src/lib/core/state/templates/templateselectors.ts +++ b/libs/designer/src/lib/core/state/templates/templateselectors.ts @@ -2,6 +2,7 @@ import { useSelector } from 'react-redux'; import type { RootState } from './store'; import type { WorkflowTemplateData } from '../../actions/bjsworkflow/templates'; import type { ConnectionReference } from '../../../common/models/workflow'; +import type { Template } from '@microsoft/logic-apps-shared'; export const useAreServicesInitialized = () => { return useSelector((state: RootState) => state.templateOptions.servicesInitialized ?? false); @@ -17,6 +18,12 @@ export const useWorkflowTemplate = (workflowId: string): WorkflowTemplateData => }); }; +export const useTemplateManifest = (): Template.TemplateManifest | undefined => { + return useSelector((state: RootState) => { + return state.template.manifest; + }); +}; + export const useWorkflowBasicsEditable = (workflowId: string) => { return useSelector((state: RootState) => { return { @@ -26,6 +33,7 @@ export const useWorkflowBasicsEditable = (workflowId: string) => { }); }; +//TODO: remove this export const useDefaultWorkflowTemplate = (): WorkflowTemplateData => { return useSelector((state: RootState) => { const workflows = state.template.workflows ?? {}; diff --git a/libs/designer/src/lib/core/templates/utils/helper.ts b/libs/designer/src/lib/core/templates/utils/helper.ts index 880f79ee0df..2ab109f6a95 100644 --- a/libs/designer/src/lib/core/templates/utils/helper.ts +++ b/libs/designer/src/lib/core/templates/utils/helper.ts @@ -49,27 +49,26 @@ export const getQuickViewTabs = ( }; export const getUniqueConnectors = ( - connections: Record, + allFeaturedConectors: Template.FeaturedConnector[], subscriptionId: string, location: string -): Template.Connection[] => { - const allConnections = Object.values(connections); - return getUniqueConnectorsFromConnections(allConnections, subscriptionId, location); +): Template.FeaturedConnector[] => { + return getUniqueConnectorsFromConnections(allFeaturedConectors, subscriptionId, location); }; export const getUniqueConnectorsFromConnections = ( - allConnections: Template.Connection[], + allFeaturedConectors: Template.FeaturedConnector[], subscriptionId: string, location: string -): Template.Connection[] => { - const result: Template.Connection[] = []; +): Template.FeaturedConnector[] => { + const result: Template.FeaturedConnector[] = []; const finalConnectorIds: string[] = []; - while (allConnections.length > 0) { - const connection = allConnections.shift() as Template.Connection; - const normalizedConnectorId = normalizeConnectorId(connection.connectorId, subscriptionId, location).toLowerCase(); + while (allFeaturedConectors.length > 0) { + const connector = allFeaturedConectors.shift() as Template.FeaturedConnector; + const normalizedConnectorId = normalizeConnectorId(connector.id, subscriptionId, location).toLowerCase(); if (!finalConnectorIds.includes(normalizedConnectorId)) { finalConnectorIds.push(normalizedConnectorId); - result.push({ ...connection, connectorId: normalizedConnectorId }); + result.push({ ...connector, id: normalizedConnectorId }); } } @@ -82,53 +81,56 @@ const templateSearchOptions = { threshold: 0, ignoreLocation: true, keys: [ + { + name: 'id', + weight: 1, + getFn: ([_name, template]: [string, Template.TemplateManifest]) => template.id, + }, { name: 'title', weight: 1, - getFn: ([_name, template]: [string, Template.Manifest]) => template.title, + getFn: ([_name, template]: [string, Template.TemplateManifest]) => template.title, }, { - name: 'description', + name: 'tags', weight: 1, - getFn: ([_name, template]: [string, Template.Manifest]) => template.description, + getFn: ([_name, template]: [string, Template.TemplateManifest]) => template.tags ?? [], + }, + { + name: 'summary', + weight: 1, + getFn: ([_name, template]: [string, Template.TemplateManifest]) => template.summary, + }, + { + name: 'workflowName', + weight: 2, + getFn: ([_name, template]: [string, Template.TemplateManifest]) => Object.values(template.workflows).map((w) => w.name), }, { name: 'manifest', weight: 2, - getFn: ([_name, template]: [string, Template.Manifest]) => [ + getFn: ([_name, template]: [string, Template.TemplateManifest]) => [ ...template.skus, ...(template.tags ?? []), - ...(template.kinds ?? []), ...Object.values(template.details), ], }, { - name: 'parameters', - weight: 3, - getFn: ([_name, template]: [string, Template.Manifest]) => - template.parameters?.reduce((acc: string[], parameter) => acc.concat([parameter.displayName, parameter.description]), []), - }, - { - name: 'connections', + name: 'featuredConnectors', weight: 3, - getFn: ([_name, template]: [string, Template.Manifest]) => - template.connections - ? Object.values(template.connections)?.reduce( - (acc: string[], connection) => acc.concat([connection.connectorId.split('/').slice(-1)[0]]), - [] - ) - : [], + getFn: ([_name, template]: [string, Template.TemplateManifest]) => + (template.featuredConnectors ?? [])?.map((connector) => connector.id), }, ], }; export const getFilteredTemplates = ( - templates: Record, + templates: Record, filters: { keyword?: string; sortKey: string; connectors?: FilterObject[]; - detailFilters: Record; + detailFilters: Record; }, isConsumption: boolean ): string[] => { @@ -138,10 +140,10 @@ export const getFilteredTemplates = ( } const hasConnectors = - filters?.connectors?.some((connector) => - Object.values(templateManifest.connections)?.some( - (connection) => - connector.value.split('/').slice(-1)[0].toLowerCase() === connection.connectorId.split('/').slice(-1)[0].toLowerCase() + filters?.connectors?.some((filterConnector) => + templateManifest?.featuredConnectors?.some( + (featuredConnector) => + filterConnector.value.split('/').slice(-1)[0].toLowerCase() === featuredConnector.id.split('/').slice(-1)[0].toLowerCase() ) ) ?? true; @@ -150,7 +152,7 @@ export const getFilteredTemplates = ( } const hasDetailFilters = Object.entries(filters.detailFilters).every(([filterName, filterItems]) => { - const templateManifestDetailValue = templateManifest.details?.[filterName]?.split(','); + const templateManifestDetailValue = templateManifest.details?.[filterName as Template.DetailsType]?.split(','); if (!templateManifestDetailValue) { return false; } @@ -170,7 +172,10 @@ export const getFilteredTemplates = ( return Object.keys(Object.fromEntries(sortedFilteredTemplateEntries)); }; -const _sortTemplateManifestEntriesByTitle = (sortKey: string | undefined, templateManifestEntries: [string, Template.Manifest][]) => { +const _sortTemplateManifestEntriesByTitle = ( + sortKey: string | undefined, + templateManifestEntries: [string, Template.TemplateManifest][] +) => { switch (sortKey) { case 'a-to-z': return templateManifestEntries.sort(([_m1, manifest1], [_m2, manifest2]) => diff --git a/libs/designer/src/lib/ui/panel/templatePanel/createWorkflowPanel/createWorkflowPanel.tsx b/libs/designer/src/lib/ui/panel/templatePanel/createWorkflowPanel/createWorkflowPanel.tsx index 809aa14cce5..946259943a9 100644 --- a/libs/designer/src/lib/ui/panel/templatePanel/createWorkflowPanel/createWorkflowPanel.tsx +++ b/libs/designer/src/lib/ui/panel/templatePanel/createWorkflowPanel/createWorkflowPanel.tsx @@ -102,17 +102,10 @@ export const CreateWorkflowPanel = ({ ), - [ - isMultiWorkflow, - resources.multiWorkflowCreateTitle, - resources.updatedWorkflowTitle, - isCreateView, - manifest?.title, - manifest?.description, - ] + [isMultiWorkflow, resources.multiWorkflowCreateTitle, resources.updatedWorkflowTitle, isCreateView, manifest?.title, manifest?.summary] ); const selectedTabProps = selectedTabId ? panelTabs?.find((tab) => tab.id === selectedTabId) : panelTabs[0]; @@ -140,11 +133,7 @@ export const CreateWorkflowPanel = ({ ); }; -export const CreateWorkflowPanelHeader = ({ - headerTitle, - title, - description, -}: { title: string; description: string; headerTitle?: string }) => { +export const CreateWorkflowPanelHeader = ({ headerTitle, title, summary }: { title: string; summary: string; headerTitle?: string }) => { const [isOpen, setIsOpen] = useState(false); const intl = useIntl(); @@ -191,7 +180,7 @@ export const CreateWorkflowPanelHeader = ({
- {description} + {summary}
diff --git a/libs/designer/src/lib/ui/panel/templatePanel/quickViewPanel/quickViewPanel.tsx b/libs/designer/src/lib/ui/panel/templatePanel/quickViewPanel/quickViewPanel.tsx index b552bb57e05..e25285a9cb6 100644 --- a/libs/designer/src/lib/ui/panel/templatePanel/quickViewPanel/quickViewPanel.tsx +++ b/libs/designer/src/lib/ui/panel/templatePanel/quickViewPanel/quickViewPanel.tsx @@ -36,13 +36,16 @@ export const QuickViewPanel = ({ }: QuickViewPanelProps) => { const dispatch = useDispatch(); const intl = useIntl(); - const { templateName, workflowAppName, isOpen, currentPanelView, shouldCloseByDefault } = useSelector((state: RootState) => ({ - templateName: state.template.templateName, - workflowAppName: state.workflow.workflowAppName, - isOpen: state.panel.isOpen, - currentPanelView: state.panel.currentPanelView, - shouldCloseByDefault: !state.templateOptions.viewTemplateDetails, - })); + const { templateName, templateManifest, workflowAppName, isOpen, currentPanelView, shouldCloseByDefault } = useSelector( + (state: RootState) => ({ + templateName: state.template.templateName, + templateManifest: state.template.manifest, + workflowAppName: state.workflow.workflowAppName, + isOpen: state.panel.isOpen, + currentPanelView: state.panel.currentPanelView, + shouldCloseByDefault: !state.templateOptions.viewTemplateDetails, + }) + ); const { manifest } = useWorkflowTemplate(workflowId); const panelTabs = getQuickViewTabs( intl, @@ -72,12 +75,12 @@ export const QuickViewPanel = ({ () => ( ), - [manifest] + [templateManifest, manifest] ); const selectedTabProps = selectedTabId ? panelTabs?.find((tab) => tab.id === selectedTabId) : panelTabs[0]; @@ -127,14 +130,14 @@ export const QuickViewPanel = ({ export const QuickViewPanelHeader = ({ title, - description, + summary, sourceCodeUrl, details, features, onBackClick, }: { title: string; - description: string; + summary: string; sourceCodeUrl: string | undefined; details: Record; features?: string; @@ -181,7 +184,7 @@ export const QuickViewPanelHeader = ({ )} - {description} + {summary} {features && (
diff --git a/libs/designer/src/lib/ui/panel/templatePanel/quickViewPanel/tabs/summaryTab.tsx b/libs/designer/src/lib/ui/panel/templatePanel/quickViewPanel/tabs/summaryTab.tsx index 8ff5baad4b3..1f69ca15804 100644 --- a/libs/designer/src/lib/ui/panel/templatePanel/quickViewPanel/tabs/summaryTab.tsx +++ b/libs/designer/src/lib/ui/panel/templatePanel/quickViewPanel/tabs/summaryTab.tsx @@ -7,15 +7,16 @@ import { Link, Text } from '@fluentui/react-components'; import type { TemplatePanelTab } from '@microsoft/designer-ui'; import { clearTemplateDetails } from '../../../../../core/state/templates/templateSlice'; import Markdown from 'react-markdown'; -import { useWorkflowTemplate } from '../../../../../core/state/templates/templateselectors'; +import { useTemplateManifest, useWorkflowTemplate } from '../../../../../core/state/templates/templateselectors'; import { ConnectionsList } from '../../../../templates/connections/connections'; import { Open16Regular } from '@fluentui/react-icons'; export const SummaryPanel = ({ workflowId }: { workflowId: string }) => { const intl = useIntl(); - const { manifest } = useWorkflowTemplate(workflowId); - const templateHasConnections = Object.keys(manifest?.connections || {}).length > 0; - const detailsTags: Record = { + const { manifest: workflowManifest } = useWorkflowTemplate(workflowId); + const templateManifest = useTemplateManifest(); + const templateHasConnections = Object.keys(workflowManifest?.connections || {}).length > 0; + const detailsTags: Partial> = { Type: intl.formatMessage({ defaultMessage: 'Solution type', id: 'JVNRly', @@ -33,7 +34,7 @@ export const SummaryPanel = ({ workflowId }: { workflowId: string }) => { }), }; - return isNullOrUndefined(manifest) ? null : ( + return isNullOrUndefined(workflowManifest) || isNullOrUndefined(templateManifest) ? null : (
@@ -49,9 +50,9 @@ export const SummaryPanel = ({ workflowId }: { workflowId: string }) => { description: 'Text to show no connections present in the template.', })} - {templateHasConnections ? : null} + {templateHasConnections ? : null}
- {manifest.prerequisites ? ( + {workflowManifest.prerequisites ? (
{intl.formatMessage({ @@ -61,7 +62,7 @@ export const SummaryPanel = ({ workflowId }: { workflowId: string }) => { })} - {manifest.prerequisites} + {workflowManifest.prerequisites}
) : null} @@ -73,12 +74,12 @@ export const SummaryPanel = ({ workflowId }: { workflowId: string }) => { description: 'Title for the details section in the template overview tab', })} - {manifest?.detailsDescription && ( + {workflowManifest?.description && ( - {manifest?.detailsDescription} + {workflowManifest?.description} )} - {manifest?.sourceCodeUrl && ( + {workflowManifest?.sourceCodeUrl && (
{intl.formatMessage({ @@ -88,22 +89,22 @@ export const SummaryPanel = ({ workflowId }: { workflowId: string }) => { })} : - - {manifest?.sourceCodeUrl} + + {workflowManifest?.sourceCodeUrl}
)} - {Object.keys(detailsTags).map((key: string) => { + {Object.keys(detailsTags).map((key) => { return (
- {detailsTags[key]}: - {manifest.details[key]} + {detailsTags[key as Template.DetailsType]}: + {templateManifest.details[key as Template.DetailsType]}
); })}
- {manifest.tags?.length ? ( + {templateManifest.tags?.length ? (
{intl.formatMessage({ @@ -113,7 +114,7 @@ export const SummaryPanel = ({ workflowId }: { workflowId: string }) => { })}
- {manifest.tags.map((key: string) => ( + {templateManifest.tags.map((key: string) => ( {key} diff --git a/libs/designer/src/lib/ui/templates/basics/multiworkflow.tsx b/libs/designer/src/lib/ui/templates/basics/multiworkflow.tsx index d2ac7e5b211..cd43eb384a3 100644 --- a/libs/designer/src/lib/ui/templates/basics/multiworkflow.tsx +++ b/libs/designer/src/lib/ui/templates/basics/multiworkflow.tsx @@ -22,7 +22,7 @@ interface WorkflowItem { key: string; text: string; }[]; - description: string; + summary: string; errors: { workflow: string | undefined; kind: string | undefined; @@ -91,7 +91,7 @@ export const MultiWorkflowBasics = () => { text: equals(kind, WorkflowKind.STATEFUL) ? resources.kind_stateful : resources.kind_stateless, })) : defaultKindOptions, - description: workflow.manifest.description, + summary: workflow.manifest.summary, errors: workflow.errors, })) ); @@ -224,7 +224,7 @@ export const MultiWorkflowBasics = () => { ); case '$description': - return ; + return ; default: return null; diff --git a/libs/designer/src/lib/ui/templates/cards/templateCard.tsx b/libs/designer/src/lib/ui/templates/cards/templateCard.tsx index e699e518783..38de746d82c 100644 --- a/libs/designer/src/lib/ui/templates/cards/templateCard.tsx +++ b/libs/designer/src/lib/ui/templates/cards/templateCard.tsx @@ -6,10 +6,8 @@ import { openQuickViewPanelView } from '../../../core/state/templates/panelSlice import type { IContextualMenuItem, IContextualMenuProps, IDocumentCardStyles } from '@fluentui/react'; import { DocumentCard, IconButton, Image, Shimmer, ShimmerElementType } from '@fluentui/react'; import { ConnectorIcon, ConnectorIconWithName } from '../connections/connector'; -import type { Manifest } from '@microsoft/logic-apps-shared/src/utils/src/lib/models/template'; -import { getUniqueConnectors } from '../../../core/templates/utils/helper'; import { useIntl } from 'react-intl'; -import type { OperationInfo } from '@microsoft/logic-apps-shared'; +import type { OperationInfo, Template } from '@microsoft/logic-apps-shared'; import { equals, getBuiltInOperationInfo, @@ -37,7 +35,7 @@ const cardStyles: IDocumentCardStyles = { export const TemplateCard = ({ templateName }: TemplateCardProps) => { const dispatch = useDispatch(); const intl = useIntl(); - const { templateManifest, workflowAppName, subscriptionId, location } = useSelector((state: RootState) => ({ + const { templateManifest, workflowAppName } = useSelector((state: RootState) => ({ templateManifest: state.manifest.availableTemplates?.[templateName], subscriptionId: state.workflow.subscriptionId, workflowAppName: state.workflow.workflowAppName, @@ -83,16 +81,10 @@ export const TemplateCard = ({ templateName }: TemplateCardProps) => { return ; } - const { title, details, featuredOperations, connections } = templateManifest as Manifest; - const connectorsFromConnections = getUniqueConnectors(connections, subscriptionId, location).map((connection) => ({ - connectorId: connection.connectorId, - operationId: undefined, - })) as { connectorId: string; operationId: string | undefined }[]; - const connectorsFeatured = getFeaturedConnectors(featuredOperations); - const allConnectors = connectorsFromConnections.concat(connectorsFeatured); - const showOverflow = allConnectors.length > maxConnectorsToShow; - const connectorsToShow = showOverflow ? allConnectors.slice(0, maxConnectorsToShow) : allConnectors; - const overflowList = showOverflow ? allConnectors.slice(maxConnectorsToShow) : []; + const { title, details, featuredConnectors = [] } = templateManifest as Template.TemplateManifest; + const showOverflow = featuredConnectors.length > maxConnectorsToShow; + const connectorsToShow = showOverflow ? featuredConnectors.slice(0, maxConnectorsToShow) : featuredConnectors; + const overflowList = showOverflow ? featuredConnectors.slice(maxConnectorsToShow) : []; const onRenderMenuItem = (item: IContextualMenuItem) => ( { ); const onRenderMenuIcon = () =>
{`+${overflowList.length}`}
; const menuProps: IContextualMenuProps = { - items: overflowList.map((info) => ({ key: info.connectorId, text: info.connectorId, data: info, onRender: onRenderMenuItem })), + items: overflowList.map((info) => ({ key: info.id, text: info.id, data: info, onRender: onRenderMenuItem })), directionalHintFixed: true, className: 'msla-template-card-connector-menu-box', }; @@ -137,24 +129,20 @@ export const TemplateCard = ({ templateName }: TemplateCardProps) => {
- {['Type', 'Trigger'].map((key: string) => { - if (!details[key]) { - return null; - } - return ( - - {details[key]} - - ); - })} + + {details.Type} + + + {details.Trigger} +
{connectorsToShow.length > 0 ? ( connectorsToShow.map((info) => ( )) diff --git a/libs/designer/src/lib/ui/templates/connections/connector.tsx b/libs/designer/src/lib/ui/templates/connections/connector.tsx index c7a04c95b0f..88ff84b2c05 100644 --- a/libs/designer/src/lib/ui/templates/connections/connector.tsx +++ b/libs/designer/src/lib/ui/templates/connections/connector.tsx @@ -15,15 +15,15 @@ import { isConnectionValid } from '../../../core/utils/connectors/connections'; export const ConnectorIcon = ({ connectorId, - operationId, + // operationId, classes, }: { connectorId: string; classes: Record; - operationId?: string; + // operationId?: string; styles?: IStyleFunctionOrObject; }) => { - const { data: connector, isLoading, isError } = useConnectorInfo(connectorId, operationId, /* useCachedData */ true); + const { data: connector, isLoading, isError } = useConnectorInfo(connectorId, undefined, /* useCachedData */ true); if (!connector) { return isLoading ? : isError ? : ; } diff --git a/libs/designer/src/lib/ui/templates/filters/templateFilters.tsx b/libs/designer/src/lib/ui/templates/filters/templateFilters.tsx index a506032e2ec..60e51c97ff9 100644 --- a/libs/designer/src/lib/ui/templates/filters/templateFilters.tsx +++ b/libs/designer/src/lib/ui/templates/filters/templateFilters.tsx @@ -9,14 +9,14 @@ import { getUniqueConnectorsFromConnections } from '../../../core/templates/util import { useConnector } from '../../../core/state/connection/connectionSelector'; import { Tab, TabList } from '@fluentui/react-components'; import type { SelectTabData, SelectTabEvent } from '@fluentui/react-components'; +import type { Template } from '@microsoft/logic-apps-shared'; -export type TemplateDetailFilterType = Record< - string, - { - displayName: string; - items: FilterObject[]; - } ->; +type TemplateDetailFilterValue = { + displayName: string; + items: FilterObject[]; +}; + +export type TemplateDetailFilterType = Partial>; export interface TemplateFiltersProps { detailFilters: TemplateDetailFilterType; @@ -38,9 +38,10 @@ export const TemplateFilters = ({ detailFilters }: TemplateFiltersProps) => { const skuTemplates = Object.values(availableTemplates).filter((templateManifest) => templateManifest.skus.includes(isConsumption ? 'consumption' : 'standard') ); - const allConnections = Object.values(skuTemplates).flatMap((template) => Object.values(template.connections)); - const uniqueConnectorsFromConnections = getUniqueConnectorsFromConnections(allConnections, subscriptionId, location); - return uniqueConnectorsFromConnections.map((connector) => connector.connectorId); + + const allConnectors = Object.values(skuTemplates).flatMap((template) => template.featuredConnectors ?? []); + const uniqueConnectorsFromConnections = getUniqueConnectorsFromConnections(allConnectors, subscriptionId, location); + return uniqueConnectorsFromConnections.map((connector) => connector.id); }, [availableTemplates, isConsumption, location, subscriptionId]); const selectedTabId = appliedDetailFilters?.Type?.[0]?.value ?? templateDefaultTabKey; @@ -165,16 +166,18 @@ export const TemplateFilters = ({ detailFilters }: TemplateFiltersProps) => { isSearchable /> )} - {Object.keys(detailFilters).map((filterName, index) => ( - { - dispatch(setDetailsFilters({ filterName, filters: filterItems })); - }} - /> - ))} + {(Object.keys(detailFilters) as Template.DetailsType[]).map((filterName, index) => + detailFilters[filterName] ? ( + { + dispatch(setDetailsFilters({ filterName, filters: filterItems })); + }} + /> + ) : null + )}
diff --git a/libs/designer/src/lib/ui/templates/templateoverview.tsx b/libs/designer/src/lib/ui/templates/templateoverview.tsx index 00103d024df..baf28a1a16a 100644 --- a/libs/designer/src/lib/ui/templates/templateoverview.tsx +++ b/libs/designer/src/lib/ui/templates/templateoverview.tsx @@ -40,7 +40,7 @@ export const TemplateOverview = ({ connections: state.template.connections, workflows: state.template.workflows, })); - const { title, description, sourceCodeUrl, details, detailsDescription } = manifest as Template.Manifest; + const { title, summary, sourceCodeUrl, details, description } = manifest as Template.TemplateManifest; const resources = { by: intl.formatMessage({ defaultMessage: 'By', @@ -86,10 +86,10 @@ export const TemplateOverview = ({ <>
@@ -157,7 +157,6 @@ export const TemplateOverview = ({ interface WorkflowItem { id: string; name: string; - trigger: string; } const WorkflowList = ({ @@ -165,17 +164,16 @@ const WorkflowList = ({ showDetails, }: { workflows: Record; showDetails: (workflowId: string) => void }) => { const intl = useIntl(); + const [items, setItems] = useFunctionalState( unmap(workflows).map((workflow) => { const { id, manifest } = workflow; - const { title, details } = manifest as Template.Manifest; - const trigger = getPropertyValue(details, 'trigger'); - return { id, name: title, trigger }; + const { title } = manifest as Template.WorkflowManifest; + return { id, name: title }; }) ); const columnsNames = { name: intl.formatMessage({ defaultMessage: 'Name', id: '+EREVh', description: 'Column name for workflow name' }), - trigger: intl.formatMessage({ defaultMessage: 'Trigger', id: 'MGq28G', description: 'Column name for trigger type' }), }; const _onColumnClick = (_event: React.MouseEvent, column: IColumn): void => { let isSortedDescending = column.isSortedDescending; @@ -212,18 +210,6 @@ const WorkflowList = ({ showSortIconWhenUnsorted: true, onColumnClick: _onColumnClick, }, - { - ariaLabel: columnsNames.trigger, - fieldName: 'trigger', - flexGrow: 1, - key: 'trigger', - isResizable: true, - minWidth: 1, - maxWidth: 100, - name: columnsNames.trigger, - showSortIconWhenUnsorted: true, - onColumnClick: _onColumnClick, - }, ]); const onRenderItemColumn = (item: WorkflowItem, _index: number | undefined, column: IColumn | undefined) => { @@ -235,13 +221,6 @@ const WorkflowList = ({ ); - case 'trigger': - return ( - - {item.trigger} - - ); - default: return null; } diff --git a/libs/logic-apps-shared/src/designer-client-services/lib/base/template.ts b/libs/logic-apps-shared/src/designer-client-services/lib/base/template.ts index d7ab331268f..7671f9a73b5 100644 --- a/libs/logic-apps-shared/src/designer-client-services/lib/base/template.ts +++ b/libs/logic-apps-shared/src/designer-client-services/lib/base/template.ts @@ -22,9 +22,9 @@ export class BaseTemplateService implements ITemplateService { public onAddBlankWorkflow = (): Promise => this.options.onAddBlankWorkflow(); - public getContentPathUrl = (templateName: string, resourcePath: string): string => { + public getContentPathUrl = (templatePath: string, resourcePath: string): string => { const { endpoint, useEndpointForTemplates } = this.options; - return useEndpointForTemplates ? `${endpoint}/templates/${templateName}/${resourcePath}` : resourcePath; + return useEndpointForTemplates ? `${endpoint}/templates/${templatePath}/${resourcePath}` : resourcePath; }; public getAllTemplateNames = async (): Promise => { @@ -34,35 +34,39 @@ export class BaseTemplateService implements ITemplateService { : ((await import('./../templates/manifest.json'))?.default as string[]); }; - public getResourceManifest = async (resourcePath: string): Promise => { + public getTemplateManifest = async (templateId: string): Promise => { const { httpClient, endpoint, useEndpointForTemplates } = this.options; if (useEndpointForTemplates) { return httpClient.get({ - uri: `${endpoint}/templates/${resourcePath}/manifest.json`, + uri: `${endpoint}/templates/${templateId}/manifest.json`, headers: { 'Access-Control-Allow-Origin': '*' }, }); } - const paths = resourcePath.split('/'); - - return paths.length === 2 - ? (await import(`./../templates/${paths[0]}/${paths[1]}/manifest.json`)).default - : (await import(`./../templates/${resourcePath}/manifest.json`)).default; + return (await import(`./../templates/${templateId}/manifest.json`)).default; }; - public getWorkflowDefinition = async (resourcePath: string): Promise => { + public getWorkflowManifest = async (templateId: string, workflowId: string): Promise => { const { httpClient, endpoint, useEndpointForTemplates } = this.options; if (useEndpointForTemplates) { return httpClient.get({ - uri: `${endpoint}/templates/${resourcePath}/workflow.json`, + uri: `${endpoint}/templates/${templateId}/${workflowId}/manifest.json`, headers: { 'Access-Control-Allow-Origin': '*' }, }); } - const paths = resourcePath.split('/'); + return (await import(`./../templates/${templateId}/${workflowId}/manifest.json`)).default; + }; + + public getWorkflowDefinition = async (templateId: string, workflowId: string): Promise => { + const { httpClient, endpoint, useEndpointForTemplates } = this.options; + if (useEndpointForTemplates) { + return httpClient.get({ + uri: `${endpoint}/templates/${templateId}/${workflowId}/workflow.json`, + headers: { 'Access-Control-Allow-Origin': '*' }, + }); + } - return paths.length === 2 - ? (await import(`./../templates/${paths[0]}/${paths[1]}/workflow.json`)).default - : (await import(`./../templates/${resourcePath}/workflow.json`)).default; + return (await import(`./../templates/${templateId}/${workflowId}/workflow.json`)).default; }; } diff --git a/libs/logic-apps-shared/src/designer-client-services/lib/template.ts b/libs/logic-apps-shared/src/designer-client-services/lib/template.ts index b5a88cace29..d2e63d3980c 100644 --- a/libs/logic-apps-shared/src/designer-client-services/lib/template.ts +++ b/libs/logic-apps-shared/src/designer-client-services/lib/template.ts @@ -6,9 +6,10 @@ export interface ITemplateService { openBladeAfterCreate: (workflowName: string | undefined) => void; onAddBlankWorkflow: () => Promise; getAllTemplateNames: () => Promise; - getResourceManifest: (resourcePath: string) => Promise; - getWorkflowDefinition: (resourcePath: string) => Promise; - getContentPathUrl: (templateName: string, resourcePath: string) => string; + getTemplateManifest: (templateId: string) => Promise; + getWorkflowManifest: (templateId: string, workflowId: string) => Promise; + getWorkflowDefinition: (templateId: string, workflowId: string) => Promise; + getContentPathUrl: (templatePath: string, resourcePath: string) => string; } let service: ITemplateService; diff --git a/libs/logic-apps-shared/src/utils/src/lib/models/template.ts b/libs/logic-apps-shared/src/utils/src/lib/models/template.ts index 67bda444169..ac9edbd1b7b 100644 --- a/libs/logic-apps-shared/src/utils/src/lib/models/template.ts +++ b/libs/logic-apps-shared/src/utils/src/lib/models/template.ts @@ -4,13 +4,13 @@ export type ConnectorRuntimeType = 'inapp' | 'shared'; export interface Manifest { title: string; - description: string; + summary: string; skus: SkuType[]; kinds?: WorkflowKindType[]; details: Record; /* This is a markdown to show features for multi-workflow and details in case of single workflow */ - detailsDescription?: string; + description?: string; tags?: string[]; artifacts: Artifact[]; @@ -27,24 +27,27 @@ export interface Manifest { sourceCodeUrl?: string; // Automatically generated for public templates, otherwise optional } +export type DetailsType = 'By' | 'Type' | 'Category' | 'Trigger'; + export interface TemplateManifest { id: string; title: string; - description: string; + summary: string; /* This is a markdown to show features for multi-workflow */ - detailsDescription?: string; + description?: string; artifacts?: Artifact[]; skus: SkuType[]; /* This consists of list of workflows listed in the multi-workflow template. The key is the folder name, followed by metadata where name is default name to be used for creation */ - workflows?: Record; - featuredConnectors: { id: string; kind: string }[]; + workflows: Record; + featuredConnectors?: FeaturedConnector[]; + // details: Record; details: { By: string; Type: string; Category: string; - Trigger?: string; // undefined for accelerator + Trigger?: string; }; tags?: string[]; sourceCodeUrl?: string; // Automatically generated for public templates, otherwise not present @@ -53,8 +56,8 @@ export interface TemplateManifest { export interface WorkflowManifest { id: string; title: string; - description: string; - detailsDescription?: string; + summary: string; + description?: string; prerequisites?: string; kinds?: WorkflowKindType[]; artifacts?: Artifact[]; @@ -103,6 +106,11 @@ export interface Connection { kind?: ConnectorRuntimeType; } +export interface FeaturedConnector { + id: string; + kind?: ConnectorRuntimeType; +} + export interface TemplateContext { templateId: string; workflowAppName?: string; From de8d2044205619b82926c041b3664d4290287977 Mon Sep 17 00:00:00 2001 From: Elaina Lee Date: Tue, 4 Mar 2025 17:25:48 -0800 Subject: [PATCH 03/24] updated the script --- downloadTemplates.js | 52 ++++++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/downloadTemplates.js b/downloadTemplates.js index ba4aa219248..4f6bf069b94 100644 --- a/downloadTemplates.js +++ b/downloadTemplates.js @@ -3,7 +3,7 @@ import { existsSync, writeFile, createWriteStream } from 'fs'; import { mkdir, rm } from 'fs/promises'; import client from 'https'; -const releaseBranch = 'release/20250224'; +const releaseBranch = 'elaina/addSummaryField'; const baseURL = `https://raw.githubusercontent.com/azure/LogicAppsTemplates/${releaseBranch}`; const sourceCodeURL = `https://github.com/Azure/LogicAppsTemplates/tree/${releaseBranch}`; @@ -31,30 +31,40 @@ const createTemplatesFolder = async (path) => { await mkdir(`${templatesFolder}/${path}`, { recursive: true }); }; -const downloadTemplate = async (path) => { - const templateManifest = await (await fetch(`${baseURL}/${path}/manifest.json`)).json(); - for (const artifact of templateManifest.artifacts) { +const downloadTemplate = async (templateId) => { + const templateManifest = await (await fetch(`${baseURL}/${templateId}/manifest.json`)).json(); + for (const artifact of templateManifest?.artifacts ?? []) { + await downloadFile(`${templateId}/${artifact.file}`); + } + + templateManifest.sourceCodeUrl = `${sourceCodeURL}/${templateId}/manifest.json`; + writeFile(`${templatesFolder}/${templateId}/manifest.json`, JSON.stringify(templateManifest, null, 2), () => {}); + + for (const workflowId of Object.keys(templateManifest.workflows)) { + createTemplatesFolder(`${templateId}/${workflowId}`); + await downloadWorkflowManifest(`${templateId}/${workflowId}`); + } +}; + +const downloadWorkflowManifest = async (path) => { + const workflowManifest = await (await fetch(`${baseURL}/${path}/manifest.json`)).json(); + for (const artifact of workflowManifest.artifacts) { await downloadFile(`${path}/${artifact.file}`); } - if (templateManifest.images.light && templateManifest.images.dark) { - await downloadFile(`${path}/${templateManifest.images.light}.png`); - await downloadFile(`${path}/${templateManifest.images.dark}.png`); - templateManifest.images = { - light: `${baseURL}/${path}/${templateManifest.images.light}.png`, - dark: `${baseURL}/${path}/${templateManifest.images.dark}.png`, + if (workflowManifest.images.light && workflowManifest.images.dark) { + await downloadFile(`${path}/${workflowManifest.images.light}.png`); + await downloadFile(`${path}/${workflowManifest.images.dark}.png`); + workflowManifest.images = { + light: `${baseURL}/${path}/${workflowManifest.images.light}.png`, + dark: `${baseURL}/${path}/${workflowManifest.images.dark}.png`, }; } else { - templateManifest.images = undefined; + workflowManifest.images = undefined; } - templateManifest.sourceCodeUrl = `${sourceCodeURL}/${path}/manifest.json`; - writeFile(`${templatesFolder}/${path}/manifest.json`, JSON.stringify(templateManifest, null, 2), () => {}); - - for (const workflowId of Object.keys(templateManifest.workflows ?? {})) { - createTemplatesFolder(`${path}/${workflowId}`); - await downloadTemplate(`${path}/${workflowId}`); - } + workflowManifest.sourceCodeUrl = `${sourceCodeURL}/${path}/manifest.json`; + writeFile(`${templatesFolder}/${path}/manifest.json`, JSON.stringify(workflowManifest, null, 2), () => {}); }; const run = async () => { @@ -62,9 +72,9 @@ const run = async () => { await createTemplatesFolder(''); const registeredManifestNames = await (await downloadFetchFile('manifest.json')).json(); - for (const manifestName of registeredManifestNames) { - await createTemplatesFolder(manifestName); - await downloadTemplate(manifestName); + for (const templateId of registeredManifestNames) { + await createTemplatesFolder(templateId); + await downloadTemplate(templateId); } }; From 77d7006f63aa2f90e4b7b776a8bb5c5d4e1d29d6 Mon Sep 17 00:00:00 2001 From: Elaina Lee Date: Tue, 4 Mar 2025 17:36:18 -0800 Subject: [PATCH 04/24] fixed logic for checking multi workflow --- libs/designer/src/lib/ui/templates/cards/templateCard.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/designer/src/lib/ui/templates/cards/templateCard.tsx b/libs/designer/src/lib/ui/templates/cards/templateCard.tsx index 38de746d82c..ea411218b41 100644 --- a/libs/designer/src/lib/ui/templates/cards/templateCard.tsx +++ b/libs/designer/src/lib/ui/templates/cards/templateCard.tsx @@ -72,7 +72,7 @@ export const TemplateCard = ({ templateName }: TemplateCardProps) => { dispatch(changeCurrentTemplateName(templateName)); dispatch(loadTemplate({ preLoadedManifest: templateManifest })); - if (Object.keys(templateManifest?.workflows ?? {}).length === 0) { + if (!isMultiWorkflow) { dispatch(openQuickViewPanelView()); } }; From be3a107e4ace3b86268f11633e38e78c8e0800e9 Mon Sep 17 00:00:00 2001 From: Elaina Lee Date: Tue, 4 Mar 2025 17:42:21 -0800 Subject: [PATCH 05/24] improvements --- .../src/lib/core/actions/bjsworkflow/templates.ts | 10 +++++++++- .../src/lib/ui/templates/cards/templateCard.tsx | 8 +++++--- libs/designer/src/lib/ui/templates/templateslist.tsx | 4 ++-- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/libs/designer/src/lib/core/actions/bjsworkflow/templates.ts b/libs/designer/src/lib/core/actions/bjsworkflow/templates.ts index 682856e4e58..28b287d3ad4 100644 --- a/libs/designer/src/lib/core/actions/bjsworkflow/templates.ts +++ b/libs/designer/src/lib/core/actions/bjsworkflow/templates.ts @@ -284,7 +284,15 @@ const loadTemplateFromResourcePath = async ( if (workflowData) { data.workflows = { - [workflowId]: workflowData.workflow, + [workflowId]: { + ...workflowData.workflow, + manifest: { + ...workflowData.workflow.manifest, + // Override title and summary with template manifest data if single workflow + title: templateManifest.title, + summary: templateManifest.summary, + }, + }, }; data.parameterDefinitions = workflowData.parameterDefinitions; data.connections = workflowData.connections; diff --git a/libs/designer/src/lib/ui/templates/cards/templateCard.tsx b/libs/designer/src/lib/ui/templates/cards/templateCard.tsx index ea411218b41..3173535808c 100644 --- a/libs/designer/src/lib/ui/templates/cards/templateCard.tsx +++ b/libs/designer/src/lib/ui/templates/cards/templateCard.tsx @@ -132,9 +132,11 @@ export const TemplateCard = ({ templateName }: TemplateCardProps) => { {details.Type} - - {details.Trigger} - + {details.Trigger ? ( + + {details.Trigger} + + ) : null}
{connectorsToShow.length > 0 ? ( diff --git a/libs/designer/src/lib/ui/templates/templateslist.tsx b/libs/designer/src/lib/ui/templates/templateslist.tsx index 18427c4a341..9ffb19e21a8 100644 --- a/libs/designer/src/lib/ui/templates/templateslist.tsx +++ b/libs/designer/src/lib/ui/templates/templateslist.tsx @@ -71,7 +71,7 @@ export const TemplatesList = ({ detailFilters, createWorkflowCall, isWorkflowEmp ) : null}
- {filteredTemplateNames?.length && filteredTemplateNames.length > 0 && ( + {(filteredTemplateNames?.length && filteredTemplateNames.length > 0) ? ( dispatch(setPageNum(page.value - 1))} /> - )} + ) : null}
{filteredTemplateNames?.length === 0 ? (
From d845caf88bb44ea46345db1898fd2b2c2d6eb427 Mon Sep 17 00:00:00 2001 From: Elaina Lee Date: Tue, 4 Mar 2025 22:24:53 -0800 Subject: [PATCH 06/24] fixed operation id not being picked up for builtin problem --- .../src/lib/core/templates/utils/helper.ts | 40 +++++++++++------ .../lib/ui/templates/cards/templateCard.tsx | 44 +++++++------------ .../ui/templates/connections/connector.tsx | 6 +-- .../ui/templates/filters/templateFilters.tsx | 2 +- .../src/utils/src/lib/models/template.ts | 6 +-- 5 files changed, 49 insertions(+), 49 deletions(-) diff --git a/libs/designer/src/lib/core/templates/utils/helper.ts b/libs/designer/src/lib/core/templates/utils/helper.ts index 2ab109f6a95..eeca51070ce 100644 --- a/libs/designer/src/lib/core/templates/utils/helper.ts +++ b/libs/designer/src/lib/core/templates/utils/helper.ts @@ -1,4 +1,4 @@ -import { type Template, getIntl, normalizeConnectorId } from '@microsoft/logic-apps-shared'; +import { type Template, getBuiltInOperationInfo, getIntl, isBuiltInOperation, normalizeConnectorId } from '@microsoft/logic-apps-shared'; import type { AppDispatch } from '../../../core'; import { summaryTab } from '../../../ui/panel/templatePanel/quickViewPanel/tabs/summaryTab'; import { workflowTab } from '../../../ui/panel/templatePanel/quickViewPanel/tabs/workflowTab'; @@ -49,26 +49,40 @@ export const getQuickViewTabs = ( }; export const getUniqueConnectors = ( - allFeaturedConectors: Template.FeaturedConnector[], + connections: Record, subscriptionId: string, location: string -): Template.FeaturedConnector[] => { - return getUniqueConnectorsFromConnections(allFeaturedConectors, subscriptionId, location); +): Template.Connection[] => { + const allConnections = Object.values(connections).map((connection) => ({ ...connection, id: connection.connectorId })); + return getUniqueConnectorsFromConnections(allConnections, subscriptionId, location); }; +interface UniqueConnectorInfo { + connectorId: string; + operationId: string | undefined; +} + export const getUniqueConnectorsFromConnections = ( - allFeaturedConectors: Template.FeaturedConnector[], + originalAllConnectors: Template.FeaturedConnector[], subscriptionId: string, location: string -): Template.FeaturedConnector[] => { - const result: Template.FeaturedConnector[] = []; +): UniqueConnectorInfo[] => { + const result: UniqueConnectorInfo[] = []; const finalConnectorIds: string[] = []; - while (allFeaturedConectors.length > 0) { - const connector = allFeaturedConectors.shift() as Template.FeaturedConnector; - const normalizedConnectorId = normalizeConnectorId(connector.id, subscriptionId, location).toLowerCase(); - if (!finalConnectorIds.includes(normalizedConnectorId)) { - finalConnectorIds.push(normalizedConnectorId); - result.push({ ...connector, id: normalizedConnectorId }); + const allConnectors = [...originalAllConnectors]; + + while (allConnectors.length > 0) { + const connection = allConnectors.shift() as Template.FeaturedConnector; + if (connection.kind === 'builtin') { + if (isBuiltInOperation(connection)) { + result.push(getBuiltInOperationInfo(connection, /* isTrigger */ false)); + } + } else { + const normalizedConnectorId = normalizeConnectorId(connection.id, subscriptionId, location).toLowerCase(); + if (!finalConnectorIds.includes(normalizedConnectorId)) { + finalConnectorIds.push(normalizedConnectorId); + result.push({ ...connection, connectorId: normalizedConnectorId, operationId: undefined }); + } } } diff --git a/libs/designer/src/lib/ui/templates/cards/templateCard.tsx b/libs/designer/src/lib/ui/templates/cards/templateCard.tsx index 3173535808c..cd436f55057 100644 --- a/libs/designer/src/lib/ui/templates/cards/templateCard.tsx +++ b/libs/designer/src/lib/ui/templates/cards/templateCard.tsx @@ -7,19 +7,13 @@ import type { IContextualMenuItem, IContextualMenuProps, IDocumentCardStyles } f import { DocumentCard, IconButton, Image, Shimmer, ShimmerElementType } from '@fluentui/react'; import { ConnectorIcon, ConnectorIconWithName } from '../connections/connector'; import { useIntl } from 'react-intl'; -import type { OperationInfo, Template } from '@microsoft/logic-apps-shared'; -import { - equals, - getBuiltInOperationInfo, - isBuiltInOperation, - LogEntryLevel, - LoggerService, - TemplateService, -} from '@microsoft/logic-apps-shared'; +import type { Template } from '@microsoft/logic-apps-shared'; +import { equals, LogEntryLevel, LoggerService, TemplateService } from '@microsoft/logic-apps-shared'; import MicrosoftIcon from '../../../common/images/templates/microsoft.svg'; import { Add16Regular, PeopleCommunity16Regular } from '@fluentui/react-icons'; import { isMultiWorkflowTemplate, loadTemplate } from '../../../core/actions/bjsworkflow/templates'; import { useMemo } from 'react'; +import { getUniqueConnectorsFromConnections } from '../../../core/templates/utils/helper'; interface TemplateCardProps { templateName: string; @@ -35,7 +29,7 @@ const cardStyles: IDocumentCardStyles = { export const TemplateCard = ({ templateName }: TemplateCardProps) => { const dispatch = useDispatch(); const intl = useIntl(); - const { templateManifest, workflowAppName } = useSelector((state: RootState) => ({ + const { templateManifest, workflowAppName, subscriptionId, location } = useSelector((state: RootState) => ({ templateManifest: state.manifest.availableTemplates?.[templateName], subscriptionId: state.workflow.subscriptionId, workflowAppName: state.workflow.workflowAppName, @@ -82,9 +76,13 @@ export const TemplateCard = ({ templateName }: TemplateCardProps) => { } const { title, details, featuredConnectors = [] } = templateManifest as Template.TemplateManifest; - const showOverflow = featuredConnectors.length > maxConnectorsToShow; - const connectorsToShow = showOverflow ? featuredConnectors.slice(0, maxConnectorsToShow) : featuredConnectors; - const overflowList = showOverflow ? featuredConnectors.slice(maxConnectorsToShow) : []; + + const connectorsFromConnections = getUniqueConnectorsFromConnections(featuredConnectors, subscriptionId, location); + const allConnectors = connectorsFromConnections; + const showOverflow = allConnectors.length > maxConnectorsToShow; + const connectorsToShow = showOverflow ? allConnectors.slice(0, maxConnectorsToShow) : allConnectors; + const overflowList = showOverflow ? allConnectors.slice(maxConnectorsToShow) : []; + const onRenderMenuItem = (item: IContextualMenuItem) => ( { ); const onRenderMenuIcon = () =>
{`+${overflowList.length}`}
; const menuProps: IContextualMenuProps = { - items: overflowList.map((info) => ({ key: info.id, text: info.id, data: info, onRender: onRenderMenuItem })), + items: overflowList.map((info) => ({ key: info.connectorId, text: info.connectorId, data: info, onRender: onRenderMenuItem })), directionalHintFixed: true, className: 'msla-template-card-connector-menu-box', }; @@ -142,9 +140,9 @@ export const TemplateCard = ({ templateName }: TemplateCardProps) => { {connectorsToShow.length > 0 ? ( connectorsToShow.map((info) => ( )) @@ -247,15 +245,3 @@ const LoadingTemplateCard = () => { ); }; - -export const getFeaturedConnectors = (operationInfos: { type: string; kind?: string }[] = []): OperationInfo[] => { - return operationInfos - .map((info) => { - if (isBuiltInOperation(info)) { - return getBuiltInOperationInfo(info, /* isTrigger */ false); - } - - return undefined; - }) - .filter((info) => info !== undefined) as OperationInfo[]; -}; diff --git a/libs/designer/src/lib/ui/templates/connections/connector.tsx b/libs/designer/src/lib/ui/templates/connections/connector.tsx index 88ff84b2c05..c7a04c95b0f 100644 --- a/libs/designer/src/lib/ui/templates/connections/connector.tsx +++ b/libs/designer/src/lib/ui/templates/connections/connector.tsx @@ -15,15 +15,15 @@ import { isConnectionValid } from '../../../core/utils/connectors/connections'; export const ConnectorIcon = ({ connectorId, - // operationId, + operationId, classes, }: { connectorId: string; classes: Record; - // operationId?: string; + operationId?: string; styles?: IStyleFunctionOrObject; }) => { - const { data: connector, isLoading, isError } = useConnectorInfo(connectorId, undefined, /* useCachedData */ true); + const { data: connector, isLoading, isError } = useConnectorInfo(connectorId, operationId, /* useCachedData */ true); if (!connector) { return isLoading ? : isError ? : ; } diff --git a/libs/designer/src/lib/ui/templates/filters/templateFilters.tsx b/libs/designer/src/lib/ui/templates/filters/templateFilters.tsx index 60e51c97ff9..f0ce9b1c7f4 100644 --- a/libs/designer/src/lib/ui/templates/filters/templateFilters.tsx +++ b/libs/designer/src/lib/ui/templates/filters/templateFilters.tsx @@ -41,7 +41,7 @@ export const TemplateFilters = ({ detailFilters }: TemplateFiltersProps) => { const allConnectors = Object.values(skuTemplates).flatMap((template) => template.featuredConnectors ?? []); const uniqueConnectorsFromConnections = getUniqueConnectorsFromConnections(allConnectors, subscriptionId, location); - return uniqueConnectorsFromConnections.map((connector) => connector.id); + return uniqueConnectorsFromConnections.map((connector) => connector.connectorId); }, [availableTemplates, isConsumption, location, subscriptionId]); const selectedTabId = appliedDetailFilters?.Type?.[0]?.value ?? templateDefaultTabKey; diff --git a/libs/logic-apps-shared/src/utils/src/lib/models/template.ts b/libs/logic-apps-shared/src/utils/src/lib/models/template.ts index ac9edbd1b7b..b4577704141 100644 --- a/libs/logic-apps-shared/src/utils/src/lib/models/template.ts +++ b/libs/logic-apps-shared/src/utils/src/lib/models/template.ts @@ -1,6 +1,7 @@ export type SkuType = 'standard' | 'consumption'; export type WorkflowKindType = 'stateful' | 'stateless'; -export type ConnectorRuntimeType = 'inapp' | 'shared'; +export type ConnectorRuntimeType = 'inapp' | 'shared' | 'custom'; +export type FeaturedConnectorType = ConnectorRuntimeType | 'builtin'; export interface Manifest { title: string; @@ -42,7 +43,6 @@ export interface TemplateManifest { The key is the folder name, followed by metadata where name is default name to be used for creation */ workflows: Record; featuredConnectors?: FeaturedConnector[]; - // details: Record; details: { By: string; Type: string; @@ -108,7 +108,7 @@ export interface Connection { export interface FeaturedConnector { id: string; - kind?: ConnectorRuntimeType; + kind?: FeaturedConnectorType; } export interface TemplateContext { From cbf5d351697ad8c67f854bdd6fa1bfbfa75e9b48 Mon Sep 17 00:00:00 2001 From: Elaina Lee Date: Tue, 4 Mar 2025 22:40:09 -0800 Subject: [PATCH 07/24] fixed built in info not kicking in --- libs/designer/src/lib/core/templates/utils/helper.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/designer/src/lib/core/templates/utils/helper.ts b/libs/designer/src/lib/core/templates/utils/helper.ts index eeca51070ce..8c7c185f311 100644 --- a/libs/designer/src/lib/core/templates/utils/helper.ts +++ b/libs/designer/src/lib/core/templates/utils/helper.ts @@ -74,8 +74,8 @@ export const getUniqueConnectorsFromConnections = ( while (allConnectors.length > 0) { const connection = allConnectors.shift() as Template.FeaturedConnector; if (connection.kind === 'builtin') { - if (isBuiltInOperation(connection)) { - result.push(getBuiltInOperationInfo(connection, /* isTrigger */ false)); + if (isBuiltInOperation({ type: connection.id })) { + result.push(getBuiltInOperationInfo({ type: connection.id }, /* isTrigger */ false)); } } else { const normalizedConnectorId = normalizeConnectorId(connection.id, subscriptionId, location).toLowerCase(); From b664f3abe4611cb87fa57104bf5a04580b17bdbc Mon Sep 17 00:00:00 2001 From: Elaina Lee Date: Wed, 5 Mar 2025 11:24:50 -0800 Subject: [PATCH 08/24] removed builtin operation logic --- .../src/lib/core/templates/utils/helper.ts | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/libs/designer/src/lib/core/templates/utils/helper.ts b/libs/designer/src/lib/core/templates/utils/helper.ts index 8c7c185f311..1b75619890a 100644 --- a/libs/designer/src/lib/core/templates/utils/helper.ts +++ b/libs/designer/src/lib/core/templates/utils/helper.ts @@ -1,4 +1,4 @@ -import { type Template, getBuiltInOperationInfo, getIntl, isBuiltInOperation, normalizeConnectorId } from '@microsoft/logic-apps-shared'; +import { type Template, getIntl, normalizeConnectorId } from '@microsoft/logic-apps-shared'; import type { AppDispatch } from '../../../core'; import { summaryTab } from '../../../ui/panel/templatePanel/quickViewPanel/tabs/summaryTab'; import { workflowTab } from '../../../ui/panel/templatePanel/quickViewPanel/tabs/workflowTab'; @@ -73,16 +73,10 @@ export const getUniqueConnectorsFromConnections = ( while (allConnectors.length > 0) { const connection = allConnectors.shift() as Template.FeaturedConnector; - if (connection.kind === 'builtin') { - if (isBuiltInOperation({ type: connection.id })) { - result.push(getBuiltInOperationInfo({ type: connection.id }, /* isTrigger */ false)); - } - } else { - const normalizedConnectorId = normalizeConnectorId(connection.id, subscriptionId, location).toLowerCase(); - if (!finalConnectorIds.includes(normalizedConnectorId)) { - finalConnectorIds.push(normalizedConnectorId); - result.push({ ...connection, connectorId: normalizedConnectorId, operationId: undefined }); - } + const normalizedConnectorId = normalizeConnectorId(connection.id, subscriptionId, location).toLowerCase(); + if (!finalConnectorIds.includes(normalizedConnectorId)) { + finalConnectorIds.push(normalizedConnectorId); + result.push({ ...connection, connectorId: normalizedConnectorId, operationId: undefined }); } } From 4a201725745ea9c427496f6446d7bcb2d77f4c82 Mon Sep 17 00:00:00 2001 From: Elaina Lee Date: Wed, 5 Mar 2025 11:40:21 -0800 Subject: [PATCH 09/24] fixed mock data --- .../BasicWorkflowOnly/default/manifest.json | 23 ++++ .../{ => default}/workflow.json | 0 .../templates/BasicWorkflowOnly/manifest.json | 68 +++++------ .../SimpleAccelerator/Workflow1/manifest.json | 96 +++++++-------- .../SimpleAccelerator/Workflow2/manifest.json | 109 ++++++++---------- .../templates/SimpleAccelerator/manifest.json | 76 ++++++------ .../default/manifest.json | 37 ++++++ .../{ => default}/workflow.json | 0 .../SimpleConnectionParameter/manifest.json | 73 +++++------- .../default/manifest.json | 31 +++++ .../{ => default}/workflow.json | 0 .../SimpleParametersOnly/manifest.json | 76 +++++------- 12 files changed, 305 insertions(+), 284 deletions(-) create mode 100644 __mocks__/templates/BasicWorkflowOnly/default/manifest.json rename __mocks__/templates/BasicWorkflowOnly/{ => default}/workflow.json (100%) create mode 100644 __mocks__/templates/SimpleConnectionParameter/default/manifest.json rename __mocks__/templates/SimpleConnectionParameter/{ => default}/workflow.json (100%) create mode 100644 __mocks__/templates/SimpleParametersOnly/default/manifest.json rename __mocks__/templates/SimpleParametersOnly/{ => default}/workflow.json (100%) diff --git a/__mocks__/templates/BasicWorkflowOnly/default/manifest.json b/__mocks__/templates/BasicWorkflowOnly/default/manifest.json new file mode 100644 index 00000000000..ff45fa61c43 --- /dev/null +++ b/__mocks__/templates/BasicWorkflowOnly/default/manifest.json @@ -0,0 +1,23 @@ +{ + "id": "default", + "title": "[Mock] Basic Workflow Only Template", + "summary": "This is a simple workflow only template description", + "description": "This is a basic workflow only template details description \n- point 1 \n- point 2.", + "kinds": [ + "stateful", + "stateless" + ], + "artifacts": [ + { + "type": "workflow", + "file": "workflow.json" + } + ], + "images": { + "light": "/templates/BasicWorkflowOnly/workflow-light.png", + "dark": "/templates/BasicWorkflowOnly/workflow-dark.png" + }, + "parameters": [], + "connections": {}, + "sourceCodeUrl": "/sourceCode" +} \ No newline at end of file diff --git a/__mocks__/templates/BasicWorkflowOnly/workflow.json b/__mocks__/templates/BasicWorkflowOnly/default/workflow.json similarity index 100% rename from __mocks__/templates/BasicWorkflowOnly/workflow.json rename to __mocks__/templates/BasicWorkflowOnly/default/workflow.json diff --git a/__mocks__/templates/BasicWorkflowOnly/manifest.json b/__mocks__/templates/BasicWorkflowOnly/manifest.json index 6882e7eaaad..bd53deb8cdf 100644 --- a/__mocks__/templates/BasicWorkflowOnly/manifest.json +++ b/__mocks__/templates/BasicWorkflowOnly/manifest.json @@ -1,41 +1,31 @@ { - "title": "[Mock] Basic Workflow Only Template", - "description": "This is a simple workflow only template description", - "tags": [ - "Basic Workflow Only", - "try catch" - ], - "skus": [ - "consumption", - "standard" - ], - "kinds": [ - "stateful", - "stateless" - ], - "detailsDescription": "This is a basic workflow only template details description \n- point 1 \n- point 2.", - "details": { - "By": "Microsoft", - "Type": "Workflow", - "Trigger": "Request", - "Category": "Mock" - }, - "artifacts": [ - { - "type": "workflow", - "file": "workflow.json" - } - ], - "images": { - "light": "/templates/BasicWorkflowOnly/workflow-light.png", - "dark": "/templates/BasicWorkflowOnly/workflow-dark.png" - }, - "parameters": [], - "connections": {}, - "featuredOperations": [ - { - "type": "Scope" - } - ], - "sourceCodeUrl": "/sourceCode" + "id": "BasicWorkflowOnly", + "title": "[Mock] Basic Workflow Only Template", + "summary": "This is a simple workflow only template description", + "skus": [ + "consumption", + "standard" + ], + "workflows": { + "default": { + "name": "BasicWorkflowOnly" + } + }, + "featuredConnectors": [ + { + "id": "connectionProviders/control", + "kind": "builtin" + } + ], + "details": { + "By": "Microsoft", + "Type": "Workflow", + "Trigger": "Request", + "Category": "Mock" + }, + "tags": [ + "Basic Workflow Only", + "try catch" + ], + "sourceCodeUrl": "/sourceCode" } \ No newline at end of file diff --git a/__mocks__/templates/SimpleAccelerator/Workflow1/manifest.json b/__mocks__/templates/SimpleAccelerator/Workflow1/manifest.json index 7d154da6062..2f8f24a9e2d 100644 --- a/__mocks__/templates/SimpleAccelerator/Workflow1/manifest.json +++ b/__mocks__/templates/SimpleAccelerator/Workflow1/manifest.json @@ -1,56 +1,48 @@ { - "title": "Workflow 1", - "description": "Workflow 1 description", - "prerequisites": "Workflow 1 prerequisites", - "skus": [ - "standard" - ], - "kinds": [ - "stateful", - "stateless" - ], - "detailsDescription": "Workflow 1 details description \n- point 1 \n- point 2.", - "details": { - "By": "Microsoft", - "Type": "Workflow", - "Trigger": "Request", - "Category": "AI,RAG" - }, - "artifacts": [ - { - "type": "workflow", - "file": "workflow.json" - } - ], - "images": { - "light": "/templates/SimpleAccelerator/Workflow1/workflow-light.png", - "dark": "/templates/SimpleAccelerator/Workflow1/workflow-dark.png" - }, - "parameters": [ - { - "name": "OpenAIEmbeddingModel_#workflowname#", - "displayName": "Azure OpenAI text embedding deployment model name", - "type": "String", - "description": "Provide the Azure OpenAI embedding model to generate vector embeddings.", - "required": true + "id": "Workflow1", + "title": "Workflow 1", + "summary": "Workflow 1 description", + "description": "Workflow 1 details description \n- point 1 \n- point 2.", + "prerequisites": "Workflow 1 prerequisites", + "kinds": [ + "stateful", + "stateless" + ], + "artifacts": [ + { + "type": "workflow", + "file": "workflow.json" + } + ], + "images": { + "light": "/templates/SimpleAccelerator/Workflow1/workflow-light.png", + "dark": "/templates/SimpleAccelerator/Workflow1/workflow-dark.png" }, - { - "name": "OpenAIChatModel_#workflowname#", - "displayName": "Azure OpenAI chat model name", - "type": "String", - "description": "Provide the Azure OpenAI chat model name to use for chat completions.", - "required": true - } - ], - "connections": { - "azuresql_#workflowname#": { - "connectorId": "/serviceProviders/sql", - "kind": "inapp" + "parameters": [ + { + "name": "OpenAIEmbeddingModel_#workflowname#", + "displayName": "Azure OpenAI text embedding deployment model name", + "type": "String", + "description": "Provide the Azure OpenAI embedding model to generate vector embeddings.", + "required": true + }, + { + "name": "OpenAIChatModel_#workflowname#", + "displayName": "Azure OpenAI chat model name", + "type": "String", + "description": "Provide the Azure OpenAI chat model name to use for chat completions.", + "required": true + } + ], + "connections": { + "azuresql_#workflowname#": { + "connectorId": "/serviceProviders/sql", + "kind": "inapp" + }, + "openai_#workflowname#": { + "connectorId": "/serviceProviders/openai", + "kind": "inapp" + } }, - "openai_#workflowname#": { - "connectorId": "/serviceProviders/openai", - "kind": "inapp" - } - }, - "sourceCodeUrl": "/sourceCodeUrl" + "sourceCodeUrl": "/sourceCode" } \ No newline at end of file diff --git a/__mocks__/templates/SimpleAccelerator/Workflow2/manifest.json b/__mocks__/templates/SimpleAccelerator/Workflow2/manifest.json index 9b1ef8e0e2e..b1a1ce32ee1 100644 --- a/__mocks__/templates/SimpleAccelerator/Workflow2/manifest.json +++ b/__mocks__/templates/SimpleAccelerator/Workflow2/manifest.json @@ -1,65 +1,52 @@ { - "title": "Workflow 2", - "description": "Workflow 2 description", - "prerequisites": "Workflow 2 prerequisites", - "skus": [ - "standard" - ], - "kinds": [ - "stateful", - "stateless" - ], - "detailsDescription": "Workflow 2 details description \n- point 1 \n- point 2.", - "details": { - "By": "Microsoft", - "Type": "Workflow", - "Trigger": "Request", - "Category": "AI,RAG" - }, - "artifacts": [ - { - "type": "workflow", - "file": "workflow.json" - } - ], - "images": { - "light": "/templates/SimpleAccelerator/Workflow2/workflow-light.png", - "dark": "/templates/SimpleAccelerator/Workflow2/workflow-dark.png" - }, - "parameters": [ - { - "name": "OpenAIEmbeddingModel_#workflowname#", - "displayName": "Azure OpenAI text embedding deployment model name", - "type": "String", - "description": "Provide the Azure OpenAI embedding model to generate vector embeddings.", - "required": true + "id": "Workflow2", + "title": "Workflow 2", + "summary": "Workflow 2 description", + "description": "Workflow 2 details description \n- point 1 \n- point 2.", + "prerequisites": "Workflow 2 prerequisites", + "kinds": [ + "stateful", + "stateless" + ], + "artifacts": [ + { + "type": "workflow", + "file": "workflow.json" + } + ], + "images": { + "light": "/templates/SimpleAccelerator/Workflow2/workflow-light.png", + "dark": "/templates/SimpleAccelerator/Workflow2/workflow-dark.png" }, - { - "name": "BlobPath_#workflowname#", - "displayName": "Blob Path", - "type": "String", - "description": "Provide the Azure Blob container name from where to get your document.", - "required": true - } - ], - "connections": { - "azuresql_#workflowname#": { - "connectorId": "/serviceProviders/sql", - "kind": "inapp" + "parameters": [ + { + "name": "OpenAIEmbeddingModel_#workflowname#", + "displayName": "Azure OpenAI text embedding deployment model name", + "type": "String", + "description": "Provide the Azure OpenAI embedding model to generate vector embeddings.", + "required": true + }, + { + "name": "BlobPath_#workflowname#", + "displayName": "Blob Path", + "type": "String", + "description": "Provide the Azure Blob container name from where to get your document.", + "required": true + } + ], + "connections": { + "azuresql_#workflowname#": { + "connectorId": "/serviceProviders/sql", + "kind": "inapp" + }, + "openai_#workflowname#": { + "connectorId": "/serviceProviders/openai", + "kind": "inapp" + }, + "azureblob_#workflowname#": { + "connectorId": "/serviceProviders/AzureBlob", + "kind": "inapp" + } }, - "openai_#workflowname#": { - "connectorId": "/serviceProviders/openai", - "kind": "inapp" - }, - "azureblob_#workflowname#": { - "connectorId": "/serviceProviders/AzureBlob", - "kind": "inapp" - } - }, - "featuredOperations": [ - { - "type": "ChunkText" - } - ], - "sourceCodeUrl": "/sourceCodeUrl" + "sourceCodeUrl": "/sourceCode" } \ No newline at end of file diff --git a/__mocks__/templates/SimpleAccelerator/manifest.json b/__mocks__/templates/SimpleAccelerator/manifest.json index 8fdaf1fde74..23363509316 100644 --- a/__mocks__/templates/SimpleAccelerator/manifest.json +++ b/__mocks__/templates/SimpleAccelerator/manifest.json @@ -1,43 +1,41 @@ { - "title": "[Mock] Simple Accelerator Template", - "description": "This is a simple accelerator description", - "skus": [ - "standard" - ], - "detailsDescription": "This is a simple accelerator details description \n- point 1 \n- point 2.", - "details": { - "By": "Microsoft", - "Type": "Accelerator", - "Category": "Mock" - }, - "artifacts": [], - "workflows": { - "Workflow1": { - "name": "Workflow1_name" + "id": "SimpleAccelerator", + "title": "[Mock] Simple Accelerator Template", + "summary": "This is a simple accelerator description", + "description": "This is a simple accelerator details description \n- point 1 \n- point 2.", + "skus": [ + "standard" + ], + "workflows": { + "Workflow1": { + "name": "Workflow1_name" + }, + "Workflow2": { + "name": "Workflow2_name" + } }, - "Workflow2": { - "name": "Workflow2_name" - } - }, - "parameters": [], - "connections": { - "azuresql_#workflowname#": { - "connectorId": "/serviceProviders/sql", - "kind": "inapp" + "featuredConnectors": [ + { + "id": "connectionProviders/dataOperationNew", + "kind": "builtin" + }, + { + "id": "/serviceProviders/sql", + "kind": "inapp" + }, + { + "id": "/serviceProviders/openai", + "kind": "inapp" + }, + { + "id": "/serviceProviders/AzureBlob", + "kind": "inapp" + } + ], + "details": { + "By": "Microsoft", + "Type": "Accelerator", + "Category": "Mock" }, - "openai_#workflowname#": { - "connectorId": "/serviceProviders/openai", - "kind": "inapp" - }, - "azureblob_#workflowname#": { - "connectorId": "/serviceProviders/AzureBlob", - "kind": "inapp" - } - }, - "featuredOperations": [ - { - "type": "ChunkText" - } - ], - "sourceCodeUrl": "/sourceCodeUrl" + "sourceCodeUrl": "/sourceCode" } \ No newline at end of file diff --git a/__mocks__/templates/SimpleConnectionParameter/default/manifest.json b/__mocks__/templates/SimpleConnectionParameter/default/manifest.json new file mode 100644 index 00000000000..7f2f8f5ef3b --- /dev/null +++ b/__mocks__/templates/SimpleConnectionParameter/default/manifest.json @@ -0,0 +1,37 @@ +{ + "id": "default", + "title": "[Mock] Simple Connection Parameter Template", + "summary": "This is a simple connection parameter template description", + "description": "This is a simple connection parameter template details description \n- point 1 \n- point 2.", + "prerequisites": "This is a simple connection parameter template prerequisites", + "kinds": [ + "stateful", + "stateless" + ], + "artifacts": [ + { + "type": "workflow", + "file": "workflow.json" + } + ], + "images": { + "light": "/templates/SimpleConnectionParameter/workflow-light.png", + "dark": "/templates/SimpleConnectionParameter/workflow-dark.png" + }, + "parameters": [ + { + "name": "AzureOpenAI_deployment_model_vision_#workflowname#", + "displayName": "OpenAI deployment model", + "type": "String", + "description": "Provide the Azure OpenAI deployment model that supports image and vision.", + "required": true + } + ], + "connections": { + "Openai_#workflowname#": { + "connectorId": "/serviceProviders/openai", + "kind": "inapp" + } + }, + "sourceCodeUrl": "/sourceCode" +} \ No newline at end of file diff --git a/__mocks__/templates/SimpleConnectionParameter/workflow.json b/__mocks__/templates/SimpleConnectionParameter/default/workflow.json similarity index 100% rename from __mocks__/templates/SimpleConnectionParameter/workflow.json rename to __mocks__/templates/SimpleConnectionParameter/default/workflow.json diff --git a/__mocks__/templates/SimpleConnectionParameter/manifest.json b/__mocks__/templates/SimpleConnectionParameter/manifest.json index 2af2aef5477..0f675e5ef3e 100644 --- a/__mocks__/templates/SimpleConnectionParameter/manifest.json +++ b/__mocks__/templates/SimpleConnectionParameter/manifest.json @@ -1,48 +1,29 @@ { - "title": "[Mock] Simple Connection Parameter Template", - "description": "This is a simple connection parameter template description", - "prerequisites": "This is a simple connection parameter template prerequisites", - "detailsDescription": "This is a simple connection parameter template details description \n- point 1 \n- point 2.", - "tags": [ - "Simple-Connection-Parameter" - ], - "skus": [ - "standard" - ], - "kinds": [ - "stateful", - "stateless" - ], - "details": { - "By": "Microsoft", - "Type": "Workflow", - "Trigger": "Request", - "Category": "Mock" - }, - "artifacts": [ - { - "type": "workflow", - "file": "workflow.json" - } - ], - "images": { - "light": "/templates/SimpleConnectionParameter/workflow-light.png", - "dark": "/templates/SimpleConnectionParameter/workflow-dark.png" - }, - "parameters": [ - { - "name": "AzureOpenAI_deployment_model_vision_#workflowname#", - "displayName": "OpenAI deployment model", - "type": "String", - "description": "Provide the Azure OpenAI deployment model that supports image and vision.", - "required": true - } - ], - "connections": { - "Openai_#workflowname#": { - "connectorId": "/serviceProviders/openai", - "kind": "inapp" - } - }, - "sourceCodeUrl": "/sourceCodeUrl" + "id": "SimpleConnectionParameter", + "title": "[Mock] Simple Connection Parameter Template", + "summary": "This is a simple connection parameter template description", + "skus": [ + "standard" + ], + "workflows": { + "default": { + "name": "SimpleConnectionParameter" + } + }, + "featuredConnectors": [ + { + "id": "/serviceProviders/openai", + "kind": "inapp" + } + ], + "details": { + "By": "Microsoft", + "Type": "Workflow", + "Trigger": "Request", + "Category": "Mock" + }, + "tags": [ + "Simple-Connection-Parameter" + ], + "sourceCodeUrl": "/sourceCode" } \ No newline at end of file diff --git a/__mocks__/templates/SimpleParametersOnly/default/manifest.json b/__mocks__/templates/SimpleParametersOnly/default/manifest.json new file mode 100644 index 00000000000..e4331f4fe46 --- /dev/null +++ b/__mocks__/templates/SimpleParametersOnly/default/manifest.json @@ -0,0 +1,31 @@ +{ + "id": "default", + "title": "[Mock] Simple Parameters Only Template", + "summary": "This is a simple workflow only template description", + "description": "This is a basic workflow only template details description \n- point 1 \n- point 2.", + "kinds": [ + "stateful", + "stateless" + ], + "artifacts": [ + { + "type": "workflow", + "file": "workflow.json" + } + ], + "images": { + "light": "/templates/BasicWorkflowOnly/workflow-light.png", + "dark": "/templates/BasicWorkflowOnly/workflow-dark.png" + }, + "parameters": [ + { + "name": "LogicMessage_#workflowname#", + "displayName": "Business Logic Message", + "type": "String", + "description": "Provide the message run by the business logic.", + "required": true + } + ], + "connections": {}, + "sourceCodeUrl": "/sourceCode" +} \ No newline at end of file diff --git a/__mocks__/templates/SimpleParametersOnly/workflow.json b/__mocks__/templates/SimpleParametersOnly/default/workflow.json similarity index 100% rename from __mocks__/templates/SimpleParametersOnly/workflow.json rename to __mocks__/templates/SimpleParametersOnly/default/workflow.json diff --git a/__mocks__/templates/SimpleParametersOnly/manifest.json b/__mocks__/templates/SimpleParametersOnly/manifest.json index d5cf9aaf332..b4507b249ae 100644 --- a/__mocks__/templates/SimpleParametersOnly/manifest.json +++ b/__mocks__/templates/SimpleParametersOnly/manifest.json @@ -1,49 +1,31 @@ { - "title": "[Mock] Simple Parameters Only Template", - "description": "This is a simple workflow only template description", - "tags": [ - "Simple Parameters Only", - "try catch" - ], - "skus": [ - "consumption", - "standard" - ], - "kinds": [ - "stateful", - "stateless" - ], - "detailsDescription": "This is a basic workflow only template details description \n- point 1 \n- point 2.", - "details": { - "By": "Microsoft", - "Type": "Workflow", - "Trigger": "Request", - "Category": "Mock" - }, - "artifacts": [ - { - "type": "workflow", - "file": "workflow.json" - } - ], - "images": { - "light": "/templates/BasicWorkflowOnly/workflow-light.png", - "dark": "/templates/BasicWorkflowOnly/workflow-dark.png" - }, - "parameters": [ - { - "name": "LogicMessage_#workflowname#", - "displayName": "Business Logic Message", - "type": "String", - "description": "Provide the message run by the business logic.", - "required": true - } - ], - "connections": {}, - "featuredOperations": [ - { - "type": "Scope" - } - ], - "sourceCodeUrl": "/sourceCode" + "id": "SimpleParametersOnly", + "title": "[Mock] Simple Parameters Only Template", + "summary": "This is a simple workflow only template description", + "skus": [ + "consumption", + "standard" + ], + "workflows": { + "default": { + "name": "SimpleParametersOnly" + } + }, + "featuredConnectors": [ + { + "id": "connectionProviders/control", + "kind": "builtin" + } + ], + "details": { + "By": "Microsoft", + "Type": "Workflow", + "Trigger": "Request", + "Category": "Mock" + }, + "tags": [ + "Simple Parameters Only", + "try catch" + ], + "sourceCodeUrl": "/sourceCode" } \ No newline at end of file From 25c9a7d9dbb4a2a92375c5402585ae7ec140b94d Mon Sep 17 00:00:00 2001 From: Elaina Lee Date: Wed, 5 Mar 2025 16:32:30 -0800 Subject: [PATCH 10/24] removed unused function --- apps/Standalone/src/templates/app/LocalTemplates.tsx | 2 +- downloadTemplates.js | 2 +- .../src/lib/core/state/templates/templateselectors.ts | 8 -------- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/apps/Standalone/src/templates/app/LocalTemplates.tsx b/apps/Standalone/src/templates/app/LocalTemplates.tsx index f2eb3a928ed..a877a51a236 100644 --- a/apps/Standalone/src/templates/app/LocalTemplates.tsx +++ b/apps/Standalone/src/templates/app/LocalTemplates.tsx @@ -24,7 +24,7 @@ const loadLocalTemplateFromResourcePath = async (resourcePath: string, artifactT return paths.length === 2 ? (await import(`./../../../../../__mocks__/templates/${paths[0]}/${paths[1]}/${artifactType}.json`)).default - : (await import(`./../../../../../__mocks__/templates//${resourcePath}/${artifactType}.json`)).default; + : (await import(`./../../../../../__mocks__/templates/${resourcePath}/${artifactType}.json`)).default; }; const localTemplateManifestPaths = ['BasicWorkflowOnly', 'SimpleConnectionParameter', 'SimpleAccelerator', 'SimpleParametersOnly']; diff --git a/downloadTemplates.js b/downloadTemplates.js index 4f6bf069b94..e445b2a0635 100644 --- a/downloadTemplates.js +++ b/downloadTemplates.js @@ -3,7 +3,7 @@ import { existsSync, writeFile, createWriteStream } from 'fs'; import { mkdir, rm } from 'fs/promises'; import client from 'https'; -const releaseBranch = 'elaina/addSummaryField'; +const releaseBranch = 'main'; const baseURL = `https://raw.githubusercontent.com/azure/LogicAppsTemplates/${releaseBranch}`; const sourceCodeURL = `https://github.com/Azure/LogicAppsTemplates/tree/${releaseBranch}`; diff --git a/libs/designer/src/lib/core/state/templates/templateselectors.ts b/libs/designer/src/lib/core/state/templates/templateselectors.ts index 99e61f9ba7e..ef56e946304 100644 --- a/libs/designer/src/lib/core/state/templates/templateselectors.ts +++ b/libs/designer/src/lib/core/state/templates/templateselectors.ts @@ -33,14 +33,6 @@ export const useWorkflowBasicsEditable = (workflowId: string) => { }); }; -//TODO: remove this -export const useDefaultWorkflowTemplate = (): WorkflowTemplateData => { - return useSelector((state: RootState) => { - const workflows = state.template.workflows ?? {}; - return Object.values(workflows)[0]; - }); -}; - export const useConnectionReferenceForKey = (key: string): ConnectionReference => { return useSelector((state: RootState) => { const connections = state.workflow.connections; From 77c33738678118e6b8fadd2c072fb16be1bfe1d5 Mon Sep 17 00:00:00 2001 From: Elaina Lee Date: Wed, 5 Mar 2025 16:46:00 -0800 Subject: [PATCH 11/24] fixed createWorkflowPanel test --- .../__tests__/createWorkflowPanel.spec.tsx | 92 +++++++++++++------ .../src/utils/src/lib/models/template.ts | 25 ----- 2 files changed, 66 insertions(+), 51 deletions(-) diff --git a/libs/designer/src/lib/ui/panel/templatePanel/__tests__/createWorkflowPanel.spec.tsx b/libs/designer/src/lib/ui/panel/templatePanel/__tests__/createWorkflowPanel.spec.tsx index 2d2c2e5224d..c52f0bdbb8e 100644 --- a/libs/designer/src/lib/ui/panel/templatePanel/__tests__/createWorkflowPanel.spec.tsx +++ b/libs/designer/src/lib/ui/panel/templatePanel/__tests__/createWorkflowPanel.spec.tsx @@ -17,14 +17,18 @@ import { CreateWorkflowPanel } from '../createWorkflowPanel/createWorkflowPanel' describe('panel/templatePanel/createWorkflowPanel', () => { let store: AppStore; let templateSliceData: TemplateState; - let template1Manifest: Template.Manifest; - let template2Manifest: Template.Manifest; + let template1Manifest: Template.TemplateManifest; + let template2Manifest: Template.TemplateManifest; + let workflow1Manifest: Template.WorkflowManifest; + let workflow2Manifest: Template.WorkflowManifest; let param1DefaultValue: string; const defaultWorkflowId = 'default'; const httpClient = new MockHttpClient(); InitTemplateService( new StandardTemplateService({ + endpoint: '', + useEndpointForTemplates: false, baseUrl: '/baseUrl', appId: '/appId', httpClient, @@ -35,7 +39,7 @@ describe('panel/templatePanel/createWorkflowPanel', () => { openBladeAfterCreate: (workflowName: string | undefined) => { console.log('Open blade after create', workflowName); }, - onAddBlankWorkflow: () => { + onAddBlankWorkflow: async () => { console.log('Add blank workflow'); }, }) @@ -44,22 +48,41 @@ describe('panel/templatePanel/createWorkflowPanel', () => { beforeAll(() => { param1DefaultValue = 'default value for param 1'; template1Manifest = { + id: 'template1Manifest', title: 'Template 1', - description: 'Template 1 Description', + summary: 'Template 1 Description', skus: ['standard', 'consumption'], + workflows: { + default: { name: 'default' }, + }, + details: { + By: '', + Type: '', + Category: '', + }, + artifacts: [ + { + type: 'description', + file: 'description.md', + }, + ], + }; + + workflow1Manifest = { + id: 'default', + title: 'Template 1', + summary: 'Template 1 Description', kinds: ['stateful', 'stateless'], - details: {}, - images: {}, artifacts: [ { type: 'workflow', file: 'workflow.json', }, - { - type: 'description', - file: 'description.md', - }, ], + images: { + light: '', + dark: '', + }, connections: {}, parameters: [ { @@ -79,22 +102,41 @@ describe('panel/templatePanel/createWorkflowPanel', () => { }; template2Manifest = { + id: 'template2Manifest', title: 'Template 2', - description: 'Template 2 Description - Consumption Only', + summary: 'Template 2 Description - Consumption Only', skus: ['consumption'], + workflows: { + default: { name: 'default' }, + }, + details: { + By: '', + Type: '', + Category: '', + }, + artifacts: [ + { + type: 'description', + file: 'description.md', + }, + ], + }; + + workflow2Manifest = { + id: 'default', + title: 'Template 2', + summary: 'Template 2 Description - Consumption Only', kinds: ['stateful', 'stateless'], - details: {}, - images: {}, artifacts: [ { type: 'workflow', file: 'workflow.json', }, - { - type: 'description', - file: 'description.md', - }, ], + images: { + light: '', + dark: '', + }, connections: {}, parameters: [ { @@ -119,7 +161,7 @@ describe('panel/templatePanel/createWorkflowPanel', () => { id: defaultWorkflowId, workflowName: '', kind: undefined, - manifest: template1Manifest, + manifest: workflow1Manifest, workflowDefinition: { $schema: 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#', contentVersion: '', @@ -133,15 +175,14 @@ describe('panel/templatePanel/createWorkflowPanel', () => { }, templateName: template1Manifest.title, manifest: template1Manifest, - parameterDefinitions: template1Manifest.parameters?.reduce((result: Record, parameter) => { + parameterDefinitions: workflow1Manifest.parameters?.reduce((result: Record, parameter) => { result[parameter.name] = { ...parameter, value: parameter.default, }; return result; }, {}), - connections: template1Manifest.connections, - servicesInitialized: false, + connections: workflow1Manifest.connections, errors: { parameters: {}, connections: undefined, @@ -176,7 +217,7 @@ describe('panel/templatePanel/createWorkflowPanel', () => { expect(store.getState().template.manifest).toBe(template1Manifest); expect(store.getState().template.parameterDefinitions).toBeDefined(); expect(store.getState().template.errors.parameters).toEqual({}); - expect(store.getState().template.connections).toBe(template1Manifest.connections); + expect(store.getState().template.connections).toBe(workflow1Manifest.connections); }); it('Shows Connections Tab for the first rendering without selected tab id', async () => { @@ -199,7 +240,7 @@ describe('panel/templatePanel/createWorkflowPanel', () => { id: defaultWorkflowId, workflowName: '', kind: undefined, - manifest: template2Manifest, + manifest: workflow2Manifest, workflowDefinition: { $schema: 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#', contentVersion: '', @@ -213,15 +254,14 @@ describe('panel/templatePanel/createWorkflowPanel', () => { }, templateName: template2Manifest.title, manifest: template2Manifest, - parameterDefinitions: template2Manifest.parameters?.reduce((result: Record, parameter) => { + parameterDefinitions: workflow2Manifest.parameters?.reduce((result: Record, parameter) => { result[parameter.name] = { ...parameter, value: parameter.default, }; return result; }, {}), - connections: template2Manifest.connections, - servicesInitialized: false, + connections: workflow2Manifest.connections, errors: { parameters: {}, connections: undefined, diff --git a/libs/logic-apps-shared/src/utils/src/lib/models/template.ts b/libs/logic-apps-shared/src/utils/src/lib/models/template.ts index b4577704141..eb797806764 100644 --- a/libs/logic-apps-shared/src/utils/src/lib/models/template.ts +++ b/libs/logic-apps-shared/src/utils/src/lib/models/template.ts @@ -3,31 +3,6 @@ export type WorkflowKindType = 'stateful' | 'stateless'; export type ConnectorRuntimeType = 'inapp' | 'shared' | 'custom'; export type FeaturedConnectorType = ConnectorRuntimeType | 'builtin'; -export interface Manifest { - title: string; - summary: string; - skus: SkuType[]; - kinds?: WorkflowKindType[]; - details: Record; - - /* This is a markdown to show features for multi-workflow and details in case of single workflow */ - description?: string; - - tags?: string[]; - artifacts: Artifact[]; - - /* This consists of list of workflows listed in the multi-workflow template. - The key is the folder name, followed by metadata where name is default name to be used for creation */ - workflows?: Record; - - images: Record; - prerequisites?: string; - parameters: Parameter[]; - connections: Record; - featuredOperations?: { type: string; kind?: string }[]; - sourceCodeUrl?: string; // Automatically generated for public templates, otherwise optional -} - export type DetailsType = 'By' | 'Type' | 'Category' | 'Trigger'; export interface TemplateManifest { From 22d528cd1ebcc404b47734f42da3dd49282f3a38 Mon Sep 17 00:00:00 2001 From: Elaina Lee Date: Wed, 5 Mar 2025 16:54:02 -0800 Subject: [PATCH 12/24] fixed template panel tests --- .../__tests__/nameStateTab.spec.tsx | 16 ++-- .../__tests__/parametersTab.spec.tsx | 9 +- .../__tests__/quickViewPanel.spec.tsx | 85 ++++++++++++++----- .../__tests__/displayConnections.spec.tsx | 62 +++++++++++--- 4 files changed, 121 insertions(+), 51 deletions(-) diff --git a/libs/designer/src/lib/ui/panel/templatePanel/__tests__/nameStateTab.spec.tsx b/libs/designer/src/lib/ui/panel/templatePanel/__tests__/nameStateTab.spec.tsx index 9d2e7152001..85ba20f5f02 100644 --- a/libs/designer/src/lib/ui/panel/templatePanel/__tests__/nameStateTab.spec.tsx +++ b/libs/designer/src/lib/ui/panel/templatePanel/__tests__/nameStateTab.spec.tsx @@ -10,25 +10,22 @@ import { QueryClientProvider } from '@tanstack/react-query'; import { getReactQueryClient } from '../../../../core'; // biome-ignore lint/correctness/noUnusedImports: import React from 'react'; +import type { TemplateState } from '../../../../core/state/templates/templateSlice'; describe('panel/templatePanel/createWorkflowPanel/nameStateTab', () => { let store: AppStore; beforeAll(() => { - const templateSliceData = { + const templateSliceData: TemplateState = { workflows: { default: { id: 'default', workflowName: 'workflowName 1', kind: undefined, - templateName: 'title', manifest: { + id: 'default', title: 'Template 1', - description: 'Template 1 Description', - tags: [], - details: {}, - images: {}, - skus: [], + summary: 'Template 1 Description', kinds: [], artifacts: [ { @@ -36,6 +33,10 @@ describe('panel/templatePanel/createWorkflowPanel/nameStateTab', () => { file: 'workflow.json', }, ], + images: { + light: '', + dark: '', + }, connections: {}, parameters: [], }, @@ -53,7 +54,6 @@ describe('panel/templatePanel/createWorkflowPanel/nameStateTab', () => { manifest: undefined, parameterDefinitions: {}, connections: {}, - servicesInitialized: false, errors: { parameters: {}, connections: undefined, diff --git a/libs/designer/src/lib/ui/panel/templatePanel/__tests__/parametersTab.spec.tsx b/libs/designer/src/lib/ui/panel/templatePanel/__tests__/parametersTab.spec.tsx index 834efccf65c..3b9e770ef29 100644 --- a/libs/designer/src/lib/ui/panel/templatePanel/__tests__/parametersTab.spec.tsx +++ b/libs/designer/src/lib/ui/panel/templatePanel/__tests__/parametersTab.spec.tsx @@ -10,6 +10,7 @@ import { ParametersPanel } from '../createWorkflowPanel/tabs/parametersTab'; import { ReactQueryProvider } from '../../../../core/ReactQueryProvider'; // biome-ignore lint/correctness/noUnusedImports: import React from 'react'; +import type { TemplateState } from '../../../../core/state/templates/templateSlice'; describe('panel/templatePanel/createWorkflowPanel/parametersTab', () => { let store: AppStore; @@ -36,9 +37,7 @@ describe('panel/templatePanel/createWorkflowPanel/parametersTab', () => { }; const parameters = [param1, param2]; - const templateSliceData = { - workflowName: '', - kind: undefined, + const templateSliceData: TemplateState = { templateName: 'title', manifest: undefined, workflows: {}, @@ -49,12 +48,8 @@ describe('panel/templatePanel/createWorkflowPanel/parametersTab', () => { }; return result; }, {}), - validationErrors: {}, connections: {}, - servicesInitialized: false, errors: { - workflow: undefined, - kind: undefined, parameters: {}, connections: undefined, }, diff --git a/libs/designer/src/lib/ui/panel/templatePanel/__tests__/quickViewPanel.spec.tsx b/libs/designer/src/lib/ui/panel/templatePanel/__tests__/quickViewPanel.spec.tsx index 252d6c79261..7ea69b65695 100644 --- a/libs/designer/src/lib/ui/panel/templatePanel/__tests__/quickViewPanel.spec.tsx +++ b/libs/designer/src/lib/ui/panel/templatePanel/__tests__/quickViewPanel.spec.tsx @@ -17,14 +17,18 @@ import constants from '../../../../common/constants'; describe('panel/templatePanel/quickViewPanel', () => { let store: AppStore; let templateSliceData: TemplateState; - let template1Manifest: Template.Manifest; - let template2Manifest: Template.Manifest; + let template1Manifest: Template.TemplateManifest; + let template2Manifest: Template.TemplateManifest; + let workflow1Manifest: Template.WorkflowManifest; + let workflow2Manifest: Template.WorkflowManifest; let param1DefaultValue: string; const defaultWorkflowId = 'default'; const httpClient = new MockHttpClient(); InitTemplateService( new StandardTemplateService({ + endpoint: '', + useEndpointForTemplates: false, baseUrl: '/baseUrl', appId: '/appId', httpClient, @@ -35,7 +39,7 @@ describe('panel/templatePanel/quickViewPanel', () => { openBladeAfterCreate: (workflowName: string | undefined) => { console.log('Open blade after create', workflowName); }, - onAddBlankWorkflow: () => { + onAddBlankWorkflow: async () => { console.log('Add blank workflow'); }, }) @@ -44,22 +48,41 @@ describe('panel/templatePanel/quickViewPanel', () => { beforeAll(() => { param1DefaultValue = 'default value for param 1'; template1Manifest = { + id: 'template1Manifest', title: 'Template 1', - description: 'Template 1 Description', + summary: 'Template 1 Description', skus: ['standard', 'consumption'], + workflows: { + default: { name: 'default' }, + }, + details: { + By: '', + Type: '', + Category: '', + }, + artifacts: [ + { + type: 'description', + file: 'description.md', + }, + ], + }; + + workflow1Manifest = { + id: 'default', + title: 'Template 1', + summary: 'Template 1 Description', kinds: ['stateful', 'stateless'], - details: {}, - images: {}, artifacts: [ { type: 'workflow', file: 'workflow.json', }, - { - type: 'description', - file: 'description.md', - }, ], + images: { + light: '', + dark: '', + }, connections: {}, parameters: [ { @@ -79,22 +102,41 @@ describe('panel/templatePanel/quickViewPanel', () => { }; template2Manifest = { + id: 'template2Manifest', title: 'Template 2', - description: 'Template 2 Description - Consumption Only', + summary: 'Template 2 Description - Consumption Only', skus: ['consumption'], + workflows: { + default: { name: 'default' }, + }, + details: { + By: '', + Type: '', + Category: '', + }, + artifacts: [ + { + type: 'description', + file: 'description.md', + }, + ], + }; + + workflow2Manifest = { + id: 'default', + title: 'Template 2', + summary: 'Template 2 Description - Consumption Only', kinds: ['stateful', 'stateless'], - details: {}, - images: {}, artifacts: [ { type: 'workflow', file: 'workflow.json', }, - { - type: 'description', - file: 'description.md', - }, ], + images: { + light: '', + dark: '', + }, connections: {}, parameters: [ { @@ -119,7 +161,7 @@ describe('panel/templatePanel/quickViewPanel', () => { id: defaultWorkflowId, workflowName: '', kind: undefined, - manifest: template1Manifest, + manifest: workflow1Manifest, workflowDefinition: { $schema: 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#', contentVersion: '', @@ -133,15 +175,14 @@ describe('panel/templatePanel/quickViewPanel', () => { }, templateName: template1Manifest.title, manifest: template1Manifest, - parameterDefinitions: template1Manifest.parameters?.reduce((result: Record, parameter) => { + parameterDefinitions: workflow1Manifest.parameters?.reduce((result: Record, parameter) => { result[parameter.name] = { ...parameter, value: parameter.default, }; return result; }, {}), - connections: template1Manifest.connections, - servicesInitialized: false, + connections: workflow1Manifest.connections, errors: { parameters: {}, connections: undefined, @@ -176,7 +217,7 @@ describe('panel/templatePanel/quickViewPanel', () => { expect(store.getState().template.manifest).toBe(template1Manifest); expect(store.getState().template.parameterDefinitions).toBeDefined(); expect(store.getState().template.errors.parameters).toEqual({}); - expect(store.getState().template.connections).toBe(template1Manifest.connections); + expect(store.getState().template.connections).toBe(workflow1Manifest.connections); }); it('Ensures the quickView panel is open with header', async () => { diff --git a/libs/designer/src/lib/ui/templates/__tests__/displayConnections.spec.tsx b/libs/designer/src/lib/ui/templates/__tests__/displayConnections.spec.tsx index b3850f34a5a..20c9dcded13 100644 --- a/libs/designer/src/lib/ui/templates/__tests__/displayConnections.spec.tsx +++ b/libs/designer/src/lib/ui/templates/__tests__/displayConnections.spec.tsx @@ -13,7 +13,8 @@ import React from 'react'; describe('ui/templates/workflowconnections', () => { let store: AppStore; let templateSliceData: TemplateState; - let template1Manifest: Template.Manifest; + let template1Manifest: Template.TemplateManifest; + let workflow1Manifest: Template.WorkflowManifest; let param1DefaultValue: string; let param2DefaultValue: string; @@ -21,23 +22,57 @@ describe('ui/templates/workflowconnections', () => { param1DefaultValue = 'default value for param 1'; param2DefaultValue = 'boolean'; template1Manifest = { + id: 'template1Manifest', title: 'Template 1', - description: 'Template 1 Description', + summary: 'Template 1 Description', skus: ['standard', 'consumption'], + workflows: { + default: { name: 'default' }, + }, + details: { + By: '', + Type: '', + Category: '', + }, + artifacts: [ + { + type: 'description', + file: 'description.md', + }, + ], + }; + + workflow1Manifest = { + id: 'default', + title: 'Template 1', + summary: 'Template 1 Description', kinds: ['stateful', 'stateless'], - details: {}, - tags: [], - images: {}, artifacts: [ { type: 'workflow', file: 'workflow.json', }, ], - connections: { - conn1: { connectorId: '/serviceProviders/abc', kind: 'inapp' }, + images: { + light: '', + dark: '', }, - parameters: [], + connections: {}, + parameters: [ + { + name: 'param1', + displayName: 'Param 1', + type: 'string', + description: 'param1 description', + default: param1DefaultValue, + }, + { + name: 'param2', + displayName: 'Param 2', + type: 'object', + description: 'param2 description', + }, + ], }; templateSliceData = { @@ -46,12 +81,12 @@ describe('ui/templates/workflowconnections', () => { id: 'default', workflowName: undefined, kind: undefined, - manifest: template1Manifest, + manifest: workflow1Manifest, workflowDefinition: { $schema: 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#', contentVersion: '', }, - connectionKeys: Object.keys(template1Manifest.connections), + connectionKeys: Object.keys(workflow1Manifest.connections), errors: { workflow: undefined, kind: undefined, @@ -61,8 +96,7 @@ describe('ui/templates/workflowconnections', () => { manifest: template1Manifest, templateName: template1Manifest.title, parameterDefinitions: {}, - connections: template1Manifest.connections, - servicesInitialized: false, + connections: workflow1Manifest.connections, errors: { parameters: {}, connections: undefined, @@ -81,14 +115,14 @@ describe('ui/templates/workflowconnections', () => { beforeEach(() => { renderWithProviders( - + , { store } ); }); it('should render the connection ids for connections', async () => { - const conn = template1Manifest?.connections['conn1']; + const conn = workflow1Manifest?.connections['conn1']; expect(screen.getByText('Name')).toBeDefined(); }); }); From 60acdfa408e538515a2c38e12a3980134178322a Mon Sep 17 00:00:00 2001 From: Elaina Lee Date: Wed, 5 Mar 2025 17:01:59 -0800 Subject: [PATCH 13/24] fixed template tests --- .../__tests__/displayParameters.spec.tsx | 70 ++++++++++++---- .../templates/__tests__/templateCard.spec.tsx | 79 ++++++++++--------- 2 files changed, 96 insertions(+), 53 deletions(-) diff --git a/libs/designer/src/lib/ui/templates/__tests__/displayParameters.spec.tsx b/libs/designer/src/lib/ui/templates/__tests__/displayParameters.spec.tsx index a6c835790e5..f3d04545f8f 100644 --- a/libs/designer/src/lib/ui/templates/__tests__/displayParameters.spec.tsx +++ b/libs/designer/src/lib/ui/templates/__tests__/displayParameters.spec.tsx @@ -13,7 +13,8 @@ import { ReactQueryProvider } from '../../../core/ReactQueryProvider'; describe('ui/templates/DisplayParameters', () => { let store: AppStore; let templateSliceData: TemplateState; - let template1Manifest: Template.Manifest; + let template1Manifest: Template.TemplateManifest; + let workflow1Manifest: Template.WorkflowManifest; let param1DefaultValue: string; let param2DefaultValue: string; const manifestService = { @@ -24,24 +25,43 @@ describe('ui/templates/DisplayParameters', () => { beforeAll(() => { param1DefaultValue = 'default value for param 1'; param2DefaultValue = 'boolean'; + param1DefaultValue = 'default value for param 1'; template1Manifest = { + id: 'template1Manifest', title: 'Template 1', - description: 'Template 1 Description', - tags: [], - details: {}, - images: {}, + summary: 'Template 1 Description', skus: ['standard', 'consumption'], + workflows: { + default: { name: 'default' }, + }, + details: { + By: '', + Type: '', + Category: '', + }, + artifacts: [ + { + type: 'description', + file: 'description.md', + }, + ], + }; + + workflow1Manifest = { + id: 'default', + title: 'Template 1', + summary: 'Template 1 Description', kinds: ['stateful', 'stateless'], artifacts: [ { type: 'workflow', file: 'workflow.json', }, - { - type: 'description', - file: 'description.md', - }, ], + images: { + light: '', + dark: '', + }, connections: {}, parameters: [ { @@ -70,16 +90,32 @@ describe('ui/templates/DisplayParameters', () => { templateSliceData = { templateName: template1Manifest.title, - workflows: {}, + workflows: { + default: { + id: 'default', + workflowName: '', + kind: undefined, + manifest: workflow1Manifest, + workflowDefinition: { + $schema: 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#', + contentVersion: '', + }, + errors: { + workflow: undefined, + kind: undefined, + }, + connectionKeys: [], + }, + }, manifest: template1Manifest, - parameterDefinitions: template1Manifest.parameters?.reduce((result: Record, parameter) => { + parameterDefinitions: workflow1Manifest.parameters?.reduce((result: Record, parameter) => { result[parameter.name] = { ...parameter, value: parameter.default, }; return result; }, {}), - connections: template1Manifest.connections, + connections: workflow1Manifest.connections, errors: { parameters: {}, connections: undefined, @@ -98,7 +134,7 @@ describe('ui/templates/DisplayParameters', () => { { store } ); - const parameter1 = template1Manifest?.parameters[0]; + const parameter1 = workflow1Manifest?.parameters[0]; expect(screen.getByText(parameter1.displayName)).toBeDefined(); expect(screen.getByText(parameter1.type)).toBeDefined(); expect(screen.getAllByDisplayValue(param1DefaultValue)).toBeDefined(); @@ -113,7 +149,7 @@ describe('ui/templates/DisplayParameters', () => { { store } ); - const parameter2 = template1Manifest?.parameters[1]; + const parameter2 = workflow1Manifest?.parameters[1]; expect(screen.getByText(parameter2.displayName)).toBeDefined(); expect(screen.getByText(parameter2.type)).toBeDefined(); @@ -140,7 +176,7 @@ describe('ui/templates/DisplayParameters', () => { { store } ); - const parameter3 = template1Manifest?.parameters[2]; + const parameter3 = workflow1Manifest?.parameters[2]; expect(screen.getByText(parameter3.displayName)).toBeDefined(); expect(screen.getByText(parameter3.type)).toBeDefined(); @@ -201,7 +237,7 @@ describe('ui/templates/DisplayParameters', () => { { store } ); - const parameter1 = template1Manifest?.parameters[0]; + const parameter1 = workflow1Manifest?.parameters[0]; expect(screen.getByText(parameter1.displayName)).toBeDefined(); expect(screen.getByText(parameter1.type)).toBeDefined(); expect(screen.getByRole('combobox')).toBeDefined(); @@ -267,7 +303,7 @@ describe('ui/templates/DisplayParameters', () => { { store } ); - const parameter1 = template1Manifest?.parameters[0]; + const parameter1 = workflow1Manifest?.parameters[0]; expect(screen.getByText(parameter1.displayName)).toBeDefined(); expect(screen.getByText(parameter1.type)).toBeDefined(); expect(screen.getAllByRole('button').find((button) => button.ariaLabel === 'Open folder')).toBeDefined(); diff --git a/libs/designer/src/lib/ui/templates/__tests__/templateCard.spec.tsx b/libs/designer/src/lib/ui/templates/__tests__/templateCard.spec.tsx index d64101b9360..cc1d85e6b08 100644 --- a/libs/designer/src/lib/ui/templates/__tests__/templateCard.spec.tsx +++ b/libs/designer/src/lib/ui/templates/__tests__/templateCard.spec.tsx @@ -11,63 +11,68 @@ import React from 'react'; describe('ui/templates/templatesDesigner', () => { let store: AppStore; let minimalStoreData: Partial; - let template1Manifest: Template.Manifest; - let template2Manifest: Template.Manifest; - let template3Manifest: Template.Manifest; + let template1Manifest: Template.TemplateManifest; + let template2Manifest: Template.TemplateManifest; + let template3Manifest: Template.TemplateManifest; beforeAll(() => { template1Manifest = { + id: 'template1Manifest', title: 'Template 1', - description: 'Template 1 Description', - skus: ['standard'], - kinds: ['stateful'], - images: {}, - details: {}, + summary: 'Template 1 Description', + skus: ['standard', 'consumption'], + workflows: { + default: { name: 'default' }, + }, + details: { + By: '', + Type: '', + Category: '', + }, artifacts: [ { - type: 'workflow', - file: 'workflow.json', + type: 'description', + file: 'description.md', }, ], - connections: {}, - parameters: [], }; template2Manifest = { + id: 'template2Manifest', title: 'Template 2', - description: 'Template 2 Description', + summary: 'Template 2 Description', skus: ['standard', 'consumption'], - kinds: ['stateful', 'stateless'], - images: {}, - details: {}, + workflows: { + default: { name: 'default' }, + }, + details: { + By: '', + Type: '', + Category: '', + }, artifacts: [ { - type: 'workflow', - file: 'workflow.json', + type: 'description', + file: 'description.md', }, ], - connections: {}, - parameters: [], }; template3Manifest = { - title: 'Template 2', - description: 'Template 2 Description', + id: 'template3Manifest', + title: 'Template 3', + summary: 'Template 3 Description', skus: ['standard', 'consumption'], - kinds: ['stateful', 'stateless'], - images: {}, - details: {}, + workflows: { + default: { name: 'default' }, + }, + details: { + By: '', + Type: '', + Category: '', + }, artifacts: [ { - type: 'workflow', - file: 'workflow.json', - }, - ], - connections: {}, - parameters: [ - { - name: 'param1', - displayName: 'param1', - type: 'object', - description: 'param1 description', + type: 'description', + file: 'description.md', }, ], }; @@ -85,6 +90,7 @@ describe('ui/templates/templatesDesigner', () => { sortKey: 'a-to-z', connectors: undefined, detailFilters: {}, + pageNum: 0, }, }, }; @@ -110,6 +116,7 @@ describe('ui/templates/templatesDesigner', () => { sortKey: 'a-to-z', connectors: undefined, detailFilters: {}, + pageNum: 0, }, }, }; From 261201231e59130e87688b6ec6700bc4b923c385 Mon Sep 17 00:00:00 2001 From: Elaina Lee Date: Thu, 6 Mar 2025 11:22:05 -0800 Subject: [PATCH 14/24] removed download image --- downloadTemplates.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/downloadTemplates.js b/downloadTemplates.js index e445b2a0635..b071067b88e 100644 --- a/downloadTemplates.js +++ b/downloadTemplates.js @@ -53,8 +53,6 @@ const downloadWorkflowManifest = async (path) => { } if (workflowManifest.images.light && workflowManifest.images.dark) { - await downloadFile(`${path}/${workflowManifest.images.light}.png`); - await downloadFile(`${path}/${workflowManifest.images.dark}.png`); workflowManifest.images = { light: `${baseURL}/${path}/${workflowManifest.images.light}.png`, dark: `${baseURL}/${path}/${workflowManifest.images.dark}.png`, From 39e51b88f6852a072461b3bc6383016070e9f2f6 Mon Sep 17 00:00:00 2001 From: Elaina Lee Date: Thu, 6 Mar 2025 11:38:00 -0800 Subject: [PATCH 15/24] combined get manifest calls to one --- .../src/templates/app/LocalTemplates.tsx | 15 ++++---------- .../lib/core/actions/bjsworkflow/templates.ts | 9 +++++---- .../lib/base/template.ts | 20 ++++++------------- .../designer-client-services/lib/template.ts | 3 +-- 4 files changed, 16 insertions(+), 31 deletions(-) diff --git a/apps/Standalone/src/templates/app/LocalTemplates.tsx b/apps/Standalone/src/templates/app/LocalTemplates.tsx index a877a51a236..5c2289806a3 100644 --- a/apps/Standalone/src/templates/app/LocalTemplates.tsx +++ b/apps/Standalone/src/templates/app/LocalTemplates.tsx @@ -284,20 +284,13 @@ class LocalTemplateService extends StandardTemplateService { } }; - public getTemplateManifest = async (templateId: string): Promise => { + public getResourceManifest = async (resourcePath: string): Promise => { + const templateId = resourcePath.split('/')[0]; if (localTemplateManifestPaths.includes(templateId)) { - return loadLocalTemplateFromResourcePath(templateId); + return loadLocalTemplateFromResourcePath(resourcePath); } - return this._options.service.getTemplateManifest(templateId); - }; - - public getWorkflowManifest = async (templateId: string, workflowId: string): Promise => { - if (localTemplateManifestPaths.includes(templateId)) { - return loadLocalTemplateFromResourcePath(`${templateId}/${workflowId}`); - } - - return this._options.service.getWorkflowManifest(templateId, workflowId); + return this._options.service.getResourceManifest(resourcePath); }; public getWorkflowDefinition = async (templateId: string, workflowId: string): Promise => { diff --git a/libs/designer/src/lib/core/actions/bjsworkflow/templates.ts b/libs/designer/src/lib/core/actions/bjsworkflow/templates.ts index 28b287d3ad4..64b4763d641 100644 --- a/libs/designer/src/lib/core/actions/bjsworkflow/templates.ts +++ b/libs/designer/src/lib/core/actions/bjsworkflow/templates.ts @@ -161,9 +161,9 @@ export const reloadTemplates = createAsyncThunk('reloadTemplates', async ({ clea export const loadManifestsFromPaths = async (templateIds: string[]) => { try { const manifestPromises = templateIds.map(async (templateId) => { - return TemplateService().getTemplateManifest(templateId); + return TemplateService().getResourceManifest(templateId); }); - const templateManifestsArray = await Promise.all(manifestPromises); + const templateManifestsArray = (await Promise.all(manifestPromises)) as Template.TemplateManifest[]; return templateManifestsArray.reduce((result: Record, manifestFile: any, index: number) => { result[templateIds[index]] = manifestFile; return result; @@ -231,7 +231,8 @@ const loadTemplateFromResourcePath = async ( preloadedTemplateManifest: Template.TemplateManifest | undefined, viewTemplateData?: Template.ViewTemplateDetails ): Promise => { - const templateManifest = preloadedTemplateManifest ?? (await TemplateService().getTemplateManifest(templateId)); + const templateManifest = + preloadedTemplateManifest ?? ((await TemplateService().getResourceManifest(templateId)) as Template.TemplateManifest); const workflows = templateManifest.workflows; const isMultiWorkflow = isMultiWorkflowTemplate(templateManifest); @@ -365,7 +366,7 @@ const loadWorkflowTemplate = async ( }; const getWorkflowAndManifest = async (templateId: string, workflowId: string) => { - const workflowManifest = await TemplateService().getWorkflowManifest(templateId, workflowId); + const workflowManifest = (await TemplateService().getResourceManifest(`${templateId}/${workflowId}`)) as Template.WorkflowManifest; const templateWorkflowDefinition = await TemplateService().getWorkflowDefinition(templateId, workflowId); return { workflowManifest, templateWorkflowDefinition }; diff --git a/libs/logic-apps-shared/src/designer-client-services/lib/base/template.ts b/libs/logic-apps-shared/src/designer-client-services/lib/base/template.ts index 7671f9a73b5..d55344f59ec 100644 --- a/libs/logic-apps-shared/src/designer-client-services/lib/base/template.ts +++ b/libs/logic-apps-shared/src/designer-client-services/lib/base/template.ts @@ -34,28 +34,20 @@ export class BaseTemplateService implements ITemplateService { : ((await import('./../templates/manifest.json'))?.default as string[]); }; - public getTemplateManifest = async (templateId: string): Promise => { + public getResourceManifest = async (resourcePath: string): Promise => { const { httpClient, endpoint, useEndpointForTemplates } = this.options; if (useEndpointForTemplates) { return httpClient.get({ - uri: `${endpoint}/templates/${templateId}/manifest.json`, + uri: `${endpoint}/templates/${resourcePath}/manifest.json`, headers: { 'Access-Control-Allow-Origin': '*' }, }); } - return (await import(`./../templates/${templateId}/manifest.json`)).default; - }; - - public getWorkflowManifest = async (templateId: string, workflowId: string): Promise => { - const { httpClient, endpoint, useEndpointForTemplates } = this.options; - if (useEndpointForTemplates) { - return httpClient.get({ - uri: `${endpoint}/templates/${templateId}/${workflowId}/manifest.json`, - headers: { 'Access-Control-Allow-Origin': '*' }, - }); - } + const paths = resourcePath.split('/'); - return (await import(`./../templates/${templateId}/${workflowId}/manifest.json`)).default; + return paths.length === 2 + ? (await import(`./../templates/${paths[0]}/${paths[1]}/manifest.json`)).default + : (await import(`./../templates/${resourcePath}/manifest.json`)).default; }; public getWorkflowDefinition = async (templateId: string, workflowId: string): Promise => { diff --git a/libs/logic-apps-shared/src/designer-client-services/lib/template.ts b/libs/logic-apps-shared/src/designer-client-services/lib/template.ts index d2e63d3980c..efa56e0eeca 100644 --- a/libs/logic-apps-shared/src/designer-client-services/lib/template.ts +++ b/libs/logic-apps-shared/src/designer-client-services/lib/template.ts @@ -6,8 +6,7 @@ export interface ITemplateService { openBladeAfterCreate: (workflowName: string | undefined) => void; onAddBlankWorkflow: () => Promise; getAllTemplateNames: () => Promise; - getTemplateManifest: (templateId: string) => Promise; - getWorkflowManifest: (templateId: string, workflowId: string) => Promise; + getResourceManifest: (templateId: string) => Promise; getWorkflowDefinition: (templateId: string, workflowId: string) => Promise; getContentPathUrl: (templatePath: string, resourcePath: string) => string; } From e98a4da021273e32292aa71f04efba745bbd053f Mon Sep 17 00:00:00 2001 From: Elaina Lee Date: Thu, 6 Mar 2025 11:44:05 -0800 Subject: [PATCH 16/24] fixed filter undefined isuse --- .../ui/templates/filters/templateFilters.tsx | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/libs/designer/src/lib/ui/templates/filters/templateFilters.tsx b/libs/designer/src/lib/ui/templates/filters/templateFilters.tsx index f0ce9b1c7f4..b74e05bbc6c 100644 --- a/libs/designer/src/lib/ui/templates/filters/templateFilters.tsx +++ b/libs/designer/src/lib/ui/templates/filters/templateFilters.tsx @@ -166,18 +166,16 @@ export const TemplateFilters = ({ detailFilters }: TemplateFiltersProps) => { isSearchable /> )} - {(Object.keys(detailFilters) as Template.DetailsType[]).map((filterName, index) => - detailFilters[filterName] ? ( - { - dispatch(setDetailsFilters({ filterName, filters: filterItems })); - }} - /> - ) : null - )} + {Object.entries(detailFilters).map(([filterName, filterItem], index) => ( + { + dispatch(setDetailsFilters({ filterName, filters: filterItems })); + }} + /> + ))}
From d144153448ca5eabb5a57a9422784f20e498b44d Mon Sep 17 00:00:00 2001 From: Elaina Lee Date: Thu, 6 Mar 2025 15:39:23 -0800 Subject: [PATCH 17/24] reflected comments --- .../lib/core/actions/bjsworkflow/templates.ts | 6 +++--- .../src/lib/core/templates/utils/helper.ts | 17 ++++++----------- .../__tests__/createWorkflowPanel.spec.tsx | 12 ------------ .../__tests__/quickViewPanel.spec.tsx | 12 ------------ .../quickViewPanel/tabs/summaryTab.tsx | 4 +++- .../__tests__/displayConnections.spec.tsx | 6 ------ .../__tests__/displayParameters.spec.tsx | 6 ------ .../templates/__tests__/templateCard.spec.tsx | 18 ------------------ .../lib/ui/templates/cards/templateCard.tsx | 8 ++++---- .../ui/templates/connections/connections.tsx | 4 ++-- .../ui/templates/filters/templateFilters.tsx | 2 +- .../designer-client-services/lib/template.ts | 2 +- .../src/utils/src/lib/models/template.ts | 2 +- 13 files changed, 21 insertions(+), 78 deletions(-) diff --git a/libs/designer/src/lib/core/actions/bjsworkflow/templates.ts b/libs/designer/src/lib/core/actions/bjsworkflow/templates.ts index 64b4763d641..aa760f741e1 100644 --- a/libs/designer/src/lib/core/actions/bjsworkflow/templates.ts +++ b/libs/designer/src/lib/core/actions/bjsworkflow/templates.ts @@ -83,8 +83,8 @@ export const initializeWorkflowMetadata = createAsyncThunk( } ); -export const isMultiWorkflowTemplate = (manifest: Template.TemplateManifest): boolean => { - return Object.keys(manifest.workflows).length > 1; +export const isMultiWorkflowTemplate = (manifest: Template.TemplateManifest | undefined): boolean => { + return Object.keys(manifest?.workflows ?? {}).length > 1; }; export const initializeTemplateServices = createAsyncThunk( @@ -247,7 +247,7 @@ const loadTemplateFromResourcePath = async ( }, }; - if (isMultiWorkflow && workflows) { + if (isMultiWorkflow) { for (const workflowId of Object.keys(workflows)) { const workflowData = await loadWorkflowTemplate(templateId, workflowId, viewTemplateData); diff --git a/libs/designer/src/lib/core/templates/utils/helper.ts b/libs/designer/src/lib/core/templates/utils/helper.ts index 1b75619890a..a004d5b392c 100644 --- a/libs/designer/src/lib/core/templates/utils/helper.ts +++ b/libs/designer/src/lib/core/templates/utils/helper.ts @@ -52,22 +52,17 @@ export const getUniqueConnectors = ( connections: Record, subscriptionId: string, location: string -): Template.Connection[] => { - const allConnections = Object.values(connections).map((connection) => ({ ...connection, id: connection.connectorId })); - return getUniqueConnectorsFromConnections(allConnections, subscriptionId, location); +): Template.FeaturedConnector[] => { + const allConnectors = Object.values(connections).map((connector) => ({ ...connector, id: connector.connectorId })); + return getUniqueConnectorsFromConnections(allConnectors, subscriptionId, location); }; -interface UniqueConnectorInfo { - connectorId: string; - operationId: string | undefined; -} - export const getUniqueConnectorsFromConnections = ( originalAllConnectors: Template.FeaturedConnector[], subscriptionId: string, location: string -): UniqueConnectorInfo[] => { - const result: UniqueConnectorInfo[] = []; +): Template.FeaturedConnector[] => { + const result: Template.FeaturedConnector[] = []; const finalConnectorIds: string[] = []; const allConnectors = [...originalAllConnectors]; @@ -76,7 +71,7 @@ export const getUniqueConnectorsFromConnections = ( const normalizedConnectorId = normalizeConnectorId(connection.id, subscriptionId, location).toLowerCase(); if (!finalConnectorIds.includes(normalizedConnectorId)) { finalConnectorIds.push(normalizedConnectorId); - result.push({ ...connection, connectorId: normalizedConnectorId, operationId: undefined }); + result.push({ ...connection, id: normalizedConnectorId }); } } diff --git a/libs/designer/src/lib/ui/panel/templatePanel/__tests__/createWorkflowPanel.spec.tsx b/libs/designer/src/lib/ui/panel/templatePanel/__tests__/createWorkflowPanel.spec.tsx index c52f0bdbb8e..b77c0c87e90 100644 --- a/libs/designer/src/lib/ui/panel/templatePanel/__tests__/createWorkflowPanel.spec.tsx +++ b/libs/designer/src/lib/ui/panel/templatePanel/__tests__/createWorkflowPanel.spec.tsx @@ -60,12 +60,6 @@ describe('panel/templatePanel/createWorkflowPanel', () => { Type: '', Category: '', }, - artifacts: [ - { - type: 'description', - file: 'description.md', - }, - ], }; workflow1Manifest = { @@ -114,12 +108,6 @@ describe('panel/templatePanel/createWorkflowPanel', () => { Type: '', Category: '', }, - artifacts: [ - { - type: 'description', - file: 'description.md', - }, - ], }; workflow2Manifest = { diff --git a/libs/designer/src/lib/ui/panel/templatePanel/__tests__/quickViewPanel.spec.tsx b/libs/designer/src/lib/ui/panel/templatePanel/__tests__/quickViewPanel.spec.tsx index 7ea69b65695..6fb531003d8 100644 --- a/libs/designer/src/lib/ui/panel/templatePanel/__tests__/quickViewPanel.spec.tsx +++ b/libs/designer/src/lib/ui/panel/templatePanel/__tests__/quickViewPanel.spec.tsx @@ -60,12 +60,6 @@ describe('panel/templatePanel/quickViewPanel', () => { Type: '', Category: '', }, - artifacts: [ - { - type: 'description', - file: 'description.md', - }, - ], }; workflow1Manifest = { @@ -114,12 +108,6 @@ describe('panel/templatePanel/quickViewPanel', () => { Type: '', Category: '', }, - artifacts: [ - { - type: 'description', - file: 'description.md', - }, - ], }; workflow2Manifest = { diff --git a/libs/designer/src/lib/ui/panel/templatePanel/quickViewPanel/tabs/summaryTab.tsx b/libs/designer/src/lib/ui/panel/templatePanel/quickViewPanel/tabs/summaryTab.tsx index 1f69ca15804..810e3c39793 100644 --- a/libs/designer/src/lib/ui/panel/templatePanel/quickViewPanel/tabs/summaryTab.tsx +++ b/libs/designer/src/lib/ui/panel/templatePanel/quickViewPanel/tabs/summaryTab.tsx @@ -10,11 +10,13 @@ import Markdown from 'react-markdown'; import { useTemplateManifest, useWorkflowTemplate } from '../../../../../core/state/templates/templateselectors'; import { ConnectionsList } from '../../../../templates/connections/connections'; import { Open16Regular } from '@fluentui/react-icons'; +import { isMultiWorkflowTemplate } from '../../../../../core/actions/bjsworkflow/templates'; export const SummaryPanel = ({ workflowId }: { workflowId: string }) => { const intl = useIntl(); const { manifest: workflowManifest } = useWorkflowTemplate(workflowId); const templateManifest = useTemplateManifest(); + const isMultiWorkflow = isMultiWorkflowTemplate(templateManifest); const templateHasConnections = Object.keys(workflowManifest?.connections || {}).length > 0; const detailsTags: Partial> = { Type: intl.formatMessage({ @@ -104,7 +106,7 @@ export const SummaryPanel = ({ workflowId }: { workflowId: string }) => { ); })}
- {templateManifest.tags?.length ? ( + {!isMultiWorkflow && templateManifest.tags?.length ? (
{intl.formatMessage({ diff --git a/libs/designer/src/lib/ui/templates/__tests__/displayConnections.spec.tsx b/libs/designer/src/lib/ui/templates/__tests__/displayConnections.spec.tsx index 20c9dcded13..7c4758343c1 100644 --- a/libs/designer/src/lib/ui/templates/__tests__/displayConnections.spec.tsx +++ b/libs/designer/src/lib/ui/templates/__tests__/displayConnections.spec.tsx @@ -34,12 +34,6 @@ describe('ui/templates/workflowconnections', () => { Type: '', Category: '', }, - artifacts: [ - { - type: 'description', - file: 'description.md', - }, - ], }; workflow1Manifest = { diff --git a/libs/designer/src/lib/ui/templates/__tests__/displayParameters.spec.tsx b/libs/designer/src/lib/ui/templates/__tests__/displayParameters.spec.tsx index f3d04545f8f..98f86e1d736 100644 --- a/libs/designer/src/lib/ui/templates/__tests__/displayParameters.spec.tsx +++ b/libs/designer/src/lib/ui/templates/__tests__/displayParameters.spec.tsx @@ -39,12 +39,6 @@ describe('ui/templates/DisplayParameters', () => { Type: '', Category: '', }, - artifacts: [ - { - type: 'description', - file: 'description.md', - }, - ], }; workflow1Manifest = { diff --git a/libs/designer/src/lib/ui/templates/__tests__/templateCard.spec.tsx b/libs/designer/src/lib/ui/templates/__tests__/templateCard.spec.tsx index cc1d85e6b08..8ef246b9a98 100644 --- a/libs/designer/src/lib/ui/templates/__tests__/templateCard.spec.tsx +++ b/libs/designer/src/lib/ui/templates/__tests__/templateCard.spec.tsx @@ -29,12 +29,6 @@ describe('ui/templates/templatesDesigner', () => { Type: '', Category: '', }, - artifacts: [ - { - type: 'description', - file: 'description.md', - }, - ], }; template2Manifest = { id: 'template2Manifest', @@ -49,12 +43,6 @@ describe('ui/templates/templatesDesigner', () => { Type: '', Category: '', }, - artifacts: [ - { - type: 'description', - file: 'description.md', - }, - ], }; template3Manifest = { id: 'template3Manifest', @@ -69,12 +57,6 @@ describe('ui/templates/templatesDesigner', () => { Type: '', Category: '', }, - artifacts: [ - { - type: 'description', - file: 'description.md', - }, - ], }; }); diff --git a/libs/designer/src/lib/ui/templates/cards/templateCard.tsx b/libs/designer/src/lib/ui/templates/cards/templateCard.tsx index cd436f55057..98978b3be31 100644 --- a/libs/designer/src/lib/ui/templates/cards/templateCard.tsx +++ b/libs/designer/src/lib/ui/templates/cards/templateCard.tsx @@ -96,7 +96,7 @@ export const TemplateCard = ({ templateName }: TemplateCardProps) => { ); const onRenderMenuIcon = () =>
{`+${overflowList.length}`}
; const menuProps: IContextualMenuProps = { - items: overflowList.map((info) => ({ key: info.connectorId, text: info.connectorId, data: info, onRender: onRenderMenuItem })), + items: overflowList.map((info) => ({ key: info.id, text: info.id, data: info, onRender: onRenderMenuItem })), directionalHintFixed: true, className: 'msla-template-card-connector-menu-box', }; @@ -140,9 +140,9 @@ export const TemplateCard = ({ templateName }: TemplateCardProps) => { {connectorsToShow.length > 0 ? ( connectorsToShow.map((info) => ( )) diff --git a/libs/designer/src/lib/ui/templates/connections/connections.tsx b/libs/designer/src/lib/ui/templates/connections/connections.tsx index 006852db2e3..b40ea716441 100644 --- a/libs/designer/src/lib/ui/templates/connections/connections.tsx +++ b/libs/designer/src/lib/ui/templates/connections/connections.tsx @@ -9,14 +9,14 @@ export const ConnectionsList = (props: { connections: Record state.workflow); const connectors = getUniqueConnectors(props.connections, subscriptionId, location); - const onRenderCell = (item: Template.Connection | undefined): JSX.Element => { + const onRenderCell = (item: Template.FeaturedConnector | undefined): JSX.Element => { if (!item) { return
No data
; } return (
- +
); }; diff --git a/libs/designer/src/lib/ui/templates/filters/templateFilters.tsx b/libs/designer/src/lib/ui/templates/filters/templateFilters.tsx index b74e05bbc6c..b1a1c23b1b2 100644 --- a/libs/designer/src/lib/ui/templates/filters/templateFilters.tsx +++ b/libs/designer/src/lib/ui/templates/filters/templateFilters.tsx @@ -41,7 +41,7 @@ export const TemplateFilters = ({ detailFilters }: TemplateFiltersProps) => { const allConnectors = Object.values(skuTemplates).flatMap((template) => template.featuredConnectors ?? []); const uniqueConnectorsFromConnections = getUniqueConnectorsFromConnections(allConnectors, subscriptionId, location); - return uniqueConnectorsFromConnections.map((connector) => connector.connectorId); + return uniqueConnectorsFromConnections.map((connector) => connector.id); }, [availableTemplates, isConsumption, location, subscriptionId]); const selectedTabId = appliedDetailFilters?.Type?.[0]?.value ?? templateDefaultTabKey; diff --git a/libs/logic-apps-shared/src/designer-client-services/lib/template.ts b/libs/logic-apps-shared/src/designer-client-services/lib/template.ts index efa56e0eeca..df1dd3964ad 100644 --- a/libs/logic-apps-shared/src/designer-client-services/lib/template.ts +++ b/libs/logic-apps-shared/src/designer-client-services/lib/template.ts @@ -6,7 +6,7 @@ export interface ITemplateService { openBladeAfterCreate: (workflowName: string | undefined) => void; onAddBlankWorkflow: () => Promise; getAllTemplateNames: () => Promise; - getResourceManifest: (templateId: string) => Promise; + getResourceManifest: (resourcePath: string) => Promise; getWorkflowDefinition: (templateId: string, workflowId: string) => Promise; getContentPathUrl: (templatePath: string, resourcePath: string) => string; } diff --git a/libs/logic-apps-shared/src/utils/src/lib/models/template.ts b/libs/logic-apps-shared/src/utils/src/lib/models/template.ts index eb797806764..f7f17bf9739 100644 --- a/libs/logic-apps-shared/src/utils/src/lib/models/template.ts +++ b/libs/logic-apps-shared/src/utils/src/lib/models/template.ts @@ -1,6 +1,6 @@ export type SkuType = 'standard' | 'consumption'; export type WorkflowKindType = 'stateful' | 'stateless'; -export type ConnectorRuntimeType = 'inapp' | 'shared' | 'custom'; +export type ConnectorRuntimeType = 'inapp' | 'shared' | 'custom' | 'builtin'; export type FeaturedConnectorType = ConnectorRuntimeType | 'builtin'; export type DetailsType = 'By' | 'Type' | 'Category' | 'Trigger'; From 1fb18b47e191bcebdf834f6c19134bed1310dba5 Mon Sep 17 00:00:00 2001 From: Elaina Lee Date: Thu, 6 Mar 2025 19:53:26 -0800 Subject: [PATCH 18/24] removed custom connection & brought back empty trigger row --- Localize/lang/strings.json | 2 + .../lib/core/actions/bjsworkflow/templates.ts | 82 ++++++++----------- .../src/lib/ui/templates/templateoverview.tsx | 23 +++++- .../src/utils/src/lib/models/template.ts | 2 +- 4 files changed, 59 insertions(+), 50 deletions(-) diff --git a/Localize/lang/strings.json b/Localize/lang/strings.json index 3abdb5d0105..a21ee22f485 100644 --- a/Localize/lang/strings.json +++ b/Localize/lang/strings.json @@ -669,6 +669,7 @@ "MCzWDc": "Preview", "MDbmMw": "Required. The collections to evaluate. An object must be in all collections passed in to appear in the result.", "MGZRu4": "Add an action", + "MGq28G": "Trigger", "MIX4f9": "Unsupported programming language.", "MKTdNk": "Required. The data URI to convert to binary representation.", "MLCQzX": "Managed identity", @@ -1743,6 +1744,7 @@ "_MCzWDc.comment": "Recurrence preview title", "_MDbmMw.comment": "Required collection parameters to check intersection function on", "_MGZRu4.comment": "Chatbot prompt to add action", + "_MGq28G.comment": "Column name for trigger type", "_MIX4f9.comment": "The exception for an unsupported programming language.", "_MKTdNk.comment": "Required dataURI string parameter to be converted using dataUriToBinary function", "_MLCQzX.comment": "Managed Identity Label Display Name", diff --git a/libs/designer/src/lib/core/actions/bjsworkflow/templates.ts b/libs/designer/src/lib/core/actions/bjsworkflow/templates.ts index aa760f741e1..3df5defb54a 100644 --- a/libs/designer/src/lib/core/actions/bjsworkflow/templates.ts +++ b/libs/designer/src/lib/core/actions/bjsworkflow/templates.ts @@ -247,56 +247,42 @@ const loadTemplateFromResourcePath = async ( }, }; - if (isMultiWorkflow) { - for (const workflowId of Object.keys(workflows)) { - const workflowData = await loadWorkflowTemplate(templateId, workflowId, viewTemplateData); - - if (workflowData) { - workflowData.workflow.workflowName = workflows[workflowId].name; - data.workflows[workflowId] = workflowData.workflow; - data.parameterDefinitions = { - ...data.parameterDefinitions, - ...Object.keys(workflowData.parameterDefinitions).reduce((acc: Record, key: string) => { - if (data.parameterDefinitions[key] && workflowData.parameterDefinitions[key]) { - // Combine associatedWorkflows arrays if both definitions exist - const combinedAssociatedWorkflows = [ - ...(data.parameterDefinitions[key].associatedWorkflows || []), - ...(workflowData.parameterDefinitions[key].associatedWorkflows || []), - ]; - - acc[key] = { - ...data.parameterDefinitions[key], - ...workflowData.parameterDefinitions[key], - associatedWorkflows: combinedAssociatedWorkflows, - }; - } else { - // If the key doesn't exist in data, just take from workflowData - acc[key] = workflowData.parameterDefinitions[key]; - } - return acc; - }, {}), - }; - data.connections = { ...data.connections, ...workflowData.connections }; - } - } - } else { - const workflowId = 'default'; + for (const workflowId of Object.keys(workflows)) { const workflowData = await loadWorkflowTemplate(templateId, workflowId, viewTemplateData); - + console.log('---workflowData ', workflowData); if (workflowData) { - data.workflows = { - [workflowId]: { - ...workflowData.workflow, - manifest: { - ...workflowData.workflow.manifest, - // Override title and summary with template manifest data if single workflow - title: templateManifest.title, - summary: templateManifest.summary, - }, - }, - }; - data.parameterDefinitions = workflowData.parameterDefinitions; - data.connections = workflowData.connections; + workflowData.workflow.workflowName = workflows[workflowId].name; + data.workflows[workflowId] = workflowData.workflow; + // Override title and summary with template manifest data if single workflow + if (!isMultiWorkflow) { + data.workflows[workflowId].manifest.title = templateManifest.title; + data.workflows[workflowId].manifest.summary = templateManifest.summary; + } + data.parameterDefinitions = isMultiWorkflow + ? { + ...data.parameterDefinitions, + ...Object.keys(workflowData.parameterDefinitions).reduce((acc: Record, key: string) => { + if (data.parameterDefinitions[key] && workflowData.parameterDefinitions[key]) { + // Combine associatedWorkflows arrays if both definitions exist + const combinedAssociatedWorkflows = [ + ...(data.parameterDefinitions[key].associatedWorkflows || []), + ...(workflowData.parameterDefinitions[key].associatedWorkflows || []), + ]; + + acc[key] = { + ...data.parameterDefinitions[key], + ...workflowData.parameterDefinitions[key], + associatedWorkflows: combinedAssociatedWorkflows, + }; + } else { + // If the key doesn't exist in data, just take from workflowData + acc[key] = workflowData.parameterDefinitions[key]; + } + return acc; + }, {}), + } + : workflowData.parameterDefinitions; + data.connections = { ...data.connections, ...workflowData.connections }; } } diff --git a/libs/designer/src/lib/ui/templates/templateoverview.tsx b/libs/designer/src/lib/ui/templates/templateoverview.tsx index baf28a1a16a..86d41498934 100644 --- a/libs/designer/src/lib/ui/templates/templateoverview.tsx +++ b/libs/designer/src/lib/ui/templates/templateoverview.tsx @@ -157,6 +157,7 @@ export const TemplateOverview = ({ interface WorkflowItem { id: string; name: string; + trigger: string; } const WorkflowList = ({ @@ -169,11 +170,12 @@ const WorkflowList = ({ unmap(workflows).map((workflow) => { const { id, manifest } = workflow; const { title } = manifest as Template.WorkflowManifest; - return { id, name: title }; + return { id, name: title, trigger: '' }; }) ); const columnsNames = { name: intl.formatMessage({ defaultMessage: 'Name', id: '+EREVh', description: 'Column name for workflow name' }), + trigger: intl.formatMessage({ defaultMessage: 'Trigger', id: 'MGq28G', description: 'Column name for trigger type' }), }; const _onColumnClick = (_event: React.MouseEvent, column: IColumn): void => { let isSortedDescending = column.isSortedDescending; @@ -210,6 +212,18 @@ const WorkflowList = ({ showSortIconWhenUnsorted: true, onColumnClick: _onColumnClick, }, + { + ariaLabel: columnsNames.trigger, + fieldName: 'trigger', + flexGrow: 1, + key: 'trigger', + isResizable: true, + minWidth: 1, + maxWidth: 100, + name: columnsNames.trigger, + showSortIconWhenUnsorted: true, + onColumnClick: _onColumnClick, + }, ]); const onRenderItemColumn = (item: WorkflowItem, _index: number | undefined, column: IColumn | undefined) => { @@ -221,6 +235,13 @@ const WorkflowList = ({ ); + case 'trigger': + return ( + + {item.trigger} + + ); + default: return null; } diff --git a/libs/logic-apps-shared/src/utils/src/lib/models/template.ts b/libs/logic-apps-shared/src/utils/src/lib/models/template.ts index f7f17bf9739..888bab3bc4f 100644 --- a/libs/logic-apps-shared/src/utils/src/lib/models/template.ts +++ b/libs/logic-apps-shared/src/utils/src/lib/models/template.ts @@ -1,6 +1,6 @@ export type SkuType = 'standard' | 'consumption'; export type WorkflowKindType = 'stateful' | 'stateless'; -export type ConnectorRuntimeType = 'inapp' | 'shared' | 'custom' | 'builtin'; +export type ConnectorRuntimeType = 'inapp' | 'shared' | 'builtin'; export type FeaturedConnectorType = ConnectorRuntimeType | 'builtin'; export type DetailsType = 'By' | 'Type' | 'Category' | 'Trigger'; From ff8cdcdae834ef24536612b9ce9e43d3ef1ab920 Mon Sep 17 00:00:00 2001 From: Elaina Lee Date: Fri, 7 Mar 2025 11:46:10 -0800 Subject: [PATCH 19/24] fixed overwriting for viewTemplate --- .../src/lib/core/actions/bjsworkflow/templates.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/libs/designer/src/lib/core/actions/bjsworkflow/templates.ts b/libs/designer/src/lib/core/actions/bjsworkflow/templates.ts index 3df5defb54a..645d6e7f1b3 100644 --- a/libs/designer/src/lib/core/actions/bjsworkflow/templates.ts +++ b/libs/designer/src/lib/core/actions/bjsworkflow/templates.ts @@ -248,10 +248,8 @@ const loadTemplateFromResourcePath = async ( }; for (const workflowId of Object.keys(workflows)) { - const workflowData = await loadWorkflowTemplate(templateId, workflowId, viewTemplateData); - console.log('---workflowData ', workflowData); + const workflowData = await loadWorkflowTemplate(templateId, workflowId, viewTemplateData, workflows[workflowId].name); if (workflowData) { - workflowData.workflow.workflowName = workflows[workflowId].name; data.workflows[workflowId] = workflowData.workflow; // Override title and summary with template manifest data if single workflow if (!isMultiWorkflow) { @@ -292,7 +290,8 @@ const loadTemplateFromResourcePath = async ( const loadWorkflowTemplate = async ( templateId: string, workflowId: string, - viewTemplateData: Template.ViewTemplateDetails | undefined + viewTemplateData: Template.ViewTemplateDetails | undefined, + defaultNameInManifest: string ): Promise< | { workflow: WorkflowTemplateData; @@ -319,7 +318,7 @@ const loadWorkflowTemplate = async ( id: workflowId, workflowDefinition: templateWorkflowDefinition, manifest: workflowManifest, - workflowName: viewTemplateData?.basicsOverride?.[workflowId]?.name?.value ?? '', + workflowName: viewTemplateData?.basicsOverride?.[workflowId]?.name?.value ?? defaultNameInManifest, kind: overridenKind && workflowManifest.kinds?.includes(overridenKind) ? overridenKind From 6c022cfb87534231ff1fb7cd484fe3c6a7aeed7a Mon Sep 17 00:00:00 2001 From: Elaina Lee Date: Fri, 7 Mar 2025 15:21:46 -0800 Subject: [PATCH 20/24] removed builtin from connectorRuntimeType --- libs/logic-apps-shared/src/utils/src/lib/models/template.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/logic-apps-shared/src/utils/src/lib/models/template.ts b/libs/logic-apps-shared/src/utils/src/lib/models/template.ts index 888bab3bc4f..95d808a4aab 100644 --- a/libs/logic-apps-shared/src/utils/src/lib/models/template.ts +++ b/libs/logic-apps-shared/src/utils/src/lib/models/template.ts @@ -1,6 +1,6 @@ export type SkuType = 'standard' | 'consumption'; export type WorkflowKindType = 'stateful' | 'stateless'; -export type ConnectorRuntimeType = 'inapp' | 'shared' | 'builtin'; +export type ConnectorRuntimeType = 'inapp' | 'shared'; export type FeaturedConnectorType = ConnectorRuntimeType | 'builtin'; export type DetailsType = 'By' | 'Type' | 'Category' | 'Trigger'; From a4645cc677eb75395e6c83c5872507eb73113c8e Mon Sep 17 00:00:00 2001 From: Elaina Lee Date: Fri, 7 Mar 2025 15:38:13 -0800 Subject: [PATCH 21/24] fixed Connection to FeaturedConnector for displaying connector --- .../src/lib/ui/templates/connections/connections.tsx | 2 +- .../src/lib/ui/templates/connections/connector.tsx | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libs/designer/src/lib/ui/templates/connections/connections.tsx b/libs/designer/src/lib/ui/templates/connections/connections.tsx index b40ea716441..9ccf49a3bb5 100644 --- a/libs/designer/src/lib/ui/templates/connections/connections.tsx +++ b/libs/designer/src/lib/ui/templates/connections/connections.tsx @@ -16,7 +16,7 @@ export const ConnectionsList = (props: { connections: Record - +
); }; diff --git a/libs/designer/src/lib/ui/templates/connections/connector.tsx b/libs/designer/src/lib/ui/templates/connections/connector.tsx index c7a04c95b0f..22c86ca0310 100644 --- a/libs/designer/src/lib/ui/templates/connections/connector.tsx +++ b/libs/designer/src/lib/ui/templates/connections/connector.tsx @@ -101,9 +101,9 @@ const textStyles = { }, }; -export const ConnectorWithDetails = ({ connectorId, kind }: Template.Connection) => { - const { data: connector, isLoading, isError } = useConnector(connectorId, /* enabled */ true, /* getCachedData */ true); - const { data: connections, isLoading: isConnectionsLoading } = useConnectionsForConnector(connectorId, /* shouldNotRefetch */ true); +export const ConnectorWithDetails = ({ id, kind }: Template.FeaturedConnector) => { + const { data: connector, isLoading, isError } = useConnector(id, /* enabled */ true, /* getCachedData */ true); + const { data: connections, isLoading: isConnectionsLoading } = useConnectionsForConnector(id, /* shouldNotRefetch */ true); const connectorConnections = useMemo(() => connections?.filter(isConnectionValid), [connections]); const intl = useIntl(); @@ -123,7 +123,7 @@ export const ConnectorWithDetails = ({ connectorId, kind }: Template.Connection) /> ) : ( From 357996091e9d01dec51d1d3a799e1788f87ab380 Mon Sep 17 00:00:00 2001 From: Elaina Lee Date: Fri, 7 Mar 2025 17:05:33 -0800 Subject: [PATCH 22/24] fixed viewTemplate test --- e2e/templates/app.spec.ts | 2 +- e2e/templates/createWorkflowPanel.spec.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/e2e/templates/app.spec.ts b/e2e/templates/app.spec.ts index 4c92de223c1..86b4094e144 100644 --- a/e2e/templates/app.spec.ts +++ b/e2e/templates/app.spec.ts @@ -51,7 +51,7 @@ test.describe( await page.getByText('Azure Business').click(); await page.getByRole('tab', { name: 'Workflow' }).click(); await page.getByRole('tab', { name: 'Summary' }).click(); - await page.getByRole('button', { name: 'Use this template' }).click(); + await page.getByTestId('template-footer-primary-button').click(); await expect(page.getByText('Create a new workflow from template', { exact: true })).toBeVisible(); await page.getByRole('button', { name: 'Close' }).click(); diff --git a/e2e/templates/createWorkflowPanel.spec.ts b/e2e/templates/createWorkflowPanel.spec.ts index b9b453828ea..ba83de805a6 100644 --- a/e2e/templates/createWorkflowPanel.spec.ts +++ b/e2e/templates/createWorkflowPanel.spec.ts @@ -13,9 +13,9 @@ test.describe( await GoToMockTemplate(page, '[Mock] Basic Workflow Only Template'); await page.getByRole('button', { name: 'Use this template' }).click(); await page.getByRole('tab', { name: 'Review + create' }).click(); - await expect(page.getByText('----', { exact: true })).toBeVisible(); + await expect(page.getByText('BasicWorkflowOnly', { exact: true })).toBeVisible(); await expect(page.getByText('Stateful', { exact: true })).toBeVisible(); - expect(await page.getByRole('button', { name: 'create' }).isDisabled()).toBeTruthy(); + expect(await page.getByRole('button', { name: 'create' }).isDisabled()).toBeFalsy(); await page.getByRole('tab', { name: 'Basics' }).click(); await page.locator('[data-testid="msla-templates-workflowName"]').fill(workflowName); From c1d36d8fed9d3bc05ff1149c1f0d3f10cca16230 Mon Sep 17 00:00:00 2001 From: preetriti1 <48265693+preetriti1@users.noreply.github.com> Date: Fri, 7 Mar 2025 19:10:18 -0800 Subject: [PATCH 23/24] Fixing failing test case (#6745) Co-authored-by: Priti Sambandam --- e2e/templates/createWorkflowPanel.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/templates/createWorkflowPanel.spec.ts b/e2e/templates/createWorkflowPanel.spec.ts index ba83de805a6..753c62df6c5 100644 --- a/e2e/templates/createWorkflowPanel.spec.ts +++ b/e2e/templates/createWorkflowPanel.spec.ts @@ -23,7 +23,7 @@ test.describe( await page.getByRole('button', { name: 'Next' }).click(); - await expect(page.getByText('----', { exact: true })).not.toBeVisible(); + await expect(page.getByText('BasicWorkflowOnly', { exact: true })).not.toBeVisible(); await expect(page.getByText('Stateful', { exact: true })).not.toBeVisible(); await expect(page.getByText(workflowName, { exact: true })).toBeVisible(); await expect(page.getByText('Stateless', { exact: true })).toBeVisible(); From b11b9c5eee623cf5167b332bdfb6ae6871baae69 Mon Sep 17 00:00:00 2001 From: Priti Sambandam Date: Fri, 7 Mar 2025 21:06:20 -0800 Subject: [PATCH 24/24] Cloning the manifest data before storing it in store to allow modification --- libs/designer/src/lib/core/actions/bjsworkflow/templates.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libs/designer/src/lib/core/actions/bjsworkflow/templates.ts b/libs/designer/src/lib/core/actions/bjsworkflow/templates.ts index 190e48c86ea..9e3a5ff98fc 100644 --- a/libs/designer/src/lib/core/actions/bjsworkflow/templates.ts +++ b/libs/designer/src/lib/core/actions/bjsworkflow/templates.ts @@ -21,6 +21,7 @@ import { type LogicAppsV2, TemplateService, type Template, + clone, } from '@microsoft/logic-apps-shared'; import { createAsyncThunk } from '@reduxjs/toolkit'; import type { RootState } from '../../state/templates/store'; @@ -307,7 +308,7 @@ const loadTemplateFromResourcePath = async ( const workflows = templateManifest.workflows; const isMultiWorkflow = isMultiWorkflowTemplate(templateManifest); const data: TemplatePayload = { - manifest: templateManifest, + manifest: clone(templateManifest), workflows: {}, parameterDefinitions: {}, connections: {}, @@ -387,7 +388,7 @@ const loadWorkflowTemplate = async ( workflow: { id: workflowId, workflowDefinition: templateWorkflowDefinition, - manifest: workflowManifest, + manifest: clone(workflowManifest), workflowName: viewTemplateData?.basicsOverride?.[workflowId]?.name?.value ?? defaultNameInManifest, kind: overridenKind && workflowManifest.kinds?.includes(overridenKind)