From 2456a287dbff955a0916b9600e89a39511cd537a Mon Sep 17 00:00:00 2001 From: Guilherme Caponetto <638737+caponetto@users.noreply.github.com> Date: Sun, 3 Mar 2024 09:22:43 -0300 Subject: [PATCH] fix(orchestrator): stop fetching workflow URI (#1297) * fix(orchestrator): stop fetching workflow uri * Fix description * Fixes after rebase --- .../mockComposedGreetingWorfklow.ts | 129 ++-- .../__fixtures__/mockGreetingWorkflowData.ts | 129 ++-- .../mockSpringBootWorkflowData.ts | 725 +++++++++--------- .../src/service/DataIndexService.ts | 43 +- .../service/DataInputSchemaService.test.ts | 16 +- .../src/service/DataInputSchemaService.ts | 6 +- .../src/service/SonataFlowService.ts | 95 +-- .../service/api/mapping/V2Mappings.test.ts | 2 +- .../src/service/api/mapping/V2Mappings.ts | 52 +- .../src/service/api/test-utils.ts | 5 +- .../src/service/api/v1.ts | 54 +- .../src/service/api/v2.test.ts | 23 +- .../src/service/api/v2.ts | 26 +- .../src/service/constants.ts | 2 + .../src/service/router.ts | 98 +-- .../src/auto-generated/api/definition.ts | 54 +- .../src/auto-generated/api/models/schema.ts | 42 +- .../auto-generated/docs/index.adoc/index.adoc | 94 ++- plugins/orchestrator-common/src/models.ts | 27 - .../src/openapi/openapi.yaml | 48 +- .../orchestrator-common/src/openapi/types.ts | 1 + plugins/orchestrator-common/src/types.ts | 12 +- .../orchestrator-common/src/workflow.test.ts | 13 + plugins/orchestrator-common/src/workflow.ts | 9 + ...keWorkflowDataInputSchemaDifferentTypes.ts | 121 ++- ...orkflowDataInputSchemaResponseMultiStep.ts | 417 +++++----- ...nputSchemaResponseMultiStepInitialState.ts | 717 +++++++++-------- .../__fixtures__/fakeWorkflowDefinition.ts | 244 ++++++ .../fakeWorkflowInputSchemaResponse.ts | 121 ++- .../src/__fixtures__/fakeWorkflowItem.ts | 247 ------ .../src/__fixtures__/fakeWorkflowOverview.ts | 2 +- .../__fixtures__/fakeWorkflowOverviewList.ts | 18 +- .../src/api/MockOrchestratorClient.ts | 68 +- .../src/api/OrchestratorClient.ts | 25 +- plugins/orchestrator/src/api/api.ts | 18 +- .../ExecuteWorkflowPage.stories.tsx | 4 +- .../ExecuteWorkflowPage.tsx | 2 +- .../components/OrchestratorPage.stories.tsx | 8 +- .../WorkflowDefinitionViewerPage.stories.tsx | 8 +- .../WorkflowEditor/WorkflowEditor.tsx | 51 +- .../WorkflowInstancePage.stories.tsx | 14 +- .../WorkflowRunsTabContent.stories.tsx | 4 +- .../src/components/WorkflowRunsTabContent.tsx | 2 +- .../src/components/WorkflowsTabContent.tsx | 2 +- .../src/components/WorkflowsTable.stories.tsx | 2 +- .../WorkflowOverviewFormatter.test.ts | 3 +- .../WorkflowOverviewFormatter.ts | 11 +- 47 files changed, 1842 insertions(+), 1972 deletions(-) create mode 100644 plugins/orchestrator-common/src/workflow.test.ts create mode 100644 plugins/orchestrator/src/__fixtures__/fakeWorkflowDefinition.ts delete mode 100644 plugins/orchestrator/src/__fixtures__/fakeWorkflowItem.ts diff --git a/plugins/orchestrator-backend/__fixtures__/mockComposedGreetingWorfklow.ts b/plugins/orchestrator-backend/__fixtures__/mockComposedGreetingWorfklow.ts index 9bd39eb899..8d38bdb94b 100644 --- a/plugins/orchestrator-backend/__fixtures__/mockComposedGreetingWorfklow.ts +++ b/plugins/orchestrator-backend/__fixtures__/mockComposedGreetingWorfklow.ts @@ -2,7 +2,7 @@ import { JsonObject } from '@backstage/types'; import { JSONSchema7 } from 'json-schema'; -import { WorkflowItem } from '@janus-idp/backstage-plugin-orchestrator-common'; +import { WorkflowDefinition } from '@janus-idp/backstage-plugin-orchestrator-common'; const schema = { $id: 'classpath:/schemas/yamlgreet__main-schema.json', @@ -38,78 +38,75 @@ const schema = { required: ['name'], } as JSONSchema7; -const workflowItem = { - uri: 'yamlgreet.sw.yaml', - definition: { - id: 'yamlgreet', - version: '1.0', - specVersion: '0.8', - name: 'Greeting workflow', - description: 'YAML based greeting workflow', - dataInputSchema: 'schemas/yamlgreet__main-schema.json', - start: 'ChooseOnLanguage', - functions: [ - { - name: 'greetFunction', - type: 'custom', - operation: 'sysout', - }, - ], - states: [ - { - name: 'ChooseOnLanguage', - type: 'switch', - dataConditions: [ - { - condition: '${ .language.language == "English" }', - transition: 'GreetInEnglish', - }, - { - condition: '${ .language.language == "Spanish" }', - transition: 'GreetInSpanish', - }, - ], - defaultCondition: { +const workflowDefinition = { + id: 'yamlgreet', + version: '1.0', + specVersion: '0.8', + name: 'Greeting workflow', + description: 'YAML based greeting workflow', + dataInputSchema: 'schemas/yamlgreet__main-schema.json', + start: 'ChooseOnLanguage', + functions: [ + { + name: 'greetFunction', + type: 'custom', + operation: 'sysout', + }, + ], + states: [ + { + name: 'ChooseOnLanguage', + type: 'switch', + dataConditions: [ + { + condition: '${ .language.language == "English" }', transition: 'GreetInEnglish', }, - }, - { - name: 'GreetInEnglish', - type: 'inject', - data: { - greeting: 'Hello from YAML Workflow, ', + { + condition: '${ .language.language == "Spanish" }', + transition: 'GreetInSpanish', }, - transition: 'GreetPerson', + ], + defaultCondition: { + transition: 'GreetInEnglish', }, - { - name: 'GreetInSpanish', - type: 'inject', - data: { - greeting: 'Saludos desde YAML Workflow, ', - }, - transition: 'GreetPerson', + }, + { + name: 'GreetInEnglish', + type: 'inject', + data: { + greeting: 'Hello from YAML Workflow, ', + }, + transition: 'GreetPerson', + }, + { + name: 'GreetInSpanish', + type: 'inject', + data: { + greeting: 'Saludos desde YAML Workflow, ', }, - { - name: 'GreetPerson', - type: 'operation', - actions: [ - { - name: 'greetAction', - functionRef: { - refName: 'greetFunction', - arguments: { - message: '.greeting+.name.name', - }, + transition: 'GreetPerson', + }, + { + name: 'GreetPerson', + type: 'operation', + actions: [ + { + name: 'greetAction', + functionRef: { + refName: 'greetFunction', + arguments: { + message: '.greeting+.name.name', }, }, - ], - end: { - terminate: true, }, + ], + end: { + terminate: true, }, - ], - }, -} as WorkflowItem; + }, + ], +} as WorkflowDefinition; const variables = { workflowdata: { @@ -125,8 +122,8 @@ const variables = { const mockData: { schema: JSONSchema7; - workflowItem: WorkflowItem; + workflowDefinition: WorkflowDefinition; variables: JsonObject; -} = { schema, workflowItem, variables }; +} = { schema, workflowDefinition, variables }; export default mockData; diff --git a/plugins/orchestrator-backend/__fixtures__/mockGreetingWorkflowData.ts b/plugins/orchestrator-backend/__fixtures__/mockGreetingWorkflowData.ts index a4ff9c2ae5..69c3bda603 100644 --- a/plugins/orchestrator-backend/__fixtures__/mockGreetingWorkflowData.ts +++ b/plugins/orchestrator-backend/__fixtures__/mockGreetingWorkflowData.ts @@ -2,7 +2,7 @@ import { JsonObject } from '@backstage/types'; import { JSONSchema7 } from 'json-schema'; -import { WorkflowItem } from '@janus-idp/backstage-plugin-orchestrator-common'; +import { WorkflowDefinition } from '@janus-idp/backstage-plugin-orchestrator-common'; const schema = { $id: 'classpath:/schemas/yamlgreet__main-schema.json', @@ -27,78 +27,75 @@ const schema = { required: ['name'], } as JSONSchema7; -const workflowItem = { - uri: 'yamlgreet.sw.yaml', - definition: { - id: 'yamlgreet', - version: '1.0', - specVersion: '0.8', - name: 'Greeting workflow', - description: 'YAML based greeting workflow', - dataInputSchema: 'schemas/yamlgreet__main-schema.json', - start: 'ChooseOnLanguage', - functions: [ - { - name: 'greetFunction', - type: 'custom', - operation: 'sysout', - }, - ], - states: [ - { - name: 'ChooseOnLanguage', - type: 'switch', - dataConditions: [ - { - condition: '${ .language == "English" }', - transition: 'GreetInEnglish', - }, - { - condition: '${ .language == "Spanish" }', - transition: 'GreetInSpanish', - }, - ], - defaultCondition: { +const workflowDefinition = { + id: 'yamlgreet', + version: '1.0', + specVersion: '0.8', + name: 'Greeting workflow', + description: 'YAML based greeting workflow', + dataInputSchema: 'schemas/yamlgreet__main-schema.json', + start: 'ChooseOnLanguage', + functions: [ + { + name: 'greetFunction', + type: 'custom', + operation: 'sysout', + }, + ], + states: [ + { + name: 'ChooseOnLanguage', + type: 'switch', + dataConditions: [ + { + condition: '${ .language == "English" }', transition: 'GreetInEnglish', }, - }, - { - name: 'GreetInEnglish', - type: 'inject', - data: { - greeting: 'Hello from YAML Workflow, ', + { + condition: '${ .language == "Spanish" }', + transition: 'GreetInSpanish', }, - transition: 'GreetPerson', + ], + defaultCondition: { + transition: 'GreetInEnglish', }, - { - name: 'GreetInSpanish', - type: 'inject', - data: { - greeting: 'Saludos desde YAML Workflow, ', - }, - transition: 'GreetPerson', + }, + { + name: 'GreetInEnglish', + type: 'inject', + data: { + greeting: 'Hello from YAML Workflow, ', + }, + transition: 'GreetPerson', + }, + { + name: 'GreetInSpanish', + type: 'inject', + data: { + greeting: 'Saludos desde YAML Workflow, ', }, - { - name: 'GreetPerson', - type: 'operation', - actions: [ - { - name: 'greetAction', - functionRef: { - refName: 'greetFunction', - arguments: { - message: '.greeting+.name', - }, + transition: 'GreetPerson', + }, + { + name: 'GreetPerson', + type: 'operation', + actions: [ + { + name: 'greetAction', + functionRef: { + refName: 'greetFunction', + arguments: { + message: '.greeting+.name', }, }, - ], - end: { - terminate: true, }, + ], + end: { + terminate: true, }, - ], - }, -} as WorkflowItem; + }, + ], +} as WorkflowDefinition; const variables = { workflowdata: { @@ -110,8 +107,8 @@ const variables = { const mockData: { schema: JSONSchema7; - workflowItem: WorkflowItem; + workflowDefinition: WorkflowDefinition; variables: JsonObject; -} = { schema, workflowItem, variables }; +} = { schema, workflowDefinition, variables }; export default mockData; diff --git a/plugins/orchestrator-backend/__fixtures__/mockSpringBootWorkflowData.ts b/plugins/orchestrator-backend/__fixtures__/mockSpringBootWorkflowData.ts index 033581c8f1..0e80c8fdf8 100644 --- a/plugins/orchestrator-backend/__fixtures__/mockSpringBootWorkflowData.ts +++ b/plugins/orchestrator-backend/__fixtures__/mockSpringBootWorkflowData.ts @@ -1,6 +1,6 @@ import { JSONSchema7 } from 'json-schema'; -import { WorkflowItem } from '@janus-idp/backstage-plugin-orchestrator-common'; +import { WorkflowDefinition } from '@janus-idp/backstage-plugin-orchestrator-common'; const schema = { $id: 'classpath:/schemas/spring-boot-backend__main-schema.json', @@ -174,410 +174,407 @@ const schema = { }, } as JSONSchema7; -const workflowItem = { - uri: 'spring-boot-backend.sw.yaml', - definition: { - id: 'spring-boot-backend', - version: '1.0', - specVersion: '0.8', - name: 'Spring Boot Backend application', - description: - 'Create a starter Spring Boot backend application with a CI pipeline', - dataInputSchema: 'schemas/spring-boot-backend__main-schema.json', - functions: [ - { - name: 'runActionFetchTemplate', - operation: 'specs/actions-openapi.json#fetch:template', - }, - { - name: 'runActionPublishGithub', - operation: 'specs/actions-openapi.json#publish:github', - }, - { - name: 'runActionCatalogRegister', - operation: 'specs/actions-openapi.json#catalog:register', - }, - { - name: 'fs:delete', - operation: 'specs/actions-openapi.json#fs:delete', - }, - { - name: 'sysout', - type: 'custom', - operation: 'sysout', - }, - ], - errors: [ - { - name: 'Error on Action', - code: 'java.lang.RuntimeException', - }, - ], - start: 'Generating the Source Code Component', - states: [ - { - name: 'Generating the Source Code Component', - type: 'operation', - actionMode: 'sequential', - actions: [ - { - name: 'Fetch Template Action - Source Code', - functionRef: { - refName: 'runActionFetchTemplate', - arguments: { - url: 'https://github.com/janus-idp/software-templates/tree/main/templates/github/spring-boot-backend/skeleton', - values: { - orgName: '.newComponent.orgName', - repoName: '.newComponent.repoName', - owner: '.newComponent.owner', - system: '.newComponent.system', - applicationType: 'api', - description: '.newComponent.description', - namespace: '.ciMethod.namespace', - port: '.newComponent.port', - ci: '.ciMethod.ci', - sourceControl: 'github.com', - groupId: '.javaMetadata.groupId', - artifactId: '.javaMetadata.artifactId', - javaPackageName: '.javaMetadata.javaPackageName', - version: '.javaMetadata.version', - }, +const workflowDefinition = { + id: 'spring-boot-backend', + version: '1.0', + specVersion: '0.8', + name: 'Spring Boot Backend application', + description: + 'Create a starter Spring Boot backend application with a CI pipeline', + dataInputSchema: 'schemas/spring-boot-backend__main-schema.json', + functions: [ + { + name: 'runActionFetchTemplate', + operation: 'specs/actions-openapi.json#fetch:template', + }, + { + name: 'runActionPublishGithub', + operation: 'specs/actions-openapi.json#publish:github', + }, + { + name: 'runActionCatalogRegister', + operation: 'specs/actions-openapi.json#catalog:register', + }, + { + name: 'fs:delete', + operation: 'specs/actions-openapi.json#fs:delete', + }, + { + name: 'sysout', + type: 'custom', + operation: 'sysout', + }, + ], + errors: [ + { + name: 'Error on Action', + code: 'java.lang.RuntimeException', + }, + ], + start: 'Generating the Source Code Component', + states: [ + { + name: 'Generating the Source Code Component', + type: 'operation', + actionMode: 'sequential', + actions: [ + { + name: 'Fetch Template Action - Source Code', + functionRef: { + refName: 'runActionFetchTemplate', + arguments: { + url: 'https://github.com/janus-idp/software-templates/tree/main/templates/github/spring-boot-backend/skeleton', + values: { + orgName: '.newComponent.orgName', + repoName: '.newComponent.repoName', + owner: '.newComponent.owner', + system: '.newComponent.system', + applicationType: 'api', + description: '.newComponent.description', + namespace: '.ciMethod.namespace', + port: '.newComponent.port', + ci: '.ciMethod.ci', + sourceControl: 'github.com', + groupId: '.javaMetadata.groupId', + artifactId: '.javaMetadata.artifactId', + javaPackageName: '.javaMetadata.javaPackageName', + version: '.javaMetadata.version', }, }, - actionDataFilter: { - toStateData: '.actionFetchTemplateSourceCodeResult', - }, }, - ], - onErrors: [ - { - errorRef: 'Error on Action', - transition: 'Handle Error', + actionDataFilter: { + toStateData: '.actionFetchTemplateSourceCodeResult', }, - ], - compensatedBy: 'Clear File System - Source Code', - transition: 'Generating the CI Component', - }, - { - name: 'Generating the CI Component', - type: 'switch', - dataConditions: [ - { - condition: '${ .ciMethod.ci == "github" }', - transition: 'Generating the CI Component - GitHub', - }, - { - condition: '${ .ciMethod.ci == "tekton" }', - transition: 'Generating the CI Component - Tekton', - }, - ], - defaultCondition: { + }, + ], + onErrors: [ + { + errorRef: 'Error on Action', + transition: 'Handle Error', + }, + ], + compensatedBy: 'Clear File System - Source Code', + transition: 'Generating the CI Component', + }, + { + name: 'Generating the CI Component', + type: 'switch', + dataConditions: [ + { + condition: '${ .ciMethod.ci == "github" }', transition: 'Generating the CI Component - GitHub', }, + { + condition: '${ .ciMethod.ci == "tekton" }', + transition: 'Generating the CI Component - Tekton', + }, + ], + defaultCondition: { + transition: 'Generating the CI Component - GitHub', }, - { - name: 'Generating the CI Component - GitHub', - type: 'operation', - actionMode: 'sequential', - actions: [ - { - name: 'Run Template Fetch Action - CI - GitHub', - functionRef: { - refName: 'runActionFetchTemplate', - arguments: { - url: 'https://github.com/janus-idp/software-templates/tree/main/skeletons/github-actions', - copyWithoutTemplating: ['".github/workflows/"'], - values: { - orgName: '.newComponent.orgName', - repoName: '.newComponent.repoName', - owner: '.newComponent.owner', - system: '.newComponent.system', - applicationType: 'api', - description: '.newComponent.description', - namespace: '.ciMethod.namespace', - port: '.newComponent.port', - ci: '.ciMethod.ci', - sourceControl: 'github.com', - groupId: '.javaMetadata.groupId', - artifactId: '.javaMetadata.artifactId', - javaPackageName: '.javaMetadata.javaPackageName', - version: '.javaMetadata.version', - }, + }, + { + name: 'Generating the CI Component - GitHub', + type: 'operation', + actionMode: 'sequential', + actions: [ + { + name: 'Run Template Fetch Action - CI - GitHub', + functionRef: { + refName: 'runActionFetchTemplate', + arguments: { + url: 'https://github.com/janus-idp/software-templates/tree/main/skeletons/github-actions', + copyWithoutTemplating: ['".github/workflows/"'], + values: { + orgName: '.newComponent.orgName', + repoName: '.newComponent.repoName', + owner: '.newComponent.owner', + system: '.newComponent.system', + applicationType: 'api', + description: '.newComponent.description', + namespace: '.ciMethod.namespace', + port: '.newComponent.port', + ci: '.ciMethod.ci', + sourceControl: 'github.com', + groupId: '.javaMetadata.groupId', + artifactId: '.javaMetadata.artifactId', + javaPackageName: '.javaMetadata.javaPackageName', + version: '.javaMetadata.version', }, }, - actionDataFilter: { - toStateData: '.actionTemplateFetchCIResult', - }, }, - ], - onErrors: [ - { - errorRef: 'Error on Action', - transition: 'Handle Error', + actionDataFilter: { + toStateData: '.actionTemplateFetchCIResult', }, - ], - compensatedBy: 'Clear File System - CI', - transition: 'Generating the Catalog Info Component', - }, - { - name: 'Generating the CI Component - Tekton', - type: 'operation', - actionMode: 'sequential', - actions: [ - { - name: 'Run Template Fetch Action - CI - Tekton', - functionRef: { - refName: 'runActionFetchTemplate', - arguments: { - url: 'https://github.com/janus-idp/software-templates/tree/main/skeletons/tekton', - copyWithoutTemplating: ['".github/workflows/"'], - values: { - orgName: '.newComponent.orgName', - repoName: '.newComponent.repoName', - owner: '.newComponent.owner', - system: '.newComponent.system', - applicationType: 'api', - description: '.newComponent.description', - namespace: '.ciMethod.namespace', - imageUrl: '.imageUrl', - imageRepository: '.imageRepository', - imageBuilder: 's2i-go', - port: '.newComponent.port', - ci: '.ciMethod.ci', - sourceControl: 'github.com', - groupId: '.javaMetadata.groupId', - artifactId: '.javaMetadata.artifactId', - javaPackageName: '.javaMetadata.javaPackageName', - version: '.javaMetadata.version', - }, + }, + ], + onErrors: [ + { + errorRef: 'Error on Action', + transition: 'Handle Error', + }, + ], + compensatedBy: 'Clear File System - CI', + transition: 'Generating the Catalog Info Component', + }, + { + name: 'Generating the CI Component - Tekton', + type: 'operation', + actionMode: 'sequential', + actions: [ + { + name: 'Run Template Fetch Action - CI - Tekton', + functionRef: { + refName: 'runActionFetchTemplate', + arguments: { + url: 'https://github.com/janus-idp/software-templates/tree/main/skeletons/tekton', + copyWithoutTemplating: ['".github/workflows/"'], + values: { + orgName: '.newComponent.orgName', + repoName: '.newComponent.repoName', + owner: '.newComponent.owner', + system: '.newComponent.system', + applicationType: 'api', + description: '.newComponent.description', + namespace: '.ciMethod.namespace', + imageUrl: '.imageUrl', + imageRepository: '.imageRepository', + imageBuilder: 's2i-go', + port: '.newComponent.port', + ci: '.ciMethod.ci', + sourceControl: 'github.com', + groupId: '.javaMetadata.groupId', + artifactId: '.javaMetadata.artifactId', + javaPackageName: '.javaMetadata.javaPackageName', + version: '.javaMetadata.version', }, }, - actionDataFilter: { - toStateData: '.actionTemplateFetchCIResult', - }, }, - ], - onErrors: [ - { - errorRef: 'Error on Action', - transition: 'Handle Error', + actionDataFilter: { + toStateData: '.actionTemplateFetchCIResult', }, - ], - compensatedBy: 'Clear File System - CI', - transition: 'Generating the Catalog Info Component', - }, - { - name: 'Generating the Catalog Info Component', - type: 'operation', - actions: [ - { - name: 'Fetch Template Action - Catalog Info', - functionRef: { - refName: 'runActionFetchTemplate', - arguments: { - url: 'https://github.com/janus-idp/software-templates/tree/main/skeletons/catalog-info', - values: { - orgName: '.newComponent.orgName', - repoName: '.newComponent.repoName', - owner: '.newComponent.owner', - system: '.newComponent.system', - applicationType: 'api', - description: '.newComponent.description', - namespace: '.ciMethod.namespace', - imageUrl: '.ciMethod.imageUrl', - imageRepository: '.ciMethod.imageRepository', - imageBuilder: 's2i-go', - port: '.newComponent.port', - ci: '.ciMethod.ci', - sourceControl: 'github.com', - groupId: '.javaMetadata.groupId', - artifactId: '.javaMetadata.artifactId', - javaPackageName: '.javaMetadata.javaPackageName', - version: '.javaMetadata.version', - }, + }, + ], + onErrors: [ + { + errorRef: 'Error on Action', + transition: 'Handle Error', + }, + ], + compensatedBy: 'Clear File System - CI', + transition: 'Generating the Catalog Info Component', + }, + { + name: 'Generating the Catalog Info Component', + type: 'operation', + actions: [ + { + name: 'Fetch Template Action - Catalog Info', + functionRef: { + refName: 'runActionFetchTemplate', + arguments: { + url: 'https://github.com/janus-idp/software-templates/tree/main/skeletons/catalog-info', + values: { + orgName: '.newComponent.orgName', + repoName: '.newComponent.repoName', + owner: '.newComponent.owner', + system: '.newComponent.system', + applicationType: 'api', + description: '.newComponent.description', + namespace: '.ciMethod.namespace', + imageUrl: '.ciMethod.imageUrl', + imageRepository: '.ciMethod.imageRepository', + imageBuilder: 's2i-go', + port: '.newComponent.port', + ci: '.ciMethod.ci', + sourceControl: 'github.com', + groupId: '.javaMetadata.groupId', + artifactId: '.javaMetadata.artifactId', + javaPackageName: '.javaMetadata.javaPackageName', + version: '.javaMetadata.version', }, }, - actionDataFilter: { - toStateData: '.actionFetchTemplateCatalogInfoResult', - }, }, - ], - onErrors: [ - { - errorRef: 'Error on Action', - transition: 'Handle Error', + actionDataFilter: { + toStateData: '.actionFetchTemplateCatalogInfoResult', }, - ], - compensatedBy: 'Clear File System - Catalog', - transition: 'Publishing to the Source Code Repository', - }, - { - name: 'Publishing to the Source Code Repository', - type: 'operation', - actionMode: 'sequential', - actions: [ - { - name: 'Publish Github', - functionRef: { - refName: 'runActionPublishGithub', - arguments: { - allowedHosts: ['"github.com"'], - description: 'Workflow Action', - repoUrl: - '"github.com?owner=" + .newComponent.orgName + "&repo=" + .newComponent.repoName', - defaultBranch: 'main', - gitCommitMessage: 'Initial commit', - allowAutoMerge: true, - allowRebaseMerge: true, - }, - }, - actionDataFilter: { - toStateData: '.actionPublishResult', + }, + ], + onErrors: [ + { + errorRef: 'Error on Action', + transition: 'Handle Error', + }, + ], + compensatedBy: 'Clear File System - Catalog', + transition: 'Publishing to the Source Code Repository', + }, + { + name: 'Publishing to the Source Code Repository', + type: 'operation', + actionMode: 'sequential', + actions: [ + { + name: 'Publish Github', + functionRef: { + refName: 'runActionPublishGithub', + arguments: { + allowedHosts: ['"github.com"'], + description: 'Workflow Action', + repoUrl: + '"github.com?owner=" + .newComponent.orgName + "&repo=" + .newComponent.repoName', + defaultBranch: 'main', + gitCommitMessage: 'Initial commit', + allowAutoMerge: true, + allowRebaseMerge: true, }, }, - ], - onErrors: [ - { - errorRef: 'Error on Action', - transition: 'Handle Error', + actionDataFilter: { + toStateData: '.actionPublishResult', }, - ], - compensatedBy: 'Remove Source Code Repository', - transition: 'Registering the Catalog Info Component', - }, - { - name: 'Registering the Catalog Info Component', - type: 'operation', - actionMode: 'sequential', - actions: [ - { - name: 'Catalog Register Action', - functionRef: { - refName: 'runActionCatalogRegister', - arguments: { - repoContentsUrl: '.actionPublishResult.repoContentsUrl', - catalogInfoPath: '"/catalog-info.yaml"', - }, - }, - actionDataFilter: { - toStateData: '.actionCatalogRegisterResult', + }, + ], + onErrors: [ + { + errorRef: 'Error on Action', + transition: 'Handle Error', + }, + ], + compensatedBy: 'Remove Source Code Repository', + transition: 'Registering the Catalog Info Component', + }, + { + name: 'Registering the Catalog Info Component', + type: 'operation', + actionMode: 'sequential', + actions: [ + { + name: 'Catalog Register Action', + functionRef: { + refName: 'runActionCatalogRegister', + arguments: { + repoContentsUrl: '.actionPublishResult.repoContentsUrl', + catalogInfoPath: '"/catalog-info.yaml"', }, }, - ], - onErrors: [ - { - errorRef: 'Error on Action', - transition: 'Handle Error', + actionDataFilter: { + toStateData: '.actionCatalogRegisterResult', }, - ], - compensatedBy: 'Remove Catalog Info Component', - end: true, - }, - { - name: 'Handle Error', - type: 'operation', - actions: [ - { - name: 'Error Action', - functionRef: { - refName: 'sysout', - arguments: { - message: 'Error on workflow, triggering compensations', - }, + }, + ], + onErrors: [ + { + errorRef: 'Error on Action', + transition: 'Handle Error', + }, + ], + compensatedBy: 'Remove Catalog Info Component', + end: true, + }, + { + name: 'Handle Error', + type: 'operation', + actions: [ + { + name: 'Error Action', + functionRef: { + refName: 'sysout', + arguments: { + message: 'Error on workflow, triggering compensations', }, }, - ], - end: { - compensate: true, }, + ], + end: { + compensate: true, }, - { - name: 'Clear File System - Source Code', - type: 'operation', - usedForCompensation: true, - actions: [ - { - name: 'Clear FS Action', - functionRef: { - refName: 'fs:delete', - arguments: { - files: ['./'], - }, + }, + { + name: 'Clear File System - Source Code', + type: 'operation', + usedForCompensation: true, + actions: [ + { + name: 'Clear FS Action', + functionRef: { + refName: 'fs:delete', + arguments: { + files: ['./'], }, }, - ], - }, - { - name: 'Clear File System - CI', - type: 'operation', - usedForCompensation: true, - actions: [ - { - name: 'Clear FS Action', - functionRef: { - refName: 'fs:delete', - arguments: { - files: ['./'], - }, + }, + ], + }, + { + name: 'Clear File System - CI', + type: 'operation', + usedForCompensation: true, + actions: [ + { + name: 'Clear FS Action', + functionRef: { + refName: 'fs:delete', + arguments: { + files: ['./'], }, }, - ], - }, - { - name: 'Clear File System - Catalog', - type: 'operation', - usedForCompensation: true, - actions: [ - { - name: 'Clear FS Action', - functionRef: { - refName: 'fs:delete', - arguments: { - files: ['./'], - }, + }, + ], + }, + { + name: 'Clear File System - Catalog', + type: 'operation', + usedForCompensation: true, + actions: [ + { + name: 'Clear FS Action', + functionRef: { + refName: 'fs:delete', + arguments: { + files: ['./'], }, }, - ], - }, - { - name: 'Remove Source Code Repository', - type: 'operation', - usedForCompensation: true, - actions: [ - { - name: 'Remove Source Code Repository', - functionRef: { - refName: 'sysout', - arguments: { - message: 'Remove Source Code Repository', - }, + }, + ], + }, + { + name: 'Remove Source Code Repository', + type: 'operation', + usedForCompensation: true, + actions: [ + { + name: 'Remove Source Code Repository', + functionRef: { + refName: 'sysout', + arguments: { + message: 'Remove Source Code Repository', }, }, - ], - }, - { - name: 'Remove Catalog Info Component', - type: 'operation', - usedForCompensation: true, - actions: [ - { - name: 'Remove Catalog Info Component', - functionRef: { - refName: 'sysout', - arguments: { - message: 'Remove Catalog Info Component', - }, + }, + ], + }, + { + name: 'Remove Catalog Info Component', + type: 'operation', + usedForCompensation: true, + actions: [ + { + name: 'Remove Catalog Info Component', + functionRef: { + refName: 'sysout', + arguments: { + message: 'Remove Catalog Info Component', }, }, - ], - }, - ], - }, -} as WorkflowItem; + }, + ], + }, + ], +} as WorkflowDefinition; const mockData: { schema: JSONSchema7; - workflowItem: WorkflowItem; -} = { schema, workflowItem }; + workflowDefinition: WorkflowDefinition; +} = { schema, workflowDefinition }; export default mockData; diff --git a/plugins/orchestrator-backend/src/service/DataIndexService.ts b/plugins/orchestrator-backend/src/service/DataIndexService.ts index ca04a3ee6f..4ba3f61fbb 100644 --- a/plugins/orchestrator-backend/src/service/DataIndexService.ts +++ b/plugins/orchestrator-backend/src/service/DataIndexService.ts @@ -4,7 +4,6 @@ import { Logger } from 'winston'; import { fromWorkflowSource, getWorkflowCategory, - Job, parseWorkflowVariables, ProcessInstance, ProcessInstanceVariables, @@ -90,7 +89,7 @@ export class DataIndexService { return processDefinitions[0]; } - public async getWorkflowDefinitions(): Promise { + public async getWorkflowInfos(): Promise { const QUERY = ` query ProcessDefinitions { ProcessDefinitions { @@ -100,11 +99,12 @@ export class DataIndexService { type endpoint serviceUrl + source } } `; - this.logger.info(`getWorkflowDefinitions() called: ${this.dataIndexUrl}`); + this.logger.info(`getWorkflowInfos() called: ${this.dataIndexUrl}`); const result = await this.client.query(QUERY, {}); this.logger.debug( @@ -148,18 +148,18 @@ export class DataIndexService { } private async getWorkflowDefinitionFromInstance(instance: ProcessInstance) { - const workflowItem = await this.getWorkflowDefinition(instance.processId); - if (!workflowItem?.source) { + const workflowInfo = await this.getWorkflowDefinition(instance.processId); + if (!workflowInfo?.source) { throw new Error( `Workflow defintion is required to fetch instance ${instance.id}`, ); } const workflowDefinitionSrc: WorkflowDefinition = fromWorkflowSource( - workflowItem.source, + workflowInfo.source, ); - if (workflowItem) { + if (workflowInfo) { instance.category = getWorkflowCategory(workflowDefinitionSrc); - instance.description = workflowItem.description; + instance.description = workflowInfo.description; } return instance; } @@ -213,25 +213,6 @@ export class DataIndexService { return result.data.ProcessInstances; } - public async fetchProcessInstanceJobs( - instanceId: string, - ): Promise { - const graphQlQuery = `{ Jobs (where: { processInstanceId: { equal: "${instanceId}" } }) { id, processId, processInstanceId, rootProcessId, status, expirationTime, priority, callbackEndpoint, repeatInterval, repeatLimit, scheduledId, retries, endpoint, nodeInstanceId, executionCounter } }`; - - const result = await this.client.query(graphQlQuery, {}); - - this.logger.debug( - `Fetch process instance jobs result: ${JSON.stringify(result)}`, - ); - - if (result.error) { - this.logger.error(`Error when fetching jobs instances: ${result.error}`); - throw result.error; - } - - return result.data.Jobs; - } - public async fetchProcessInstanceVariables( instanceId: string, ): Promise { @@ -285,16 +266,16 @@ export class DataIndexService { const instance = processInstances[0]; - const workflowItem = await this.getWorkflowDefinition(instance.processId); - if (!workflowItem?.source) { + const workflowInfo = await this.getWorkflowDefinition(instance.processId); + if (!workflowInfo?.source) { throw new Error( `Workflow defintion is required to fetch instance ${instance.id}`, ); } const workflowDefinitionSrc: WorkflowDefinition = fromWorkflowSource( - workflowItem.source, + workflowInfo.source, ); - if (workflowItem) { + if (workflowInfo) { instance.category = getWorkflowCategory(workflowDefinitionSrc); instance.description = workflowDefinitionSrc.description; } diff --git a/plugins/orchestrator-backend/src/service/DataInputSchemaService.test.ts b/plugins/orchestrator-backend/src/service/DataInputSchemaService.test.ts index a6cf501615..e4aff97505 100644 --- a/plugins/orchestrator-backend/src/service/DataInputSchemaService.test.ts +++ b/plugins/orchestrator-backend/src/service/DataInputSchemaService.test.ts @@ -8,7 +8,7 @@ const service = new DataInputSchemaService(); describe('workflow input schema response', () => { it('schema with refs should return multiple steps', () => { const response = service.getWorkflowInputSchemaResponse( - mockSpringBootWorkflowData.workflowItem, + mockSpringBootWorkflowData.workflowDefinition, mockSpringBootWorkflowData.schema, ); expect(response.isComposedSchema).toEqual(true); @@ -26,7 +26,7 @@ describe('workflow input schema response', () => { it('schema with two layers without refs should return a schema parse error', () => { const response = service.getWorkflowInputSchemaResponse( - mockSpringBootWorkflowData.workflowItem, + mockSpringBootWorkflowData.workflowDefinition, { ...mockSpringBootWorkflowData.schema, $defs: undefined }, ); expect(response.isComposedSchema).toEqual(false); @@ -38,7 +38,7 @@ describe('workflow input schema response', () => { it('none composed schema should return isComposedSchema false and one step', () => { const response = service.getWorkflowInputSchemaResponse( - mockGreetingWorkflowData.workflowItem, + mockGreetingWorkflowData.workflowDefinition, mockGreetingWorkflowData.schema, ); expect(response.isComposedSchema).toEqual(false); @@ -48,7 +48,7 @@ describe('workflow input schema response', () => { it('composed schema also wihtout refs should return multiple steps', () => { const response = service.getWorkflowInputSchemaResponse( - mockComposedGreetingWorkflowData.workflowItem, + mockComposedGreetingWorkflowData.workflowDefinition, mockComposedGreetingWorkflowData.schema, ); expect(response.isComposedSchema).toEqual(true); @@ -60,7 +60,7 @@ describe('workflow input schema response', () => { it('a schema without properties should return a schema parse error', () => { const response = service.getWorkflowInputSchemaResponse( - mockComposedGreetingWorkflowData.workflowItem, + mockComposedGreetingWorkflowData.workflowDefinition, { title: 'A' }, ); expect(response.isComposedSchema).toEqual(false); @@ -72,7 +72,7 @@ describe('workflow input schema response', () => { it('using initial variables should return data for each step', () => { const response = service.getWorkflowInputSchemaResponse( - mockGreetingWorkflowData.workflowItem, + mockGreetingWorkflowData.workflowDefinition, mockGreetingWorkflowData.schema, mockGreetingWorkflowData.variables, ); @@ -86,7 +86,7 @@ describe('workflow input schema response', () => { it('using initial variables on composed schema should return data for each step', () => { const response = service.getWorkflowInputSchemaResponse( - mockComposedGreetingWorkflowData.workflowItem, + mockComposedGreetingWorkflowData.workflowDefinition, mockComposedGreetingWorkflowData.schema, mockComposedGreetingWorkflowData.variables, ); @@ -98,7 +98,7 @@ describe('workflow input schema response', () => { it('using initial assessment variables should return read only keys', () => { const response = service.getWorkflowInputSchemaResponse( - mockGreetingWorkflowData.workflowItem, + mockGreetingWorkflowData.workflowDefinition, mockGreetingWorkflowData.schema, undefined, { diff --git a/plugins/orchestrator-backend/src/service/DataInputSchemaService.ts b/plugins/orchestrator-backend/src/service/DataInputSchemaService.ts index 45650a82bf..4bd6a6d510 100644 --- a/plugins/orchestrator-backend/src/service/DataInputSchemaService.ts +++ b/plugins/orchestrator-backend/src/service/DataInputSchemaService.ts @@ -8,9 +8,9 @@ import { isJsonObjectSchema, JsonObjectSchema, ProcessInstanceVariables, + WorkflowDefinition, WorkflowInputSchemaResponse, WorkflowInputSchemaStep, - WorkflowItem, } from '@janus-idp/backstage-plugin-orchestrator-common'; import { WORKFLOW_DATA_KEY } from './constants'; @@ -86,7 +86,7 @@ export class DataInputSchemaService { } public getWorkflowInputSchemaResponse( - workflowItem: WorkflowItem, + definition: WorkflowDefinition, inputSchema: JSONSchema7, instanceVariables?: ProcessInstanceVariables, assessmentInstanceVariables?: ProcessInstanceVariables, @@ -98,7 +98,7 @@ export class DataInputSchemaService { : undefined; const res: WorkflowInputSchemaResponse = { - workflowItem, + definition, isComposedSchema: false, schemaSteps: [], }; diff --git a/plugins/orchestrator-backend/src/service/SonataFlowService.ts b/plugins/orchestrator-backend/src/service/SonataFlowService.ts index d7436e12ad..b5fd062cc0 100644 --- a/plugins/orchestrator-backend/src/service/SonataFlowService.ts +++ b/plugins/orchestrator-backend/src/service/SonataFlowService.ts @@ -1,6 +1,5 @@ import { Config } from '@backstage/config'; -import { OpenAPIV3 } from 'openapi-types'; import { Logger } from 'winston'; import { @@ -8,6 +7,7 @@ import { DEFAULT_SONATAFLOW_CONTAINER_IMAGE, DEFAULT_SONATAFLOW_PERSISTANCE_PATH, DEFAULT_WORKFLOWS_PATH, + extractWorkflowFormat, fromWorkflowSource, getWorkflowCategory, ProcessInstance, @@ -27,10 +27,6 @@ import { executeWithRetry } from './Helper'; const SONATA_FLOW_RESOURCES_PATH = '/home/kogito/serverless-workflow-project/src/main/resources'; -interface SonataFlowSource { - uri: string; -} - interface LauncherCommand { command: string; args: string[]; @@ -77,49 +73,22 @@ export class SonataFlowService { return true; } - const isAlreadyUp = await this.isSonataFlowUp(false, this.devmodeUrl); + const isAlreadyUp = await this.isSonataFlowUp(false, this.devModeUrl); if (isAlreadyUp) { return true; } this.launchSonataFlow(); - return await this.isSonataFlowUp(true, this.devmodeUrl); + return await this.isSonataFlowUp(true, this.devModeUrl); } - public get devmodeUrl(): string { + public get devModeUrl(): string { if (!this.connection.port) { return this.connection.host; } return `${this.connection.host}:${this.connection.port}`; } - public async fetchWorkflowUri( - workflowId: string, - ): Promise { - try { - const definition = await this.dataIndex.getWorkflowDefinition(workflowId); - if (!definition?.serviceUrl) { - return undefined; - } - const urlToFetch = `${definition.serviceUrl}/management/processes/${workflowId}/sources`; - const response = await executeWithRetry(() => fetch(urlToFetch)); - - if (response.ok) { - const json = (await response.json()) as SonataFlowSource[]; - this.logger.debug(`Fetch workflow uri result: ${JSON.stringify(json)}`); - // Assuming only one source in the list - return json.pop()?.uri; - } - const responseStr = JSON.stringify(response); - this.logger.error( - `Response was NOT okay when fetch(${urlToFetch}). Received response: ${responseStr}`, - ); - } catch (error) { - this.logger.error(`Error when fetching workflow uri: ${error}`); - } - return undefined; - } - public async fetchWorkflowInfo( workflowId: string, endpoint: string, @@ -146,37 +115,30 @@ export class SonataFlowService { return undefined; } - public async fetchWorkflowDefinition( + public async fetchWorkflowSource( workflowId: string, - ): Promise { + ): Promise { try { const source = await this.dataIndex.fetchWorkflowSource(workflowId); if (source) { - return fromWorkflowSource(source); + return source; } } catch (error) { - this.logger.error(`Error when fetching workflow definition: ${error}`); + this.logger.error(`Error when fetching workflow source: ${error}`); } return undefined; } - public async fetchOpenApi( - endpoint: string, - ): Promise { + public async fetchWorkflowDefinition( + workflowId: string, + ): Promise { try { - const urlToFetch = `${endpoint}/q/openapi.json`; - const response = await executeWithRetry(() => fetch(urlToFetch)); - if (response.ok) { - const json = await response.json(); - this.logger.debug(`Fetch openapi result: ${JSON.stringify(json)}`); - return json; + const source = await this.fetchWorkflowSource(workflowId); + if (source) { + return fromWorkflowSource(source); } - const responseStr = JSON.stringify(response); - this.logger.error( - `Response was NOT okay when fetch(${urlToFetch}). Received response: ${responseStr}`, - ); } catch (error) { - this.logger.error(`Error when fetching openapi: ${error}`); + this.logger.error(`Error when fetching workflow definition: ${error}`); } return undefined; } @@ -185,14 +147,14 @@ export class SonataFlowService { WorkflowOverview[] | undefined > { try { - const workflowDefinitions = await this.dataIndex.getWorkflowDefinitions(); - if (!workflowDefinitions?.length) { + const workflowInfos = await this.dataIndex.getWorkflowInfos(); + if (!workflowInfos?.length) { return []; } const items = await Promise.all( - workflowDefinitions - .filter(def => def.id) - .map(async (def: WorkflowInfo) => this.fetchWorkflowOverview(def.id)), + workflowInfos + .filter(info => info.source) + .map(info => this.fetchWorkflowOverviewBySource(info.source!)), ); return items.filter((item): item is WorkflowOverview => !!item); } catch (error) { @@ -295,7 +257,7 @@ export class SonataFlowService { launcherArgs.push('-e', `QUARKUS_HTTP_PORT=${this.connection.port}`); launcherArgs.push('-p', `${this.connection.port}:${this.connection.port}`); - launcherArgs.push('-e', `KOGITO_SERVICE_URL=${this.devmodeUrl}`); + launcherArgs.push('-e', `KOGITO_SERVICE_URL=${this.devModeUrl}`); launcherArgs.push( '-v', `${resourcesAbsPath}:${SONATA_FLOW_RESOURCES_PATH}`, @@ -375,11 +337,17 @@ export class SonataFlowService { public async fetchWorkflowOverview( workflowId: string, ): Promise { - const definition = await this.fetchWorkflowDefinition(workflowId); - if (!definition) { - this.logger.debug(`Workflow definition not found: ${workflowId}`); + const source = await this.dataIndex.fetchWorkflowSource(workflowId); + if (!source) { + this.logger.debug(`Workflow source not found: ${workflowId}`); return undefined; } + return await this.fetchWorkflowOverviewBySource(source); + } + + private async fetchWorkflowOverviewBySource( + source: string, + ): Promise { let processInstances: ProcessInstance[] = []; const limit = 10; let offset: number = 0; @@ -388,6 +356,7 @@ export class SonataFlowService { let lastRunStatus: ProcessInstanceStateValues | undefined; let counter = 0; let totalDuration = 0; + const definition = fromWorkflowSource(source); do { processInstances = await this.dataIndex.fetchWorkflowInstances( @@ -417,7 +386,7 @@ export class SonataFlowService { return { workflowId: definition.id, name: definition.name, - uri: await this.fetchWorkflowUri(workflowId), + format: extractWorkflowFormat(source), lastTriggeredMs: lastTriggered.getTime(), lastRunStatus, category: getWorkflowCategory(definition), diff --git a/plugins/orchestrator-backend/src/service/api/mapping/V2Mappings.test.ts b/plugins/orchestrator-backend/src/service/api/mapping/V2Mappings.test.ts index 5baf39902e..e9f8dc6107 100644 --- a/plugins/orchestrator-backend/src/service/api/mapping/V2Mappings.test.ts +++ b/plugins/orchestrator-backend/src/service/api/mapping/V2Mappings.test.ts @@ -19,7 +19,7 @@ describe('scenarios to verify mapToWorkflowOverviewDTO', () => { // Assert expect(result.workflowId).toBe(overview.workflowId); expect(result.name).toBe(overview.name); - expect(result.uri).toBe(overview.uri); + expect(result.format).toBe(overview.format); expect(result.lastTriggeredMs).toBe(overview.lastTriggeredMs); expect(result.lastRunStatus).toBe(overview.lastRunStatus); expect(result.category).toBe('assessment'); diff --git a/plugins/orchestrator-backend/src/service/api/mapping/V2Mappings.ts b/plugins/orchestrator-backend/src/service/api/mapping/V2Mappings.ts index 15903081c6..c98278c46d 100644 --- a/plugins/orchestrator-backend/src/service/api/mapping/V2Mappings.ts +++ b/plugins/orchestrator-backend/src/service/api/mapping/V2Mappings.ts @@ -1,8 +1,10 @@ import moment from 'moment'; import { - ASSESSMENT_WORKFLOW_TYPE, ExecuteWorkflowResponseDTO, + extractWorkflowFormat, + fromWorkflowSource, + getWorkflowCategory, ProcessInstance, ProcessInstanceDTO, ProcessInstanceState, @@ -13,8 +15,7 @@ import { WorkflowDefinition, WorkflowDTO, WorkflowExecutionResponse, - WorkflowListResult, - WorkflowListResultDTO, + WorkflowFormatDTO, WorkflowOverview, WorkflowOverviewDTO, } from '@janus-idp/backstage-plugin-orchestrator-common'; @@ -37,55 +38,24 @@ export function mapWorkflowCategoryDTOFromString( : 'infrastructure'; } -export function mapToWorkflowListResultDTO( - definitions: WorkflowListResult, -): WorkflowListResultDTO { - const result = { - items: definitions.items.map(def => { - return { - annotations: def.definition.annotations, - category: getWorkflowCategoryDTO(def.definition), - description: def.definition.description, - name: def.definition.name, - uri: def.uri, - id: def.definition.id, - }; - }), - paginationInfo: { - limit: definitions.limit, - offset: definitions.offset, - totalCount: definitions.totalCount, - }, - }; - return result; -} - export function getWorkflowCategoryDTO( definition: WorkflowDefinition | undefined, ): WorkflowCategoryDTO { - let result: WorkflowCategoryDTO = 'infrastructure'; - - if ( - definition?.annotations?.find( - annotation => annotation === ASSESSMENT_WORKFLOW_TYPE, - ) - ) { - result = 'assessment'; - } + return getWorkflowCategory(definition); +} - return result; +export function getWorkflowFormatDTO(source: string): WorkflowFormatDTO { + return extractWorkflowFormat(source); } -export function mapToWorkflowDTO( - uri: string, - definition: WorkflowDefinition, -): WorkflowDTO { +export function mapToWorkflowDTO(source: string): WorkflowDTO { + const definition = fromWorkflowSource(source); return { annotations: definition.annotations, category: getWorkflowCategoryDTO(definition), description: definition.description, name: definition.name, - uri: uri, + format: getWorkflowFormatDTO(source), id: definition.id, }; } diff --git a/plugins/orchestrator-backend/src/service/api/test-utils.ts b/plugins/orchestrator-backend/src/service/api/test-utils.ts index 70c5d8c6e5..1a72a3a8f2 100644 --- a/plugins/orchestrator-backend/src/service/api/test-utils.ts +++ b/plugins/orchestrator-backend/src/service/api/test-utils.ts @@ -2,6 +2,7 @@ import { ProcessInstanceState, ProcessInstanceStateValues, WorkflowDefinition, + WorkflowFormat, WorkflowOverview, WorkflowOverviewListResult, } from '@janus-idp/backstage-plugin-orchestrator-common'; @@ -10,7 +11,7 @@ interface WorkflowOverviewParams { suffix?: string; workflowId?: string; name?: string; - uri?: string; + format?: WorkflowFormat; lastTriggeredMs?: number; lastRunStatus?: ProcessInstanceStateValues; category?: string; @@ -23,7 +24,7 @@ export function generateTestWorkflowOverview( return { workflowId: params.workflowId ?? `testWorkflowId${params.suffix}`, name: params.name ?? `Test Workflow${params.suffix}`, - uri: params.uri ?? 'http://example.com', + format: params.format ?? 'yaml', lastTriggeredMs: params.lastTriggeredMs ?? Date.parse('2024-02-09T10:34:56Z'), lastRunStatus: params.lastRunStatus ?? ProcessInstanceState.Completed, diff --git a/plugins/orchestrator-backend/src/service/api/v1.ts b/plugins/orchestrator-backend/src/service/api/v1.ts index 9759932b7f..341f716085 100644 --- a/plugins/orchestrator-backend/src/service/api/v1.ts +++ b/plugins/orchestrator-backend/src/service/api/v1.ts @@ -6,9 +6,6 @@ import { ProcessInstance, WorkflowDefinition, WorkflowExecutionResponse, - WorkflowInfo, - WorkflowItem, - WorkflowListResult, WorkflowOverview, WorkflowOverviewListResult, } from '@janus-idp/backstage-plugin-orchestrator-common'; @@ -50,43 +47,10 @@ export namespace V1 { return overviewObj; } - export async function getWorkflows( - sonataFlowService: SonataFlowService, - dataIndexService: DataIndexService, - ): Promise { - const definitions: WorkflowInfo[] = - await dataIndexService.getWorkflowDefinitions(); - const items: WorkflowItem[] = await Promise.all( - definitions.map(async info => { - const uri = await sonataFlowService.fetchWorkflowUri(info.id); - if (!uri) { - throw new Error(`Uri is required for workflow ${info.id}`); - } - const item: WorkflowItem = { - definition: info as WorkflowDefinition, - serviceUrl: info.serviceUrl, - uri, - }; - return item; - }), - ); - - if (!items) { - throw new Error("Couldn't fetch workflows"); - } - - return { - items: items, - limit: 0, - offset: 0, - totalCount: items?.length ?? 0, - }; - } - export async function getWorkflowById( sonataFlowService: SonataFlowService, workflowId: string, - ): Promise<{ uri: string; definition: WorkflowDefinition }> { + ): Promise { const definition = await sonataFlowService.fetchWorkflowDefinition(workflowId); @@ -94,12 +58,20 @@ export namespace V1 { throw new Error(`Couldn't fetch workflow definition for ${workflowId}`); } - const uri = await sonataFlowService.fetchWorkflowUri(workflowId); - if (!uri) { - throw new Error(`Couldn't fetch workflow uri for ${workflowId}`); + return definition; + } + + export async function getWorkflowSourceById( + sonataFlowService: SonataFlowService, + workflowId: string, + ): Promise { + const source = await sonataFlowService.fetchWorkflowSource(workflowId); + + if (!source) { + throw new Error(`Couldn't fetch workflow source for ${workflowId}`); } - return { uri, definition }; + return source; } export async function getInstances( diff --git a/plugins/orchestrator-backend/src/service/api/v2.test.ts b/plugins/orchestrator-backend/src/service/api/v2.test.ts index 752d2016fe..c28dedca86 100644 --- a/plugins/orchestrator-backend/src/service/api/v2.test.ts +++ b/plugins/orchestrator-backend/src/service/api/v2.test.ts @@ -1,4 +1,5 @@ import { + toWorkflowYaml, WorkflowOverviewDTO, WorkflowOverviewListResultDTO, } from '@janus-idp/backstage-plugin-orchestrator-common'; @@ -32,7 +33,7 @@ const createMockSonataFlowService = (): SonataFlowService => { mockSonataFlowService.fetchWorkflowOverviews = jest.fn(); mockSonataFlowService.fetchWorkflowOverview = jest.fn(); mockSonataFlowService.fetchWorkflowDefinition = jest.fn(); - mockSonataFlowService.fetchWorkflowUri = jest.fn(); + mockSonataFlowService.fetchWorkflowSource = jest.fn(); return mockSonataFlowService; }; @@ -166,7 +167,7 @@ describe('getWorkflowOverviewById', () => { expect(overviewV2).toBeDefined(); expect(overviewV2.workflowId).toBeUndefined(); expect(overviewV2.name).toBeUndefined(); - expect(overviewV2.uri).toBeUndefined(); + expect(overviewV2.format).toBeUndefined(); expect(overviewV2.lastTriggeredMs).toBeUndefined(); expect(overviewV2.lastRunStatus).toBeUndefined(); expect(overviewV2.category).toEqual('infrastructure'); @@ -203,9 +204,9 @@ describe('getWorkflowById', () => { }); it("Workflow doesn't exists", async () => { - ( - mockSonataFlowService.fetchWorkflowDefinition as jest.Mock - ).mockRejectedValue(new Error('No definition')); + (mockSonataFlowService.fetchWorkflowSource as jest.Mock).mockRejectedValue( + new Error('No definition'), + ); // Act const promise = V2.getWorkflowById( mockSonataFlowService, @@ -217,14 +218,12 @@ describe('getWorkflowById', () => { }); it('1 items in workflow list', async () => { - const testUri = 'test-uri.sw.yaml'; + const testFormat = 'yaml'; const wfDefinition = generateWorkflowDefinition; + const source = toWorkflowYaml(wfDefinition); - ( - mockSonataFlowService.fetchWorkflowDefinition as jest.Mock - ).mockResolvedValue(wfDefinition); - (mockSonataFlowService.fetchWorkflowUri as jest.Mock).mockResolvedValue( - testUri, + (mockSonataFlowService.fetchWorkflowSource as jest.Mock).mockResolvedValue( + source, ); // Act const workflowV2 = await V2.getWorkflowById( @@ -237,7 +236,7 @@ describe('getWorkflowById', () => { expect(workflowV2.id).toBeDefined(); expect(workflowV2.id).toEqual(wfDefinition.id); expect(workflowV2.name).toEqual(wfDefinition.name); - expect(workflowV2.uri).toEqual(testUri); + expect(workflowV2.format).toEqual(testFormat); expect(workflowV2.description).toEqual(wfDefinition.description); expect(workflowV2.category).toEqual('infrastructure'); expect(workflowV2.annotations).toBeUndefined(); diff --git a/plugins/orchestrator-backend/src/service/api/v2.ts b/plugins/orchestrator-backend/src/service/api/v2.ts index 29be770082..8aa77a6279 100644 --- a/plugins/orchestrator-backend/src/service/api/v2.ts +++ b/plugins/orchestrator-backend/src/service/api/v2.ts @@ -9,8 +9,6 @@ import { ProcessInstanceState, WorkflowDataDTO, WorkflowDTO, - WorkflowListResult, - WorkflowListResultDTO, WorkflowOverviewDTO, WorkflowOverviewListResultDTO, WorkflowRunStatusDTO, @@ -23,7 +21,6 @@ import { mapToGetWorkflowInstanceResults, mapToProcessInstanceDTO, mapToWorkflowDTO, - mapToWorkflowListResultDTO, mapToWorkflowOverviewDTO, } from './mapping/V2Mappings'; import { V1 } from './v1'; @@ -59,23 +56,26 @@ export namespace V2 { return mapToWorkflowOverviewDTO(overviewV1); } - export async function getWorkflows( + export async function getWorkflowById( sonataFlowService: SonataFlowService, - dataIndexService: DataIndexService, - ): Promise { - const definitions: WorkflowListResult = await V1.getWorkflows( + workflowId: string, + ): Promise { + const resultV1 = await V1.getWorkflowSourceById( sonataFlowService, - dataIndexService, + workflowId, ); - return mapToWorkflowListResultDTO(definitions); + return mapToWorkflowDTO(resultV1); } - export async function getWorkflowById( + export async function getWorkflowSourceById( sonataFlowService: SonataFlowService, workflowId: string, - ): Promise { - const resultV1 = await V1.getWorkflowById(sonataFlowService, workflowId); - return mapToWorkflowDTO(resultV1.uri, resultV1.definition); + ): Promise { + const resultV1 = await V1.getWorkflowSourceById( + sonataFlowService, + workflowId, + ); + return resultV1; } export async function getInstances( diff --git a/plugins/orchestrator-backend/src/service/constants.ts b/plugins/orchestrator-backend/src/service/constants.ts index 3a4d1ef92e..c90564c40d 100644 --- a/plugins/orchestrator-backend/src/service/constants.ts +++ b/plugins/orchestrator-backend/src/service/constants.ts @@ -1 +1,3 @@ export const WORKFLOW_DATA_KEY = 'workflowdata'; + +export const INTERNAL_SERVER_ERROR_MESSAGE = 'internal server error'; diff --git a/plugins/orchestrator-backend/src/service/router.ts b/plugins/orchestrator-backend/src/service/router.ts index 6ba6e78c91..89962b6528 100644 --- a/plugins/orchestrator-backend/src/service/router.ts +++ b/plugins/orchestrator-backend/src/service/router.ts @@ -14,14 +14,13 @@ import { QUERY_PARAM_INCLUDE_ASSESSMENT, QUERY_PARAM_INSTANCE_ID, WorkflowInputSchemaResponse, - WorkflowItem, } from '@janus-idp/backstage-plugin-orchestrator-common'; import { RouterArgs } from '../routerWrapper'; -import { ApiResponseBuilder } from '../types/apiResponse'; import { V1 } from './api/v1'; import { V2 } from './api/v2'; import { CloudEventService } from './CloudEventService'; +import { INTERNAL_SERVER_ERROR_MESSAGE } from './constants'; import { DataIndexService } from './DataIndexService'; import { DataInputSchemaService } from './DataInputSchemaService'; import { JiraEvent, JiraService } from './JiraService'; @@ -145,18 +144,13 @@ function setupInternalRoutes( api: OpenAPIBackend, services: Services, ) { - router.get('/workflows/definitions', async (_, response) => { - const swfs = await services.dataIndexService.getWorkflowDefinitions(); - response.json(ApiResponseBuilder.SUCCESS_RESPONSE(swfs)); - }); - router.get('/workflows/overview', async (_c, res) => { await V1.getWorkflowsOverview(services.sonataFlowService) .then(result => res.status(200).json(result)) .catch(error => { res .status(500) - .json({ message: error.message || 'internal server error' }); + .json({ message: error.message || INTERNAL_SERVER_ERROR_MESSAGE }); }); }); @@ -169,57 +163,58 @@ function setupInternalRoutes( .catch(error => { res .status(500) - .json({ message: error.message || 'internal server error' }); + .json({ message: error.message || INTERNAL_SERVER_ERROR_MESSAGE }); next(); }); }, ); - router.get('/workflows', async (_, res) => { - await V1.getWorkflows(services.sonataFlowService, services.dataIndexService) + router.get('/workflows/:workflowId', async (req, res) => { + const { + params: { workflowId }, + } = req; + await V1.getWorkflowById(services.sonataFlowService, workflowId) .then(result => res.status(200).json(result)) .catch(error => { res .status(500) - .json({ message: error.message || 'internal server error' }); + .json({ message: error.message || INTERNAL_SERVER_ERROR_MESSAGE }); }); }); // v2 - api.register('getWorkflows', async (_c, _req, res, next) => { - await V2.getWorkflows(services.sonataFlowService, services.dataIndexService) + api.register('getWorkflowById', async (c, _req, res, next) => { + const workflowId = c.request.params.workflowId as string; + + await V2.getWorkflowById(services.sonataFlowService, workflowId) .then(result => res.json(result)) .catch(error => { res .status(500) - .json({ message: error.message || 'internal server error' }); + .json({ message: error.message || INTERNAL_SERVER_ERROR_MESSAGE }); next(); }); }); - router.get('/workflows/:workflowId', async (req, res) => { + router.get('/workflows/:workflowId/source', async (req, res) => { const { params: { workflowId }, } = req; - await V1.getWorkflowById(services.sonataFlowService, workflowId) - .then(result => res.status(200).json(result)) + await V1.getWorkflowSourceById(services.sonataFlowService, workflowId) + .then(result => res.status(200).send(result)) .catch(error => { - res - .status(500) - .json({ message: error.message || 'internal server error' }); + res.status(500).send(error.message || INTERNAL_SERVER_ERROR_MESSAGE); }); }); // v2 - api.register('getWorkflowById', async (c, _req, res, next) => { + api.register('getWorkflowSourceById', async (c, _req, res, next) => { const workflowId = c.request.params.workflowId as string; - await V2.getWorkflowById(services.sonataFlowService, workflowId) - .then(result => res.json(result)) + await V2.getWorkflowSourceById(services.sonataFlowService, workflowId) + .then(result => res.send(result)) .catch(error => { - res - .status(500) - .json({ message: error.message || 'internal server error' }); + res.status(500).send(error.message || INTERNAL_SERVER_ERROR_MESSAGE); next(); }); }); @@ -234,7 +229,7 @@ function setupInternalRoutes( .catch(error => { res .status(500) - .json({ message: error.message || 'internal server error' }); + .json({ message: error.message || INTERNAL_SERVER_ERROR_MESSAGE }); }); }); @@ -246,7 +241,7 @@ function setupInternalRoutes( .catch(error => { res .status(500) - .json({ message: error.message || 'internal server error' }); + .json({ message: error.message || INTERNAL_SERVER_ERROR_MESSAGE }); next(); }); }); @@ -269,7 +264,7 @@ function setupInternalRoutes( .catch((error: { message: any }) => { res .status(500) - .json({ message: error.message || 'internal server error' }); + .json({ message: error.message || INTERNAL_SERVER_ERROR_MESSAGE }); }); }); @@ -295,7 +290,7 @@ function setupInternalRoutes( .catch((error: { message: string }) => { res .status(500) - .json({ message: error.message || 'internal server error' }); + .json({ message: error.message || INTERNAL_SERVER_ERROR_MESSAGE }); }); }, ); @@ -322,7 +317,7 @@ function setupInternalRoutes( .catch((error: { message: string }) => { res .status(500) - .json({ message: error.message || 'internal server error' }); + .json({ message: error.message || INTERNAL_SERVER_ERROR_MESSAGE }); }); }, ); @@ -355,7 +350,7 @@ function setupInternalRoutes( .catch(error => { res .status(500) - .json({ message: error.message || 'internal server error' }); + .json({ message: error.message || INTERNAL_SERVER_ERROR_MESSAGE }); }); }); @@ -388,7 +383,7 @@ function setupInternalRoutes( .catch(error => { res .status(500) - .json({ message: error.message || 'internal server error' }); + .json({ message: error.message || INTERNAL_SERVER_ERROR_MESSAGE }); }); }); @@ -410,28 +405,12 @@ function setupInternalRoutes( .catch(error => { res .status(500) - .json({ message: error.message || 'internal server error' }); + .json({ message: error.message || INTERNAL_SERVER_ERROR_MESSAGE }); next(); }); }, ); - router.get('/instances/:instanceId/jobs', async (req, res) => { - const { - params: { instanceId }, - } = req; - - const jobs = - await services.dataIndexService.fetchProcessInstanceJobs(instanceId); - - if (!jobs) { - res.status(500).send(`Couldn't fetch jobs for instance ${instanceId}`); - return; - } - - res.status(200).json(jobs); - }); - router.get('/workflows/:workflowId/inputSchema', async (req, res) => { const { params: { workflowId }, @@ -467,17 +446,8 @@ function setupInternalRoutes( return; } - const uri = await services.sonataFlowService.fetchWorkflowUri(workflowId); - - if (!uri) { - res.status(500).send(`Couldn't fetch workflow uri ${workflowId}`); - return; - } - - const workflowItem: WorkflowItem = { uri, definition }; - const response: WorkflowInputSchemaResponse = { - workflowItem, + definition, schemaSteps: [], isComposedSchema: false, }; @@ -520,7 +490,7 @@ function setupInternalRoutes( .status(200) .json( services.dataInputSchemaService.getWorkflowInputSchemaResponse( - workflowItem, + definition, workflowInfo.inputSchema, instanceVariables, assessmentInstanceVariables, @@ -545,7 +515,7 @@ function setupInternalRoutes( .catch((error: { message: string }) => { res .status(500) - .json({ message: error.message || 'internal server error' }); + .json({ message: error.message || INTERNAL_SERVER_ERROR_MESSAGE }); }); }, ); @@ -559,7 +529,7 @@ function setupInternalRoutes( .catch((error: { message: string }) => { res .status(500) - .json({ message: error.message || 'internal server error' }); + .json({ message: error.message || INTERNAL_SERVER_ERROR_MESSAGE }); }); }, ); diff --git a/plugins/orchestrator-common/src/auto-generated/api/definition.ts b/plugins/orchestrator-common/src/auto-generated/api/definition.ts index 2c562baafd..8b3b4debb3 100644 --- a/plugins/orchestrator-common/src/auto-generated/api/definition.ts +++ b/plugins/orchestrator-common/src/auto-generated/api/definition.ts @@ -84,23 +84,34 @@ const OPENAPI = ` } } }, - "/v2/workflows": { + "/v2/workflows/{workflowId}": { "get": { - "operationId": "getWorkflows", - "description": "Get a list of workflow", + "operationId": "getWorkflowById", + "description": "Get a workflow by ID", + "parameters": [ + { + "name": "workflowId", + "in": "path", + "description": "ID of the workflow to fetch", + "required": true, + "schema": { + "type": "string" + } + } + ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/WorkflowListResultDTO" + "$ref": "#/components/schemas/WorkflowDTO" } } } }, "500": { - "description": "Error fetching workflow list", + "description": "Error fetching workflow by id", "content": { "application/json": { "schema": { @@ -112,15 +123,15 @@ const OPENAPI = ` } } }, - "/v2/workflows/{workflowId}": { + "/v2/workflows/{workflowId}/source": { "get": { - "operationId": "getWorkflowById", - "description": "Get a workflow by ID", + "operationId": "getWorkflowSourceById", + "description": "Get a workflow source by ID", "parameters": [ { "name": "workflowId", "in": "path", - "description": "ID of the workflow to execute", + "description": "ID of the workflow to fetch", "required": true, "schema": { "type": "string" @@ -131,15 +142,15 @@ const OPENAPI = ` "200": { "description": "Success", "content": { - "application/json": { + "text/plain": { "schema": { - "$ref": "#/components/schemas/WorkflowDTO" + "type": "string" } } } }, "500": { - "description": "Error workflow by id", + "description": "Error fetching workflow source by id", "content": { "application/json": { "schema": { @@ -427,8 +438,8 @@ const OPENAPI = ` "description": "Workflow name", "minLength": 1 }, - "uri": { - "type": "string" + "format": { + "$ref": "#/components/schemas/WorkflowFormatDTO" }, "lastTriggeredMs": { "type": "number", @@ -467,6 +478,14 @@ const OPENAPI = ` }, "additionalProperties": false }, + "WorkflowFormatDTO": { + "type": "string", + "description": "Format of the workflow definition", + "enum": [ + "yaml", + "json" + ] + }, "WorkflowCategoryDTO": { "type": "string", "description": "Category of the workflow", @@ -506,9 +525,8 @@ const OPENAPI = ` "description": "Workflow name", "minLength": 1 }, - "uri": { - "type": "string", - "description": "URI of the workflow definition" + "format": { + "$ref": "#/components/schemas/WorkflowFormatDTO" }, "category": { "$ref": "#/components/schemas/WorkflowCategoryDTO" @@ -527,7 +545,7 @@ const OPENAPI = ` "required": [ "id", "category", - "uri" + "format" ] }, "ProcessInstancesDTO": { diff --git a/plugins/orchestrator-common/src/auto-generated/api/models/schema.ts b/plugins/orchestrator-common/src/auto-generated/api/models/schema.ts index 088bd40b23..89864045d3 100644 --- a/plugins/orchestrator-common/src/auto-generated/api/models/schema.ts +++ b/plugins/orchestrator-common/src/auto-generated/api/models/schema.ts @@ -12,14 +12,14 @@ export interface paths { /** @description Get a workflow overview by ID */ get: operations['getWorkflowOverviewById']; }; - '/v2/workflows': { - /** @description Get a list of workflow */ - get: operations['getWorkflows']; - }; '/v2/workflows/{workflowId}': { /** @description Get a workflow by ID */ get: operations['getWorkflowById']; }; + '/v2/workflows/{workflowId}/source': { + /** @description Get a workflow source by ID */ + get: operations['getWorkflowSourceById']; + }; '/v2/workflows/instances': { /** * Get instances @@ -78,7 +78,7 @@ export interface components { workflowId?: string; /** @description Workflow name */ name?: string; - uri?: string; + format?: components['schemas']['WorkflowFormatDTO']; lastTriggeredMs?: number; lastRunStatus?: string; category?: components['schemas']['WorkflowCategoryDTO']; @@ -90,6 +90,11 @@ export interface components { offset?: number; totalCount?: number; }; + /** + * @description Format of the workflow definition + * @enum {string} + */ + WorkflowFormatDTO: 'yaml' | 'json'; /** * @description Category of the workflow * @enum {string} @@ -104,8 +109,7 @@ export interface components { id: string; /** @description Workflow name */ name?: string; - /** @description URI of the workflow definition */ - uri: string; + format: components['schemas']['WorkflowFormatDTO']; category: components['schemas']['WorkflowCategoryDTO']; /** @description Description of the workflow */ description?: string; @@ -248,16 +252,22 @@ export interface operations { }; }; }; - /** @description Get a list of workflow */ - getWorkflows: { + /** @description Get a workflow by ID */ + getWorkflowById: { + parameters: { + path: { + /** @description ID of the workflow to fetch */ + workflowId: string; + }; + }; responses: { /** @description Success */ 200: { content: { - 'application/json': components['schemas']['WorkflowListResultDTO']; + 'application/json': components['schemas']['WorkflowDTO']; }; }; - /** @description Error fetching workflow list */ + /** @description Error fetching workflow by id */ 500: { content: { 'application/json': components['schemas']['ErrorResponse']; @@ -265,11 +275,11 @@ export interface operations { }; }; }; - /** @description Get a workflow by ID */ - getWorkflowById: { + /** @description Get a workflow source by ID */ + getWorkflowSourceById: { parameters: { path: { - /** @description ID of the workflow to execute */ + /** @description ID of the workflow to fetch */ workflowId: string; }; }; @@ -277,10 +287,10 @@ export interface operations { /** @description Success */ 200: { content: { - 'application/json': components['schemas']['WorkflowDTO']; + 'text/plain': string; }; }; - /** @description Error workflow by id */ + /** @description Error fetching workflow source by id */ 500: { content: { 'application/json': components['schemas']['ErrorResponse']; diff --git a/plugins/orchestrator-common/src/auto-generated/docs/index.adoc/index.adoc b/plugins/orchestrator-common/src/auto-generated/docs/index.adoc/index.adoc index f76e52860d..839750ac5c 100644 --- a/plugins/orchestrator-common/src/auto-generated/docs/index.adoc/index.adoc +++ b/plugins/orchestrator-common/src/auto-generated/docs/index.adoc/index.adoc @@ -403,7 +403,7 @@ Get a workflow by ID |Name| Description| Required| Default| Pattern | workflowId -| ID of the workflow to execute +| ID of the workflow to fetch | X | null | @@ -438,7 +438,7 @@ Get a workflow by ID | 500 -| Error workflow by id +| Error fetching workflow by id | <> |=== @@ -639,24 +639,37 @@ ifdef::internal-generation[] endif::internal-generation[] -[.getWorkflowStatuses] -==== getWorkflowStatuses +[.getWorkflowSourceById] +==== getWorkflowSourceById + +`GET /v2/workflows/{workflowId}/source` -`GET /v2/workflows/instances/statuses` -Get workflow status list ===== Description -Retrieve an array of workflow statuses +Get a workflow source by ID -// markup not found, no include::{specDir}v2/workflows/instances/statuses/GET/spec.adoc[opts=optional] +// markup not found, no include::{specDir}v2/workflows/\{workflowId\}/source/GET/spec.adoc[opts=optional] ===== Parameters +====== Path Parameters + +[cols="2,3,1,1,1"] +|=== +|Name| Description| Required| Default| Pattern + +| workflowId +| ID of the workflow to fetch +| X +| null +| + +|=== @@ -665,11 +678,13 @@ Retrieve an array of workflow statuses ===== Return Type -array[<>] + +<> ===== Content Type +* text/plain * application/json ===== Responses @@ -682,11 +697,11 @@ array[<>] | 200 | Success -| List[<>] +| <> | 500 -| Error fetching workflow statuses +| Error fetching workflow source by id | <> |=== @@ -694,38 +709,38 @@ array[<>] ===== Samples -// markup not found, no include::{snippetDir}v2/workflows/instances/statuses/GET/http-request.adoc[opts=optional] +// markup not found, no include::{snippetDir}v2/workflows/\{workflowId\}/source/GET/http-request.adoc[opts=optional] -// markup not found, no include::{snippetDir}v2/workflows/instances/statuses/GET/http-response.adoc[opts=optional] +// markup not found, no include::{snippetDir}v2/workflows/\{workflowId\}/source/GET/http-response.adoc[opts=optional] -// file not found, no * wiremock data link :v2/workflows/instances/statuses/GET/GET.json[] +// file not found, no * wiremock data link :v2/workflows/{workflowId}/source/GET/GET.json[] ifdef::internal-generation[] ===== Implementation -// markup not found, no include::{specDir}v2/workflows/instances/statuses/GET/implementation.adoc[opts=optional] +// markup not found, no include::{specDir}v2/workflows/\{workflowId\}/source/GET/implementation.adoc[opts=optional] endif::internal-generation[] -[.getWorkflows] -==== getWorkflows - -`GET /v2/workflows` +[.getWorkflowStatuses] +==== getWorkflowStatuses +`GET /v2/workflows/instances/statuses` +Get workflow status list ===== Description -Get a list of workflow +Retrieve an array of workflow statuses -// markup not found, no include::{specDir}v2/workflows/GET/spec.adoc[opts=optional] +// markup not found, no include::{specDir}v2/workflows/instances/statuses/GET/spec.adoc[opts=optional] @@ -739,7 +754,7 @@ Get a list of workflow ===== Return Type -<> +array[<>] ===== Content Type @@ -756,11 +771,11 @@ Get a list of workflow | 200 | Success -| <> +| List[<>] | 500 -| Error fetching workflow list +| Error fetching workflow statuses | <> |=== @@ -768,20 +783,20 @@ Get a list of workflow ===== Samples -// markup not found, no include::{snippetDir}v2/workflows/GET/http-request.adoc[opts=optional] +// markup not found, no include::{snippetDir}v2/workflows/instances/statuses/GET/http-request.adoc[opts=optional] -// markup not found, no include::{snippetDir}v2/workflows/GET/http-response.adoc[opts=optional] +// markup not found, no include::{snippetDir}v2/workflows/instances/statuses/GET/http-response.adoc[opts=optional] -// file not found, no * wiremock data link :v2/workflows/GET/GET.json[] +// file not found, no * wiremock data link :v2/workflows/instances/statuses/GET/GET.json[] ifdef::internal-generation[] ===== Implementation -// markup not found, no include::{specDir}v2/workflows/GET/implementation.adoc[opts=optional] +// markup not found, no include::{specDir}v2/workflows/instances/statuses/GET/implementation.adoc[opts=optional] endif::internal-generation[] @@ -1191,10 +1206,10 @@ Category of the workflow | Workflow name | -| uri +| format | X -| String -| URI of the workflow definition +| WorkflowFormatDTO +| | | category @@ -1237,6 +1252,19 @@ Category of the workflow |=== +[#WorkflowFormatDTO] +=== _WorkflowFormatDTO_ + +Format of the workflow definition + +[.fields-WorkflowFormatDTO] +[cols="2,1,2,4,1"] +|=== +| Field Name| Required| Type| Description| Format + +|=== + + [#WorkflowListResultDTO] === _WorkflowListResultDTO_ @@ -1284,9 +1312,9 @@ Category of the workflow | Workflow name | -| uri +| format | -| String +| WorkflowFormatDTO | | diff --git a/plugins/orchestrator-common/src/models.ts b/plugins/orchestrator-common/src/models.ts index 0f25475266..b09cef3409 100644 --- a/plugins/orchestrator-common/src/models.ts +++ b/plugins/orchestrator-common/src/models.ts @@ -83,30 +83,3 @@ export interface ProcessInstance { category?: WorkflowCategory; description?: WorkflowDefinition['description']; } - -export enum JobStatus { - Error = 'ERROR', - Executed = 'EXECUTED', - Scheduled = 'SCHEDULED', - Retry = 'RETRY', - Canceled = 'CANCELED', -} - -export interface Job { - id: string; - processId: string; - processInstanceId: string; - rootProcessInstanceId?: string; - rootProcessId?: string; - status: JobStatus; - expirationTime: Date; - priority: number; - callbackEndpoint: string; - repeatInterval: number; - repeatLimit: number; - scheduledId: string; - retries: number; - executionCounter?: number; - endpoint?: string; - nodeInstanceId?: string; -} diff --git a/plugins/orchestrator-common/src/openapi/openapi.yaml b/plugins/orchestrator-common/src/openapi/openapi.yaml index 32f226f80c..9510ff67fb 100644 --- a/plugins/orchestrator-common/src/openapi/openapi.yaml +++ b/plugins/orchestrator-common/src/openapi/openapi.yaml @@ -50,31 +50,38 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - /v2/workflows: + /v2/workflows/{workflowId}: get: - operationId: getWorkflows - description: Get a list of workflow + operationId: getWorkflowById + description: Get a workflow by ID + parameters: + - name: workflowId + in: path + description: ID of the workflow to fetch + required: true + schema: + type: string responses: '200': description: Success content: application/json: schema: - $ref: '#/components/schemas/WorkflowListResultDTO' + $ref: '#/components/schemas/WorkflowDTO' '500': - description: Error fetching workflow list + description: Error fetching workflow by id content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - /v2/workflows/{workflowId}: + /v2/workflows/{workflowId}/source: get: - operationId: getWorkflowById - description: Get a workflow by ID + operationId: getWorkflowSourceById + description: Get a workflow source by ID parameters: - name: workflowId in: path - description: ID of the workflow to execute + description: ID of the workflow to fetch required: true schema: type: string @@ -82,11 +89,11 @@ paths: '200': description: Success content: - application/json: + text/plain: schema: - $ref: '#/components/schemas/WorkflowDTO' + type: string '500': - description: Error workflow by id + description: Error fetching workflow source by id content: application/json: schema: @@ -273,8 +280,8 @@ components: type: string description: Workflow name minLength: 1 - uri: - type: string + format: + $ref: '#/components/schemas/WorkflowFormatDTO' lastTriggeredMs: type: number minimum: 0 @@ -300,6 +307,12 @@ components: type: number minimum: 0 additionalProperties: false + WorkflowFormatDTO: + type: string + description: Format of the workflow definition + enum: + - yaml + - json WorkflowCategoryDTO: type: string description: Category of the workflow @@ -329,9 +342,8 @@ components: type: string description: Workflow name minLength: 1 - uri: - type: string - description: URI of the workflow definition + format: + $ref: '#/components/schemas/WorkflowFormatDTO' category: $ref: '#/components/schemas/WorkflowCategoryDTO' description: @@ -344,7 +356,7 @@ components: required: - id - category - - uri + - format ProcessInstancesDTO: type: array items: diff --git a/plugins/orchestrator-common/src/openapi/types.ts b/plugins/orchestrator-common/src/openapi/types.ts index 7076ca8fb0..f7ac78e0d6 100644 --- a/plugins/orchestrator-common/src/openapi/types.ts +++ b/plugins/orchestrator-common/src/openapi/types.ts @@ -19,5 +19,6 @@ export type WorkflowDataDTO = components['schemas']['WorkflowDataDTO']; export type ProcessInstanceStatusDTO = components['schemas']['ProcessInstanceStatusDTO']; export type WorkflowCategoryDTO = components['schemas']['WorkflowCategoryDTO']; +export type WorkflowFormatDTO = components['schemas']['WorkflowFormatDTO']; export type WorkflowRunStatusDTO = components['schemas']['WorkflowRunStatusDTO']; diff --git a/plugins/orchestrator-common/src/types.ts b/plugins/orchestrator-common/src/types.ts index c69ac12d9e..d80fb44683 100644 --- a/plugins/orchestrator-common/src/types.ts +++ b/plugins/orchestrator-common/src/types.ts @@ -23,14 +23,8 @@ export type WorkflowDefinition = OmitRecursively< 'normalize' >; -export interface WorkflowItem { - serviceUrl?: string; - uri: string; - definition: WorkflowDefinition; -} - export type WorkflowListResult = { - items: WorkflowItem[]; + items: WorkflowDefinition[]; totalCount: number; offset: number; limit: number; @@ -83,7 +77,7 @@ export const isComposedSchema = ( ).length === 0; export interface WorkflowInputSchemaResponse { - workflowItem: WorkflowItem; + definition: WorkflowDefinition; schemaSteps: WorkflowInputSchemaStep[]; isComposedSchema: boolean; schemaParseError?: string; @@ -100,8 +94,8 @@ export enum WorkflowCategory { export interface WorkflowOverview { workflowId: string; + format: WorkflowFormat; name?: string; - uri?: string; lastTriggeredMs?: number; lastRunStatus?: ProcessInstanceStateValues; category?: string; diff --git a/plugins/orchestrator-common/src/workflow.test.ts b/plugins/orchestrator-common/src/workflow.test.ts new file mode 100644 index 0000000000..6effdf254e --- /dev/null +++ b/plugins/orchestrator-common/src/workflow.test.ts @@ -0,0 +1,13 @@ +import { extractWorkflowFormat } from './workflow'; + +describe('extractWorkflowFormat', () => { + it('should return "json" when input is valid JSON', () => { + const source = '{"name": "workflow", "steps": ["step1", "step2"]}'; + expect(extractWorkflowFormat(source)).toEqual('json'); + }); + + it('should return "yaml" when input is valid YAML', () => { + const source = 'name: workflow\nsteps:\n - step1\n - step2\n'; + expect(extractWorkflowFormat(source)).toEqual('yaml'); + }); +}); diff --git a/plugins/orchestrator-common/src/workflow.ts b/plugins/orchestrator-common/src/workflow.ts index 474ea9ec6c..9c3a32eb47 100644 --- a/plugins/orchestrator-common/src/workflow.ts +++ b/plugins/orchestrator-common/src/workflow.ts @@ -98,3 +98,12 @@ export function parseWorkflowVariables( return variables; } + +export function extractWorkflowFormat(source: string): WorkflowFormat { + try { + JSON.parse(source); + return 'json'; + } catch (_) { + return 'yaml'; + } +} diff --git a/plugins/orchestrator/src/__fixtures__/fakeWorkflowDataInputSchemaDifferentTypes.ts b/plugins/orchestrator/src/__fixtures__/fakeWorkflowDataInputSchemaDifferentTypes.ts index 9f02e70ec4..f2f63abb4d 100644 --- a/plugins/orchestrator/src/__fixtures__/fakeWorkflowDataInputSchemaDifferentTypes.ts +++ b/plugins/orchestrator/src/__fixtures__/fakeWorkflowDataInputSchemaDifferentTypes.ts @@ -1,77 +1,74 @@ import { WorkflowInputSchemaResponse } from '@janus-idp/backstage-plugin-orchestrator-common'; export const fakeDataInputSchemaDifferentTypes: WorkflowInputSchemaResponse = { - workflowItem: { - uri: 'yamlgreet.sw.yaml', - definition: { - id: 'yamlgreet', - version: '1.0', - specVersion: '0.8', - name: 'Greeting workflow', - description: 'YAML based greeting workflow', - dataInputSchema: 'schemas/yamlgreet__main_schema.json', - start: 'ChooseOnLanguage', - functions: [ - { - name: 'greetFunction', - type: 'custom', - operation: 'sysout', - }, - ], - states: [ - { - name: 'ChooseOnLanguage', - type: 'switch', - dataConditions: [ - { - condition: '${ .language == "English" }', - transition: 'GreetInEnglish', - }, - { - condition: '${ .language == "Spanish" }', - transition: 'GreetInSpanish', - }, - ], - defaultCondition: { + definition: { + id: 'yamlgreet', + version: '1.0', + specVersion: '0.8', + name: 'Greeting workflow', + description: 'YAML based greeting workflow', + dataInputSchema: 'schemas/yamlgreet__main_schema.json', + start: 'ChooseOnLanguage', + functions: [ + { + name: 'greetFunction', + type: 'custom', + operation: 'sysout', + }, + ], + states: [ + { + name: 'ChooseOnLanguage', + type: 'switch', + dataConditions: [ + { + condition: '${ .language == "English" }', transition: 'GreetInEnglish', }, - }, - { - name: 'GreetInEnglish', - type: 'inject', - data: { - greeting: 'Hello from YAML Workflow, ', + { + condition: '${ .language == "Spanish" }', + transition: 'GreetInSpanish', }, - transition: 'GreetPerson', + ], + defaultCondition: { + transition: 'GreetInEnglish', }, - { - name: 'GreetInSpanish', - type: 'inject', - data: { - greeting: 'Saludos desde YAML Workflow, ', - }, - transition: 'GreetPerson', + }, + { + name: 'GreetInEnglish', + type: 'inject', + data: { + greeting: 'Hello from YAML Workflow, ', }, - { - name: 'GreetPerson', - type: 'operation', - actions: [ - { - name: 'greetAction', - functionRef: { - refName: 'greetFunction', - arguments: { - message: '.greeting+.name', - }, + transition: 'GreetPerson', + }, + { + name: 'GreetInSpanish', + type: 'inject', + data: { + greeting: 'Saludos desde YAML Workflow, ', + }, + transition: 'GreetPerson', + }, + { + name: 'GreetPerson', + type: 'operation', + actions: [ + { + name: 'greetAction', + functionRef: { + refName: 'greetFunction', + arguments: { + message: '.greeting+.name', }, }, - ], - end: { - terminate: true, }, + ], + end: { + terminate: true, }, - ], - }, + }, + ], }, isComposedSchema: true, schemaSteps: [ diff --git a/plugins/orchestrator/src/__fixtures__/fakeWorkflowDataInputSchemaResponseMultiStep.ts b/plugins/orchestrator/src/__fixtures__/fakeWorkflowDataInputSchemaResponseMultiStep.ts index f4074dd708..91c0db046c 100644 --- a/plugins/orchestrator/src/__fixtures__/fakeWorkflowDataInputSchemaResponseMultiStep.ts +++ b/plugins/orchestrator/src/__fixtures__/fakeWorkflowDataInputSchemaResponseMultiStep.ts @@ -2,239 +2,236 @@ import { WorkflowInputSchemaResponse } from '@janus-idp/backstage-plugin-orchest export const fakeDataInputSchemaMultiStepResponse: WorkflowInputSchemaResponse = { - workflowItem: { - uri: 'ansible-job-template.sw.yaml', - definition: { - id: 'ansible-job-template', - version: '1.0', - specVersion: '0.8', - name: 'Ansible Job Template', - description: - 'Define an Ansible Job Template within Ansible Automation Platform', - dataInputSchema: 'schemas/ansible-job-template__main-schema.json', - functions: [ - { - name: 'runActionFetchTemplate', - operation: 'specs/actions-openapi.json#fetch:template', - }, - { - name: 'runActionGitHubRepoPush', - operation: 'specs/actions-openapi.json#github:repo:push', - }, - { - name: 'runActionCatalogRegister', - operation: 'specs/actions-openapi.json#catalog:register', - }, - { - name: 'fs:delete', - operation: 'specs/actions-openapi.json#fs:delete', - }, - { - name: 'sysout', - type: 'custom', - operation: 'sysout', - }, - ], - errors: [ - { - name: 'Error on Action', - code: 'java.lang.RuntimeException', - }, - ], - start: 'Code and Catalog generation', - states: [ - { - name: 'Code and Catalog generation', - type: 'parallel', - branches: [ - { - name: 'Generating the Ansible Job component', - actions: [ - { - name: 'Run Template Fetch Action', - functionRef: { - refName: 'runActionFetchTemplate', - arguments: { - id: '$WORKFLOW.instanceId', - url: 'https://github.com/janus-idp/software-templates/tree/main/templates/github/launch-ansible-job/skeleton', - values: { - name: '${.ansibleJobDefinition.name}', - jobTemplate: '${.ansibleJobDefinition.jobTemplate}', - component_id: '${.ansibleJobDefinition.name}', - namespace: '${.ansibleJobDefinition.namespace}', - connection_secret: - '${.ansibleJobDefinition.connectionSecret}', - description: '${.ansibleJobDefinition.description}', - extra_vars: '${.ansibleJobDefinition.extraVars}', - }, + definition: { + id: 'ansible-job-template', + version: '1.0', + specVersion: '0.8', + name: 'Ansible Job Template', + description: + 'Define an Ansible Job Template within Ansible Automation Platform', + dataInputSchema: 'schemas/ansible-job-template__main-schema.json', + functions: [ + { + name: 'runActionFetchTemplate', + operation: 'specs/actions-openapi.json#fetch:template', + }, + { + name: 'runActionGitHubRepoPush', + operation: 'specs/actions-openapi.json#github:repo:push', + }, + { + name: 'runActionCatalogRegister', + operation: 'specs/actions-openapi.json#catalog:register', + }, + { + name: 'fs:delete', + operation: 'specs/actions-openapi.json#fs:delete', + }, + { + name: 'sysout', + type: 'custom', + operation: 'sysout', + }, + ], + errors: [ + { + name: 'Error on Action', + code: 'java.lang.RuntimeException', + }, + ], + start: 'Code and Catalog generation', + states: [ + { + name: 'Code and Catalog generation', + type: 'parallel', + branches: [ + { + name: 'Generating the Ansible Job component', + actions: [ + { + name: 'Run Template Fetch Action', + functionRef: { + refName: 'runActionFetchTemplate', + arguments: { + id: '$WORKFLOW.instanceId', + url: 'https://github.com/janus-idp/software-templates/tree/main/templates/github/launch-ansible-job/skeleton', + values: { + name: '${.ansibleJobDefinition.name}', + jobTemplate: '${.ansibleJobDefinition.jobTemplate}', + component_id: '${.ansibleJobDefinition.name}', + namespace: '${.ansibleJobDefinition.namespace}', + connection_secret: + '${.ansibleJobDefinition.connectionSecret}', + description: '${.ansibleJobDefinition.description}', + extra_vars: '${.ansibleJobDefinition.extraVars}', }, }, }, - ], - }, - { - name: 'Generating the Catalog Info Component', - actions: [ - { - functionRef: { - refName: 'runActionFetchTemplate', - arguments: { - id: '$WORKFLOW.instanceId', - url: 'https://github.com/janus-idp/software-templates/tree/main/skeletons/catalog-info', - values: { - githubOrg: '${.repositoryInfo.githubOrg}', - repoName: '${.repositoryInfo.repoName}', - owner: '${.repositoryInfo.owner}', - applicationType: 'api', - description: '${.ansibleJobDefinition.description}', - }, + }, + ], + }, + { + name: 'Generating the Catalog Info Component', + actions: [ + { + functionRef: { + refName: 'runActionFetchTemplate', + arguments: { + id: '$WORKFLOW.instanceId', + url: 'https://github.com/janus-idp/software-templates/tree/main/skeletons/catalog-info', + values: { + githubOrg: '${.repositoryInfo.githubOrg}', + repoName: '${.repositoryInfo.repoName}', + owner: '${.repositoryInfo.owner}', + applicationType: 'api', + description: '${.ansibleJobDefinition.description}', }, }, }, - ], - }, - ], - onErrors: [ - { - errorRef: 'Error on Action', - transition: 'Handle Error', - }, - ], - compensatedBy: 'Clear Code and Catalog generation', - transition: 'Publishing to the Source Code Repository', - }, - { - name: 'Publishing to the Source Code Repository', - type: 'operation', - actionMode: 'sequential', - actions: [ - { - name: 'Publish Github', - functionRef: { - refName: 'runActionGitHubRepoPush', - arguments: { - id: '$WORKFLOW.instanceId', - title: '.ansibleJobDefinition.name + "-job"', - description: 'Workflow Action', - repoUrl: - '"github.com?owner=" + .repositoryInfo.githubOrg + "&repo=" + .repositoryInfo.repoName', - defaultBranch: 'main', - gitCommitMessage: 'Initial commit', - protectDefaultBranch: false, - protectEnforceAdmins: false, - }, }, - actionDataFilter: { - results: '.actionPublishResult', + ], + }, + ], + onErrors: [ + { + errorRef: 'Error on Action', + transition: 'Handle Error', + }, + ], + compensatedBy: 'Clear Code and Catalog generation', + transition: 'Publishing to the Source Code Repository', + }, + { + name: 'Publishing to the Source Code Repository', + type: 'operation', + actionMode: 'sequential', + actions: [ + { + name: 'Publish Github', + functionRef: { + refName: 'runActionGitHubRepoPush', + arguments: { + id: '$WORKFLOW.instanceId', + title: '.ansibleJobDefinition.name + "-job"', + description: 'Workflow Action', + repoUrl: + '"github.com?owner=" + .repositoryInfo.githubOrg + "&repo=" + .repositoryInfo.repoName', + defaultBranch: 'main', + gitCommitMessage: 'Initial commit', + protectDefaultBranch: false, + protectEnforceAdmins: false, }, }, - ], - onErrors: [ - { - errorRef: 'Error on Action', - transition: 'Handle Error', + actionDataFilter: { + results: '.actionPublishResult', }, - ], - compensatedBy: 'Remove Source Code Repository', - transition: 'Registering the Catalog Info Component', - }, - { - name: 'Registering the Catalog Info Component', - type: 'operation', - actionMode: 'sequential', - actions: [ - { - name: 'Catalog Register Action', - functionRef: { - refName: 'runActionCatalogRegister', - arguments: { - id: '$WORKFLOW.instanceId', - repoContentsUrl: '.actionPublishResult.repoContentsUrl', - catalogInfoPath: '"/catalog-info.yaml"', - }, - }, - actionDataFilter: { - toStateData: '.actionCatalogRegisterResult', + }, + ], + onErrors: [ + { + errorRef: 'Error on Action', + transition: 'Handle Error', + }, + ], + compensatedBy: 'Remove Source Code Repository', + transition: 'Registering the Catalog Info Component', + }, + { + name: 'Registering the Catalog Info Component', + type: 'operation', + actionMode: 'sequential', + actions: [ + { + name: 'Catalog Register Action', + functionRef: { + refName: 'runActionCatalogRegister', + arguments: { + id: '$WORKFLOW.instanceId', + repoContentsUrl: '.actionPublishResult.repoContentsUrl', + catalogInfoPath: '"/catalog-info.yaml"', }, }, - ], - onErrors: [ - { - errorRef: 'Error on Action', - transition: 'Handle Error', + actionDataFilter: { + toStateData: '.actionCatalogRegisterResult', }, - ], - compensatedBy: 'Remove Catalog Info Component', - end: true, - }, - { - name: 'Clear Code and Catalog generation', - type: 'operation', - usedForCompensation: true, - actions: [ - { - name: 'Clear FS Action', - functionRef: { - refName: 'fs:delete', - arguments: { - files: ['./'], - }, + }, + ], + onErrors: [ + { + errorRef: 'Error on Action', + transition: 'Handle Error', + }, + ], + compensatedBy: 'Remove Catalog Info Component', + end: true, + }, + { + name: 'Clear Code and Catalog generation', + type: 'operation', + usedForCompensation: true, + actions: [ + { + name: 'Clear FS Action', + functionRef: { + refName: 'fs:delete', + arguments: { + files: ['./'], }, }, - ], - }, - { - name: 'Remove Source Code Repository', - type: 'operation', - usedForCompensation: true, - actions: [ - { - name: 'Remove Source Code Repository', - functionRef: { - refName: 'sysout', - arguments: { - message: 'Remove Source Code Repository', - }, + }, + ], + }, + { + name: 'Remove Source Code Repository', + type: 'operation', + usedForCompensation: true, + actions: [ + { + name: 'Remove Source Code Repository', + functionRef: { + refName: 'sysout', + arguments: { + message: 'Remove Source Code Repository', }, }, - ], - }, - { - name: 'Remove Catalog Info Component', - type: 'operation', - usedForCompensation: true, - actions: [ - { - name: 'Remove Catalog Info Component', - functionRef: { - refName: 'sysout', - arguments: { - message: 'Remove Catalog Info Component', - }, + }, + ], + }, + { + name: 'Remove Catalog Info Component', + type: 'operation', + usedForCompensation: true, + actions: [ + { + name: 'Remove Catalog Info Component', + functionRef: { + refName: 'sysout', + arguments: { + message: 'Remove Catalog Info Component', }, }, - ], - }, - { - name: 'Handle Error', - type: 'operation', - actions: [ - { - name: 'Error Action', - functionRef: { - refName: 'sysout', - arguments: { - message: 'Error on workflow, triggering compensations', - }, + }, + ], + }, + { + name: 'Handle Error', + type: 'operation', + actions: [ + { + name: 'Error Action', + functionRef: { + refName: 'sysout', + arguments: { + message: 'Error on workflow, triggering compensations', }, }, - ], - end: { - compensate: true, }, + ], + end: { + compensate: true, }, - ], - }, + }, + ], }, isComposedSchema: true, schemaSteps: [ diff --git a/plugins/orchestrator/src/__fixtures__/fakeWorkflowDataInputSchemaResponseMultiStepInitialState.ts b/plugins/orchestrator/src/__fixtures__/fakeWorkflowDataInputSchemaResponseMultiStepInitialState.ts index efdad9e212..f4b0a93fa0 100644 --- a/plugins/orchestrator/src/__fixtures__/fakeWorkflowDataInputSchemaResponseMultiStepInitialState.ts +++ b/plugins/orchestrator/src/__fixtures__/fakeWorkflowDataInputSchemaResponseMultiStepInitialState.ts @@ -2,405 +2,402 @@ import { WorkflowInputSchemaResponse } from '@janus-idp/backstage-plugin-orchest export const fakeDataInputSchemaMultiStepInitialStateResponse: WorkflowInputSchemaResponse = { - workflowItem: { - uri: 'quarkus-backend.sw.yaml', - definition: { - id: 'quarkus-backend', - version: '1.0', - specVersion: '0.8', - name: 'Quarkus Backend application', - description: - 'Create a starter Quarkus backend application with a CI pipeline', - dataInputSchema: 'schemas/quarkus-backend__main-schema.json', - functions: [ - { - name: 'runActionFetchTemplate', - operation: 'specs/actions-openapi.json#fetch:template', - }, - { - name: 'runActionPublishGithub', - operation: 'specs/actions-openapi.json#publish:github', - }, - { - name: 'runActionCatalogRegister', - operation: 'specs/actions-openapi.json#catalog:register', - }, - { - name: 'fs:delete', - operation: 'specs/actions-openapi.json#fs:delete', - }, - { - name: 'sysout', - type: 'custom', - operation: 'sysout', - }, - ], - errors: [ - { - name: 'Error on Action', - code: 'java.lang.RuntimeException', - }, - ], - start: 'Generating the Source Code Component', - states: [ - { - name: 'Generating the Source Code Component', - type: 'operation', - actionMode: 'sequential', - actions: [ - { - name: 'Fetch Template Action - Source Code', - functionRef: { - refName: 'runActionFetchTemplate', - arguments: { - url: 'https://github.com/janus-idp/software-templates/tree/main/templates/github/quarkus-backend/skeleton', - values: { - orgName: '.orgName', - repoName: '.repoName', - owner: '.owner', - system: '.system', - applicationType: 'api', - description: '.description', - namespace: '.namespace', - port: '.port', - ci: '.ci', - sourceControl: 'github.com', - groupId: '.groupId', - artifactId: '.artifactId', - javaPackageName: '.javaPackageName', - version: '.version', - }, + definition: { + id: 'quarkus-backend', + version: '1.0', + specVersion: '0.8', + name: 'Quarkus Backend application', + description: + 'Create a starter Quarkus backend application with a CI pipeline', + dataInputSchema: 'schemas/quarkus-backend__main-schema.json', + functions: [ + { + name: 'runActionFetchTemplate', + operation: 'specs/actions-openapi.json#fetch:template', + }, + { + name: 'runActionPublishGithub', + operation: 'specs/actions-openapi.json#publish:github', + }, + { + name: 'runActionCatalogRegister', + operation: 'specs/actions-openapi.json#catalog:register', + }, + { + name: 'fs:delete', + operation: 'specs/actions-openapi.json#fs:delete', + }, + { + name: 'sysout', + type: 'custom', + operation: 'sysout', + }, + ], + errors: [ + { + name: 'Error on Action', + code: 'java.lang.RuntimeException', + }, + ], + start: 'Generating the Source Code Component', + states: [ + { + name: 'Generating the Source Code Component', + type: 'operation', + actionMode: 'sequential', + actions: [ + { + name: 'Fetch Template Action - Source Code', + functionRef: { + refName: 'runActionFetchTemplate', + arguments: { + url: 'https://github.com/janus-idp/software-templates/tree/main/templates/github/quarkus-backend/skeleton', + values: { + orgName: '.orgName', + repoName: '.repoName', + owner: '.owner', + system: '.system', + applicationType: 'api', + description: '.description', + namespace: '.namespace', + port: '.port', + ci: '.ci', + sourceControl: 'github.com', + groupId: '.groupId', + artifactId: '.artifactId', + javaPackageName: '.javaPackageName', + version: '.version', }, }, - actionDataFilter: { - toStateData: '.actionFetchTemplateSourceCodeResult', - }, }, - ], - onErrors: [ - { - errorRef: 'Error on Action', - transition: 'Handle Error', + actionDataFilter: { + toStateData: '.actionFetchTemplateSourceCodeResult', }, - ], - compensatedBy: 'Clear File System - Source Code', - transition: 'Generating the CI Component', - }, - { - name: 'Generating the CI Component', - type: 'switch', - dataConditions: [ - { - condition: '${ .ci == "github" }', - transition: 'Generating the CI Component - GitHub', - }, - { - condition: '${ .ci == "tekton" }', - transition: 'Generating the CI Component - Tekton', - }, - ], - defaultCondition: { + }, + ], + onErrors: [ + { + errorRef: 'Error on Action', + transition: 'Handle Error', + }, + ], + compensatedBy: 'Clear File System - Source Code', + transition: 'Generating the CI Component', + }, + { + name: 'Generating the CI Component', + type: 'switch', + dataConditions: [ + { + condition: '${ .ci == "github" }', transition: 'Generating the CI Component - GitHub', }, + { + condition: '${ .ci == "tekton" }', + transition: 'Generating the CI Component - Tekton', + }, + ], + defaultCondition: { + transition: 'Generating the CI Component - GitHub', }, - { - name: 'Generating the CI Component - GitHub', - type: 'operation', - actionMode: 'sequential', - actions: [ - { - name: 'Run Template Fetch Action - CI - GitHub', - functionRef: { - refName: 'runActionFetchTemplate', - arguments: { - url: 'https://github.com/janus-idp/software-templates/tree/main/skeletons/github-actions', - copyWithoutTemplating: ['".github/workflows/"'], - values: { - orgName: '.orgName', - repoName: '.repoName', - owner: '.owner', - system: '.system', - applicationType: 'api', - description: '.description', - namespace: '.namespace', - port: '.port', - ci: '.ci', - sourceControl: 'github.com', - groupId: '.groupId', - artifactId: '.artifactId', - javaPackageName: '.javaPackageName', - version: '.version', - }, + }, + { + name: 'Generating the CI Component - GitHub', + type: 'operation', + actionMode: 'sequential', + actions: [ + { + name: 'Run Template Fetch Action - CI - GitHub', + functionRef: { + refName: 'runActionFetchTemplate', + arguments: { + url: 'https://github.com/janus-idp/software-templates/tree/main/skeletons/github-actions', + copyWithoutTemplating: ['".github/workflows/"'], + values: { + orgName: '.orgName', + repoName: '.repoName', + owner: '.owner', + system: '.system', + applicationType: 'api', + description: '.description', + namespace: '.namespace', + port: '.port', + ci: '.ci', + sourceControl: 'github.com', + groupId: '.groupId', + artifactId: '.artifactId', + javaPackageName: '.javaPackageName', + version: '.version', }, }, - actionDataFilter: { - toStateData: '.actionTemplateFetchCIResult', - }, }, - ], - onErrors: [ - { - errorRef: 'Error on Action', - transition: 'Handle Error', + actionDataFilter: { + toStateData: '.actionTemplateFetchCIResult', }, - ], - compensatedBy: 'Clear File System - CI', - transition: 'Generating the Catalog Info Component', - }, - { - name: 'Generating the CI Component - Tekton', - type: 'operation', - actionMode: 'sequential', - actions: [ - { - name: 'Run Template Fetch Action - CI - Tekton', - functionRef: { - refName: 'runActionFetchTemplate', - arguments: { - url: 'https://github.com/janus-idp/software-templates/tree/main/skeletons/tekton', - copyWithoutTemplating: ['".github/workflows/"'], - values: { - orgName: '.orgName', - repoName: '.repoName', - owner: '.owner', - system: '.system', - applicationType: 'api', - description: '.description', - namespace: '.namespace', - imageUrl: '.imageUrl', - imageRepository: '.imageRepository', - imageBuilder: 's2i-java', - port: '.port', - ci: '.ci', - sourceControl: 'github.com', - groupId: '.groupId', - artifactId: '.artifactId', - javaPackageName: '.javaPackageName', - version: '.version', - }, + }, + ], + onErrors: [ + { + errorRef: 'Error on Action', + transition: 'Handle Error', + }, + ], + compensatedBy: 'Clear File System - CI', + transition: 'Generating the Catalog Info Component', + }, + { + name: 'Generating the CI Component - Tekton', + type: 'operation', + actionMode: 'sequential', + actions: [ + { + name: 'Run Template Fetch Action - CI - Tekton', + functionRef: { + refName: 'runActionFetchTemplate', + arguments: { + url: 'https://github.com/janus-idp/software-templates/tree/main/skeletons/tekton', + copyWithoutTemplating: ['".github/workflows/"'], + values: { + orgName: '.orgName', + repoName: '.repoName', + owner: '.owner', + system: '.system', + applicationType: 'api', + description: '.description', + namespace: '.namespace', + imageUrl: '.imageUrl', + imageRepository: '.imageRepository', + imageBuilder: 's2i-java', + port: '.port', + ci: '.ci', + sourceControl: 'github.com', + groupId: '.groupId', + artifactId: '.artifactId', + javaPackageName: '.javaPackageName', + version: '.version', }, }, - actionDataFilter: { - toStateData: '.actionTemplateFetchCIResult', - }, }, - ], - onErrors: [ - { - errorRef: 'Error on Action', - transition: 'Handle Error', + actionDataFilter: { + toStateData: '.actionTemplateFetchCIResult', }, - ], - compensatedBy: 'Clear File System - CI', - transition: 'Generating the Catalog Info Component', - }, - { - name: 'Generating the Catalog Info Component', - type: 'operation', - actions: [ - { - name: 'Fetch Template Action - Catalog Info', - functionRef: { - refName: 'runActionFetchTemplate', - arguments: { - url: 'https://github.com/janus-idp/software-templates/tree/main/skeletons/catalog-info', - values: { - orgName: '.orgName', - repoName: '.repoName', - owner: '.owner', - system: '.system', - applicationType: 'api', - description: '.description', - namespace: '.namespace', - imageUrl: 'imageUrl', - imageRepository: '.imageRepository', - imageBuilder: 's2i-go', - port: '.port', - ci: '.ci', - sourceControl: 'github.com', - groupId: '.groupId', - artifactId: '.artifactId', - javaPackageName: '.javaPackageName', - version: '.version', - }, + }, + ], + onErrors: [ + { + errorRef: 'Error on Action', + transition: 'Handle Error', + }, + ], + compensatedBy: 'Clear File System - CI', + transition: 'Generating the Catalog Info Component', + }, + { + name: 'Generating the Catalog Info Component', + type: 'operation', + actions: [ + { + name: 'Fetch Template Action - Catalog Info', + functionRef: { + refName: 'runActionFetchTemplate', + arguments: { + url: 'https://github.com/janus-idp/software-templates/tree/main/skeletons/catalog-info', + values: { + orgName: '.orgName', + repoName: '.repoName', + owner: '.owner', + system: '.system', + applicationType: 'api', + description: '.description', + namespace: '.namespace', + imageUrl: 'imageUrl', + imageRepository: '.imageRepository', + imageBuilder: 's2i-go', + port: '.port', + ci: '.ci', + sourceControl: 'github.com', + groupId: '.groupId', + artifactId: '.artifactId', + javaPackageName: '.javaPackageName', + version: '.version', }, }, - actionDataFilter: { - toStateData: '.actionFetchTemplateCatalogInfoResult', - }, }, - ], - onErrors: [ - { - errorRef: 'Error on Action', - transition: 'Handle Error', + actionDataFilter: { + toStateData: '.actionFetchTemplateCatalogInfoResult', }, - ], - compensatedBy: 'Clear File System - Catalog', - transition: 'Publishing to the Source Code Repository', - }, - { - name: 'Publishing to the Source Code Repository', - type: 'operation', - actionMode: 'sequential', - actions: [ - { - name: 'Publish Github', - functionRef: { - refName: 'runActionPublishGithub', - arguments: { - allowedHosts: ['"github.com"'], - description: 'Workflow Action', - repoUrl: - '"github.com?owner=" + .orgName + "&repo=" + .repoName', - defaultBranch: 'main', - gitCommitMessage: 'Initial commit', - allowAutoMerge: true, - allowRebaseMerge: true, - }, - }, - actionDataFilter: { - toStateData: '.actionPublishResult', + }, + ], + onErrors: [ + { + errorRef: 'Error on Action', + transition: 'Handle Error', + }, + ], + compensatedBy: 'Clear File System - Catalog', + transition: 'Publishing to the Source Code Repository', + }, + { + name: 'Publishing to the Source Code Repository', + type: 'operation', + actionMode: 'sequential', + actions: [ + { + name: 'Publish Github', + functionRef: { + refName: 'runActionPublishGithub', + arguments: { + allowedHosts: ['"github.com"'], + description: 'Workflow Action', + repoUrl: + '"github.com?owner=" + .orgName + "&repo=" + .repoName', + defaultBranch: 'main', + gitCommitMessage: 'Initial commit', + allowAutoMerge: true, + allowRebaseMerge: true, }, }, - ], - onErrors: [ - { - errorRef: 'Error on Action', - transition: 'Handle Error', + actionDataFilter: { + toStateData: '.actionPublishResult', }, - ], - compensatedBy: 'Remove Source Code Repository', - transition: 'Registering the Catalog Info Component', - }, - { - name: 'Registering the Catalog Info Component', - type: 'operation', - actionMode: 'sequential', - actions: [ - { - name: 'Catalog Register Action', - functionRef: { - refName: 'runActionCatalogRegister', - arguments: { - repoContentsUrl: '.actionPublishResult.repoContentsUrl', - catalogInfoPath: '"/catalog-info.yaml"', - }, - }, - actionDataFilter: { - toStateData: '.actionCatalogRegisterResult', + }, + ], + onErrors: [ + { + errorRef: 'Error on Action', + transition: 'Handle Error', + }, + ], + compensatedBy: 'Remove Source Code Repository', + transition: 'Registering the Catalog Info Component', + }, + { + name: 'Registering the Catalog Info Component', + type: 'operation', + actionMode: 'sequential', + actions: [ + { + name: 'Catalog Register Action', + functionRef: { + refName: 'runActionCatalogRegister', + arguments: { + repoContentsUrl: '.actionPublishResult.repoContentsUrl', + catalogInfoPath: '"/catalog-info.yaml"', }, }, - ], - onErrors: [ - { - errorRef: 'Error on Action', - transition: 'Handle Error', + actionDataFilter: { + toStateData: '.actionCatalogRegisterResult', }, - ], - compensatedBy: 'Remove Catalog Info Component', - end: true, - }, - { - name: 'Handle Error', - type: 'operation', - actions: [ - { - name: 'Error Action', - functionRef: { - refName: 'sysout', - arguments: { - message: 'Error on workflow, triggering compensations', - }, + }, + ], + onErrors: [ + { + errorRef: 'Error on Action', + transition: 'Handle Error', + }, + ], + compensatedBy: 'Remove Catalog Info Component', + end: true, + }, + { + name: 'Handle Error', + type: 'operation', + actions: [ + { + name: 'Error Action', + functionRef: { + refName: 'sysout', + arguments: { + message: 'Error on workflow, triggering compensations', }, }, - ], - end: { - compensate: true, }, + ], + end: { + compensate: true, }, - { - name: 'Clear File System - Source Code', - type: 'operation', - usedForCompensation: true, - actions: [ - { - name: 'Clear FS Action', - functionRef: { - refName: 'fs:delete', - arguments: { - files: ['./'], - }, + }, + { + name: 'Clear File System - Source Code', + type: 'operation', + usedForCompensation: true, + actions: [ + { + name: 'Clear FS Action', + functionRef: { + refName: 'fs:delete', + arguments: { + files: ['./'], }, }, - ], - }, - { - name: 'Clear File System - CI', - type: 'operation', - usedForCompensation: true, - actions: [ - { - name: 'Clear FS Action', - functionRef: { - refName: 'fs:delete', - arguments: { - files: ['./'], - }, + }, + ], + }, + { + name: 'Clear File System - CI', + type: 'operation', + usedForCompensation: true, + actions: [ + { + name: 'Clear FS Action', + functionRef: { + refName: 'fs:delete', + arguments: { + files: ['./'], }, }, - ], - }, - { - name: 'Clear File System - Catalog', - type: 'operation', - usedForCompensation: true, - actions: [ - { - name: 'Clear FS Action', - functionRef: { - refName: 'fs:delete', - arguments: { - files: ['./'], - }, + }, + ], + }, + { + name: 'Clear File System - Catalog', + type: 'operation', + usedForCompensation: true, + actions: [ + { + name: 'Clear FS Action', + functionRef: { + refName: 'fs:delete', + arguments: { + files: ['./'], }, }, - ], - }, - { - name: 'Remove Source Code Repository', - type: 'operation', - usedForCompensation: true, - actions: [ - { - name: 'Remove Source Code Repository', - functionRef: { - refName: 'sysout', - arguments: { - message: 'Remove Source Code Repository', - }, + }, + ], + }, + { + name: 'Remove Source Code Repository', + type: 'operation', + usedForCompensation: true, + actions: [ + { + name: 'Remove Source Code Repository', + functionRef: { + refName: 'sysout', + arguments: { + message: 'Remove Source Code Repository', }, }, - ], - }, - { - name: 'Remove Catalog Info Component', - type: 'operation', - usedForCompensation: true, - actions: [ - { - name: 'Remove Catalog Info Component', - functionRef: { - refName: 'sysout', - arguments: { - message: 'Remove Catalog Info Component', - }, + }, + ], + }, + { + name: 'Remove Catalog Info Component', + type: 'operation', + usedForCompensation: true, + actions: [ + { + name: 'Remove Catalog Info Component', + functionRef: { + refName: 'sysout', + arguments: { + message: 'Remove Catalog Info Component', }, }, - ], - }, - ], - }, + }, + ], + }, + ], }, schemaSteps: [ { diff --git a/plugins/orchestrator/src/__fixtures__/fakeWorkflowDefinition.ts b/plugins/orchestrator/src/__fixtures__/fakeWorkflowDefinition.ts new file mode 100644 index 0000000000..98d37f597f --- /dev/null +++ b/plugins/orchestrator/src/__fixtures__/fakeWorkflowDefinition.ts @@ -0,0 +1,244 @@ +import { WorkflowDefinition } from '@janus-idp/backstage-plugin-orchestrator-common'; + +export const fakeWorkflowDefinition: WorkflowDefinition = { + id: 'quarkus-backend-workflow-ci-switch', + version: '1.0', + specVersion: '0.8', + name: '[WF] Create a starter Quarkus Backend application with a CI pipeline - CI Switch', + description: + '[WF] Create a starter Quarkus Backend application with a CI pipeline - CI Switch', + functions: [ + { + name: 'runActionTemplateFetch', + operation: 'specs/actions-openapi.json#fetch:template', + }, + { + name: 'runActionPublishGithub', + operation: 'specs/actions-openapi.json#publish:github', + }, + { + name: 'runActionCatalogRegister', + operation: 'specs/actions-openapi.json#catalog:register', + }, + ], + start: 'Generating the Source Code Component', + states: [ + { + name: 'Generating the Source Code Component', + type: 'operation', + actionMode: 'sequential', + actions: [ + { + name: 'Fetch Template Action - Source Code', + functionRef: { + refName: 'runActionTemplateFetch', + arguments: { + url: 'https://github.com/janus-idp/software-templates/tree/main/templates/github/quarkus-backend/skeleton', + values: { + githubOrg: '.githubOrg', + repoName: '.repoName', + owner: '.owner', + system: '.system', + applicationType: 'api', + description: '.description', + namespace: '.namespace', + imageUrl: 'imageUrl', + imageBuilder: '.imageBuilder', + imageRepository: '.imageRepository', + port: '.port', + ci: '.ci', + groupId: '.groupId', + artifactId: '.artifactId', + javaPackageName: '.javaPackageName', + }, + }, + }, + actionDataFilter: { + toStateData: '.actionFetchTemplateSourceCodeResult', + }, + }, + ], + transition: 'Generating the CI Component', + }, + { + name: 'Generating the CI Component', + type: 'switch', + dataConditions: [ + { + condition: '${ .ci == "github" }', + transition: 'Generating the CI Component - GitHub', + }, + { + condition: '${ .ci == "tekton" }', + transition: 'Generating the CI Component - Tekton', + }, + ], + defaultCondition: { + transition: 'Generating the CI Component - GitHub', + }, + }, + { + name: 'Generating the CI Component - GitHub', + type: 'operation', + actionMode: 'sequential', + actions: [ + { + name: 'Run Template Fetch Action - CI - GitHub', + functionRef: { + refName: 'runActionTemplateFetch', + arguments: { + url: 'https://github.com/janus-idp/software-templates/tree/main/skeletons/github-actions', + copyWithoutTemplating: ['".github/workflows/"'], + values: { + githubOrg: '.githubOrg', + repoName: '.repoName', + owner: '.owner', + system: '.system', + applicationType: 'api', + description: '.description', + namespace: '.namespace', + imageUrl: 'imageUrl', + imageBuilder: '.imageBuilder', + imageRepository: '.imageRepository', + port: '.port', + ci: '.ci', + groupId: '.groupId', + artifactId: '.artifactId', + javaPackageName: '.javaPackageName', + }, + }, + }, + actionDataFilter: { + toStateData: '.actionTemplateFetchCIResult', + }, + }, + ], + transition: 'Generating the Catalog Info Component', + }, + { + name: 'Generating the CI Component - Tekton', + type: 'operation', + actionMode: 'sequential', + actions: [ + { + name: 'Run Template Fetch Action - CI - Tekton', + functionRef: { + refName: 'runActionTemplateFetch', + arguments: { + url: 'https://github.com/janus-idp/software-templates/tree/main/skeletons/tekton', + copyWithoutTemplating: ['".tekton/workflows/"'], + values: { + githubOrg: '.githubOrg', + repoName: '.repoName', + owner: '.owner', + system: '.system', + applicationType: 'api', + description: '.description', + namespace: '.namespace', + imageUrl: 'imageUrl', + imageBuilder: '.imageBuilder', + imageRepository: '.imageRepository', + port: '.port', + ci: '.ci', + groupId: '.groupId', + artifactId: '.artifactId', + javaPackageName: '.javaPackageName', + }, + }, + }, + actionDataFilter: { + toStateData: '.actionTemplateFetchCIResult', + }, + }, + ], + transition: 'Generating the Catalog Info Component', + }, + { + name: 'Generating the Catalog Info Component', + type: 'operation', + actions: [ + { + name: 'Fetch Template Action - Catalog Info', + functionRef: { + refName: 'runActionTemplateFetch', + arguments: { + url: 'https://github.com/janus-idp/software-templates/tree/main/skeletons/catalog-info', + values: { + githubOrg: '.githubOrg', + repoName: '.repoName', + owner: '.owner', + system: '.system', + applicationType: 'api', + description: '.description', + namespace: '.namespace', + imageUrl: 'imageUrl', + imageBuilder: '.imageBuilder', + imageRepository: '.imageRepository', + port: '.port', + ci: '.ci', + groupId: '.groupId', + artifactId: '.artifactId', + javaPackageName: '.javaPackageName', + }, + }, + }, + actionDataFilter: { + toStateData: '.actionFetchTemplateCatalogInfoResult', + }, + }, + ], + transition: 'Publishing to the Source Code Repository', + }, + { + name: 'Publishing to the Source Code Repository', + type: 'operation', + actionMode: 'sequential', + actions: [ + { + name: 'Publish Github Action', + functionRef: { + refName: 'runActionPublishGithub', + arguments: { + allowedHosts: ['"github.com"'], + description: 'Workflow Action', + repoUrl: + '"github.com?owner=" + .githubOrg + "&repo=" + .repoName', + defaultBranch: '.defaultBranch', + gitCommitMessage: '.gitCommitMessage', + allowAutoMerge: true, + allowRebaseMerge: true, + }, + }, + actionDataFilter: { + toStateData: '.actionPublishResult', + }, + }, + ], + transition: 'Registering the Catalog Info Component', + }, + { + name: 'Registering the Catalog Info Component', + type: 'operation', + actionMode: 'sequential', + actions: [ + { + name: 'Catalog Register Action', + functionRef: { + refName: 'runActionCatalogRegister', + arguments: { + repoContentsUrl: '.actionPublishResult.repoContentsUrl', + catalogInfoPath: '"/catalog-info.yaml"', + }, + }, + actionDataFilter: { + toStateData: '.actionCatalogRegisterResult', + }, + }, + ], + end: true, + }, + ], + dataInputSchema: + 'schemas/quarkus-backend-workflow-ci-switch__main_schema.json', + annotations: ['workflow-type/ci'], +}; diff --git a/plugins/orchestrator/src/__fixtures__/fakeWorkflowInputSchemaResponse.ts b/plugins/orchestrator/src/__fixtures__/fakeWorkflowInputSchemaResponse.ts index ff20633c19..f65aef0a42 100644 --- a/plugins/orchestrator/src/__fixtures__/fakeWorkflowInputSchemaResponse.ts +++ b/plugins/orchestrator/src/__fixtures__/fakeWorkflowInputSchemaResponse.ts @@ -1,77 +1,74 @@ import { WorkflowInputSchemaResponse } from '@janus-idp/backstage-plugin-orchestrator-common'; export const fakeDataInputSchemaResponse: WorkflowInputSchemaResponse = { - workflowItem: { - uri: 'yamlgreet.sw.yaml', - definition: { - id: 'yamlgreet', - version: '1.0', - specVersion: '0.8', - name: 'Greeting workflow', - description: 'YAML based greeting workflow', - dataInputSchema: 'schemas/yamlgreet__main_schema.json', - start: 'ChooseOnLanguage', - functions: [ - { - name: 'greetFunction', - type: 'custom', - operation: 'sysout', - }, - ], - states: [ - { - name: 'ChooseOnLanguage', - type: 'switch', - dataConditions: [ - { - condition: '${ .language == "English" }', - transition: 'GreetInEnglish', - }, - { - condition: '${ .language == "Spanish" }', - transition: 'GreetInSpanish', - }, - ], - defaultCondition: { + definition: { + id: 'yamlgreet', + version: '1.0', + specVersion: '0.8', + name: 'Greeting workflow', + description: 'YAML based greeting workflow', + dataInputSchema: 'schemas/yamlgreet__main_schema.json', + start: 'ChooseOnLanguage', + functions: [ + { + name: 'greetFunction', + type: 'custom', + operation: 'sysout', + }, + ], + states: [ + { + name: 'ChooseOnLanguage', + type: 'switch', + dataConditions: [ + { + condition: '${ .language == "English" }', transition: 'GreetInEnglish', }, - }, - { - name: 'GreetInEnglish', - type: 'inject', - data: { - greeting: 'Hello from YAML Workflow, ', + { + condition: '${ .language == "Spanish" }', + transition: 'GreetInSpanish', }, - transition: 'GreetPerson', + ], + defaultCondition: { + transition: 'GreetInEnglish', }, - { - name: 'GreetInSpanish', - type: 'inject', - data: { - greeting: 'Saludos desde YAML Workflow, ', - }, - transition: 'GreetPerson', + }, + { + name: 'GreetInEnglish', + type: 'inject', + data: { + greeting: 'Hello from YAML Workflow, ', + }, + transition: 'GreetPerson', + }, + { + name: 'GreetInSpanish', + type: 'inject', + data: { + greeting: 'Saludos desde YAML Workflow, ', }, - { - name: 'GreetPerson', - type: 'operation', - actions: [ - { - name: 'greetAction', - functionRef: { - refName: 'greetFunction', - arguments: { - message: '.greeting+.name', - }, + transition: 'GreetPerson', + }, + { + name: 'GreetPerson', + type: 'operation', + actions: [ + { + name: 'greetAction', + functionRef: { + refName: 'greetFunction', + arguments: { + message: '.greeting+.name', }, }, - ], - end: { - terminate: true, }, + ], + end: { + terminate: true, }, - ], - }, + }, + ], }, schemaSteps: [ { diff --git a/plugins/orchestrator/src/__fixtures__/fakeWorkflowItem.ts b/plugins/orchestrator/src/__fixtures__/fakeWorkflowItem.ts deleted file mode 100644 index a4d8249ea7..0000000000 --- a/plugins/orchestrator/src/__fixtures__/fakeWorkflowItem.ts +++ /dev/null @@ -1,247 +0,0 @@ -import { WorkflowItem } from '@janus-idp/backstage-plugin-orchestrator-common'; - -export const fakeWorkflowItem: WorkflowItem = { - uri: 'quarkus-backend-workflow-ci-switch.sw.yaml', - definition: { - id: 'quarkus-backend-workflow-ci-switch', - version: '1.0', - specVersion: '0.8', - name: '[WF] Create a starter Quarkus Backend application with a CI pipeline - CI Switch', - description: - '[WF] Create a starter Quarkus Backend application with a CI pipeline - CI Switch', - functions: [ - { - name: 'runActionTemplateFetch', - operation: 'specs/actions-openapi.json#fetch:template', - }, - { - name: 'runActionPublishGithub', - operation: 'specs/actions-openapi.json#publish:github', - }, - { - name: 'runActionCatalogRegister', - operation: 'specs/actions-openapi.json#catalog:register', - }, - ], - start: 'Generating the Source Code Component', - states: [ - { - name: 'Generating the Source Code Component', - type: 'operation', - actionMode: 'sequential', - actions: [ - { - name: 'Fetch Template Action - Source Code', - functionRef: { - refName: 'runActionTemplateFetch', - arguments: { - url: 'https://github.com/janus-idp/software-templates/tree/main/templates/github/quarkus-backend/skeleton', - values: { - githubOrg: '.githubOrg', - repoName: '.repoName', - owner: '.owner', - system: '.system', - applicationType: 'api', - description: '.description', - namespace: '.namespace', - imageUrl: 'imageUrl', - imageBuilder: '.imageBuilder', - imageRepository: '.imageRepository', - port: '.port', - ci: '.ci', - groupId: '.groupId', - artifactId: '.artifactId', - javaPackageName: '.javaPackageName', - }, - }, - }, - actionDataFilter: { - toStateData: '.actionFetchTemplateSourceCodeResult', - }, - }, - ], - transition: 'Generating the CI Component', - }, - { - name: 'Generating the CI Component', - type: 'switch', - dataConditions: [ - { - condition: '${ .ci == "github" }', - transition: 'Generating the CI Component - GitHub', - }, - { - condition: '${ .ci == "tekton" }', - transition: 'Generating the CI Component - Tekton', - }, - ], - defaultCondition: { - transition: 'Generating the CI Component - GitHub', - }, - }, - { - name: 'Generating the CI Component - GitHub', - type: 'operation', - actionMode: 'sequential', - actions: [ - { - name: 'Run Template Fetch Action - CI - GitHub', - functionRef: { - refName: 'runActionTemplateFetch', - arguments: { - url: 'https://github.com/janus-idp/software-templates/tree/main/skeletons/github-actions', - copyWithoutTemplating: ['".github/workflows/"'], - values: { - githubOrg: '.githubOrg', - repoName: '.repoName', - owner: '.owner', - system: '.system', - applicationType: 'api', - description: '.description', - namespace: '.namespace', - imageUrl: 'imageUrl', - imageBuilder: '.imageBuilder', - imageRepository: '.imageRepository', - port: '.port', - ci: '.ci', - groupId: '.groupId', - artifactId: '.artifactId', - javaPackageName: '.javaPackageName', - }, - }, - }, - actionDataFilter: { - toStateData: '.actionTemplateFetchCIResult', - }, - }, - ], - transition: 'Generating the Catalog Info Component', - }, - { - name: 'Generating the CI Component - Tekton', - type: 'operation', - actionMode: 'sequential', - actions: [ - { - name: 'Run Template Fetch Action - CI - Tekton', - functionRef: { - refName: 'runActionTemplateFetch', - arguments: { - url: 'https://github.com/janus-idp/software-templates/tree/main/skeletons/tekton', - copyWithoutTemplating: ['".tekton/workflows/"'], - values: { - githubOrg: '.githubOrg', - repoName: '.repoName', - owner: '.owner', - system: '.system', - applicationType: 'api', - description: '.description', - namespace: '.namespace', - imageUrl: 'imageUrl', - imageBuilder: '.imageBuilder', - imageRepository: '.imageRepository', - port: '.port', - ci: '.ci', - groupId: '.groupId', - artifactId: '.artifactId', - javaPackageName: '.javaPackageName', - }, - }, - }, - actionDataFilter: { - toStateData: '.actionTemplateFetchCIResult', - }, - }, - ], - transition: 'Generating the Catalog Info Component', - }, - { - name: 'Generating the Catalog Info Component', - type: 'operation', - actions: [ - { - name: 'Fetch Template Action - Catalog Info', - functionRef: { - refName: 'runActionTemplateFetch', - arguments: { - url: 'https://github.com/janus-idp/software-templates/tree/main/skeletons/catalog-info', - values: { - githubOrg: '.githubOrg', - repoName: '.repoName', - owner: '.owner', - system: '.system', - applicationType: 'api', - description: '.description', - namespace: '.namespace', - imageUrl: 'imageUrl', - imageBuilder: '.imageBuilder', - imageRepository: '.imageRepository', - port: '.port', - ci: '.ci', - groupId: '.groupId', - artifactId: '.artifactId', - javaPackageName: '.javaPackageName', - }, - }, - }, - actionDataFilter: { - toStateData: '.actionFetchTemplateCatalogInfoResult', - }, - }, - ], - transition: 'Publishing to the Source Code Repository', - }, - { - name: 'Publishing to the Source Code Repository', - type: 'operation', - actionMode: 'sequential', - actions: [ - { - name: 'Publish Github Action', - functionRef: { - refName: 'runActionPublishGithub', - arguments: { - allowedHosts: ['"github.com"'], - description: 'Workflow Action', - repoUrl: - '"github.com?owner=" + .githubOrg + "&repo=" + .repoName', - defaultBranch: '.defaultBranch', - gitCommitMessage: '.gitCommitMessage', - allowAutoMerge: true, - allowRebaseMerge: true, - }, - }, - actionDataFilter: { - toStateData: '.actionPublishResult', - }, - }, - ], - transition: 'Registering the Catalog Info Component', - }, - { - name: 'Registering the Catalog Info Component', - type: 'operation', - actionMode: 'sequential', - actions: [ - { - name: 'Catalog Register Action', - functionRef: { - refName: 'runActionCatalogRegister', - arguments: { - repoContentsUrl: '.actionPublishResult.repoContentsUrl', - catalogInfoPath: '"/catalog-info.yaml"', - }, - }, - actionDataFilter: { - toStateData: '.actionCatalogRegisterResult', - }, - }, - ], - end: true, - }, - ], - dataInputSchema: - 'schemas/quarkus-backend-workflow-ci-switch__main_schema.json', - annotations: ['workflow-type/ci'], - }, -}; diff --git a/plugins/orchestrator/src/__fixtures__/fakeWorkflowOverview.ts b/plugins/orchestrator/src/__fixtures__/fakeWorkflowOverview.ts index e769c24b75..c0382106d7 100644 --- a/plugins/orchestrator/src/__fixtures__/fakeWorkflowOverview.ts +++ b/plugins/orchestrator/src/__fixtures__/fakeWorkflowOverview.ts @@ -9,5 +9,5 @@ export const fakeWorkflowOverview: WorkflowOverview = { avgDurationMs: 150000, description: 'Create a starter Quarkus Backend application with a CI pipeline', - uri: 'quarkus-backend-workflow-ci-switch.sw.yaml', + format: 'yaml', }; diff --git a/plugins/orchestrator/src/__fixtures__/fakeWorkflowOverviewList.ts b/plugins/orchestrator/src/__fixtures__/fakeWorkflowOverviewList.ts index 27d8b2bb14..5c02c5cbfc 100644 --- a/plugins/orchestrator/src/__fixtures__/fakeWorkflowOverviewList.ts +++ b/plugins/orchestrator/src/__fixtures__/fakeWorkflowOverviewList.ts @@ -4,7 +4,7 @@ export const fakeWorkflowOverviewList: WorkflowOverview[] = [ { workflowId: 'quarkus-backend-workflow-ci-switch', name: '[WF] Create a starter Quarkus Backend application with a CI pipeline - CI Switch', - uri: 'quarkus-backend-workflow-ci-switch.sw.yaml', + format: 'yaml', lastTriggeredMs: 1701765793, category: 'Infrastructure', avgDurationMs: 5000, @@ -15,7 +15,7 @@ export const fakeWorkflowOverviewList: WorkflowOverview[] = [ { workflowId: 'orchestrator-ansible-job-long-timeout', name: '[WF] Ansible Job with Jira and Timeout', - uri: 'orchestrator-ansible-job-long-timeout.sw.yaml', + format: 'yaml', lastTriggeredMs: 1701765793, category: 'Infrastructure', avgDurationMs: 5000, @@ -26,7 +26,7 @@ export const fakeWorkflowOverviewList: WorkflowOverview[] = [ { workflowId: 'orchestrator-ansible-job-parallel-error-handler', name: '[WF] Ansible Job - Parallel/ERROR', - uri: 'orchestrator-ansible-job-parallel-error-handler.sw.yaml', + format: 'yaml', lastTriggeredMs: 1701765793, category: 'Infrastructure', avgDurationMs: 5000, @@ -37,7 +37,7 @@ export const fakeWorkflowOverviewList: WorkflowOverview[] = [ { workflowId: 'orchestrator-ansible-job-long', name: '[WF] Ansible Job with Jira', - uri: 'orchestrator-ansible-job-long.sw.yaml', + format: 'yaml', lastTriggeredMs: 1701765793, category: 'Infrastructure', avgDurationMs: 5000, @@ -48,7 +48,7 @@ export const fakeWorkflowOverviewList: WorkflowOverview[] = [ { workflowId: 'orchestrator-ansible-job', name: '[WF] Ansible Job', - uri: 'orchestrator-ansible-job.sw.yaml', + format: 'yaml', lastTriggeredMs: 1701765793, category: 'Infrastructure', avgDurationMs: 5000, @@ -59,7 +59,7 @@ export const fakeWorkflowOverviewList: WorkflowOverview[] = [ { workflowId: 'quarkus-backend-workflow-extended', name: '[WF] Create a Quarkus Backend application with a CI pipeline - Extended', - uri: 'quarkus-backend-workflow-extended.sw.yaml', + format: 'yaml', lastTriggeredMs: 1701765793, category: 'Infrastructure', avgDurationMs: 5000, @@ -70,7 +70,7 @@ export const fakeWorkflowOverviewList: WorkflowOverview[] = [ { workflowId: 'workflow_actions', name: 'Workflow name', - uri: 'workflow_actions.sw.yaml', + format: 'yaml', lastTriggeredMs: 1701765793, category: 'Infrastructure', avgDurationMs: 5000, @@ -80,7 +80,7 @@ export const fakeWorkflowOverviewList: WorkflowOverview[] = [ { workflowId: 'yamlgreet', name: 'Greeting workflow', - uri: 'yamlgreet.sw.yaml', + format: 'yaml', lastTriggeredMs: 1701765793, category: 'Infrastructure', avgDurationMs: 5000, @@ -90,7 +90,7 @@ export const fakeWorkflowOverviewList: WorkflowOverview[] = [ { workflowId: 'jira', name: '[WF] Jira', - uri: 'jira.sw.json', + format: 'json', lastTriggeredMs: 1701765793, category: 'Infrastructure', avgDurationMs: 5000, diff --git a/plugins/orchestrator/src/api/MockOrchestratorClient.ts b/plugins/orchestrator/src/api/MockOrchestratorClient.ts index 88d82e5711..9b55aef4fc 100644 --- a/plugins/orchestrator/src/api/MockOrchestratorClient.ts +++ b/plugins/orchestrator/src/api/MockOrchestratorClient.ts @@ -2,12 +2,10 @@ import { JsonObject } from '@backstage/types'; import { AssessedProcessInstance, - Job, ProcessInstance, + WorkflowDefinition, WorkflowExecutionResponse, WorkflowInputSchemaResponse, - WorkflowItem, - WorkflowListResult, WorkflowOverview, WorkflowOverviewListResult, } from '@janus-idp/backstage-plugin-orchestrator-common'; @@ -18,15 +16,16 @@ import { OrchestratorApi } from './api'; export interface MockOrchestratorApiData { executeWorkflowResponse: () => ReturnType; getInstanceResponse: () => ReturnType; - getInstancesResponse: ReturnType; - getInstanceJobsResponse: ReturnType; - getWorkflowResponse: ReturnType; + listInstancesResponse: ReturnType; + getWorkflowDefinitionResponse: ReturnType< + OrchestratorApi['getWorkflowDefinition'] + >; + getWorkflowSourceResponse: ReturnType; getWorkflowDataInputSchemaResponse: ReturnType< OrchestratorApi['getWorkflowDataInputSchema'] >; - listWorkflowsResponse: ReturnType; - listWorkflowsOverviewResponse: ReturnType< - OrchestratorApi['listWorkflowsOverview'] + listWorkflowOverviewsResponse: ReturnType< + OrchestratorApi['listWorkflowOverviews'] >; getWorkflowOverviewResponse: ReturnType< OrchestratorApi['getWorkflowOverview'] @@ -68,37 +67,37 @@ export class MockOrchestratorClient implements OrchestratorApi { return Promise.resolve(this._mockData.getInstanceResponse()); } - getInstanceJobs(_instanceId: string): Promise { + listInstances(): Promise { if ( - !hasOwnProp(this._mockData, 'getInstanceJobsResponse') || - !isNonNullable(this._mockData.getInstanceJobsResponse) + !hasOwnProp(this._mockData, 'listInstancesResponse') || + !isNonNullable(this._mockData.listInstancesResponse) ) { - throw new Error(`[getInstanceJobs]: No mock data available`); + throw new Error(`[listInstances]: No mock data available`); } - return Promise.resolve(this._mockData.getInstanceJobsResponse); + return Promise.resolve(this._mockData.listInstancesResponse); } - getInstances(): Promise { + getWorkflowSource(_workflowId: string): Promise { if ( - !hasOwnProp(this._mockData, 'getInstancesResponse') || - !isNonNullable(this._mockData.getInstancesResponse) + !hasOwnProp(this._mockData, 'getWorkflowSourceResponse') || + !isNonNullable(this._mockData.getWorkflowSourceResponse) ) { - throw new Error(`[getInstances]: No mock data available`); + throw new Error(`[getWorkflowSource]: No mock data available`); } - return Promise.resolve(this._mockData.getInstancesResponse); + return Promise.resolve(this._mockData.getWorkflowSourceResponse); } - getWorkflow(_workflowId: string): Promise { + getWorkflowDefinition(_workflowId: string): Promise { if ( - !hasOwnProp(this._mockData, 'getWorkflowResponse') || - !isNonNullable(this._mockData.getWorkflowResponse) + !hasOwnProp(this._mockData, 'getWorkflowDefinitionResponse') || + !isNonNullable(this._mockData.getWorkflowDefinitionResponse) ) { - throw new Error(`[getWorkflow]: No mock data available`); + throw new Error(`[getWorkflowDefinition]: No mock data available`); } - return Promise.resolve(this._mockData.getWorkflowResponse); + return Promise.resolve(this._mockData.getWorkflowDefinitionResponse); } getWorkflowDataInputSchema(_args: { @@ -116,26 +115,15 @@ export class MockOrchestratorClient implements OrchestratorApi { return Promise.resolve(this._mockData.getWorkflowDataInputSchemaResponse); } - listWorkflows(): Promise { - if ( - !hasOwnProp(this._mockData, 'listWorkflowsResponse') || - !isNonNullable(this._mockData.listWorkflowsResponse) - ) { - throw new Error(`[listWorkflows]: No mock data available`); - } - - return Promise.resolve(this._mockData.listWorkflowsResponse); - } - - listWorkflowsOverview(): Promise { + listWorkflowOverviews(): Promise { if ( - !hasOwnProp(this._mockData, 'listWorkflowsOverviewResponse') || - !isNonNullable(this._mockData.listWorkflowsOverviewResponse) + !hasOwnProp(this._mockData, 'listWorkflowOverviewsResponse') || + !isNonNullable(this._mockData.listWorkflowOverviewsResponse) ) { - throw new Error(`[listWorkflowsOverview]: No mock data available`); + throw new Error(`[listWorkflowOverviews]: No mock data available`); } - return Promise.resolve(this._mockData.listWorkflowsOverviewResponse); + return Promise.resolve(this._mockData.listWorkflowOverviewsResponse); } getWorkflowOverview(): Promise { diff --git a/plugins/orchestrator/src/api/OrchestratorClient.ts b/plugins/orchestrator/src/api/OrchestratorClient.ts index 910f8011ec..ba9615f89d 100644 --- a/plugins/orchestrator/src/api/OrchestratorClient.ts +++ b/plugins/orchestrator/src/api/OrchestratorClient.ts @@ -4,16 +4,14 @@ import { JsonObject } from '@backstage/types'; import { AssessedProcessInstance, - Job, ProcessInstance, QUERY_PARAM_ASSESSMENT_INSTANCE_ID, QUERY_PARAM_BUSINESS_KEY, QUERY_PARAM_INCLUDE_ASSESSMENT, QUERY_PARAM_INSTANCE_ID, + WorkflowDefinition, WorkflowExecutionResponse, WorkflowInputSchemaResponse, - WorkflowItem, - WorkflowListResult, WorkflowOverview, WorkflowOverviewListResult, } from '@janus-idp/backstage-plugin-orchestrator-common'; @@ -74,7 +72,7 @@ export class OrchestratorClient implements OrchestratorApi { return await response.json(); } - async getWorkflow(workflowId: string): Promise { + async getWorkflowDefinition(workflowId: string): Promise { const baseUrl = await this.getBaseUrl(); const res = await fetch(`${baseUrl}/workflows/${workflowId}`); if (!res.ok) { @@ -83,16 +81,16 @@ export class OrchestratorClient implements OrchestratorApi { return await res.json(); } - async listWorkflows(): Promise { + async getWorkflowSource(workflowId: string): Promise { const baseUrl = await this.getBaseUrl(); - const res = await fetch(`${baseUrl}/workflows`); + const res = await fetch(`${baseUrl}/workflows/${workflowId}/source`); if (!res.ok) { throw await ResponseError.fromResponse(res); } - return await res.json(); + return await res.text(); } - async listWorkflowsOverview(): Promise { + async listWorkflowOverviews(): Promise { const baseUrl = await this.getBaseUrl(); const res = await fetch(`${baseUrl}/workflows/overview`); if (!res.ok) { @@ -101,7 +99,7 @@ export class OrchestratorClient implements OrchestratorApi { return res.json(); } - async getInstances(): Promise { + async listInstances(): Promise { const baseUrl = await this.getBaseUrl(); const res = await fetch(`${baseUrl}/instances`); if (!res.ok) { @@ -126,15 +124,6 @@ export class OrchestratorClient implements OrchestratorApi { return await res.json(); } - async getInstanceJobs(instanceId: string): Promise { - const baseUrl = await this.getBaseUrl(); - const res = await fetch(`${baseUrl}/instances/${instanceId}/jobs`); - if (!res.ok) { - throw await ResponseError.fromResponse(res); - } - return await res.json(); - } - async getWorkflowDataInputSchema(args: { workflowId: string; instanceId?: string; diff --git a/plugins/orchestrator/src/api/api.ts b/plugins/orchestrator/src/api/api.ts index 1f3835fa3c..b6d9be2ea6 100644 --- a/plugins/orchestrator/src/api/api.ts +++ b/plugins/orchestrator/src/api/api.ts @@ -3,12 +3,10 @@ import { JsonObject } from '@backstage/types'; import { AssessedProcessInstance, - Job, ProcessInstance, + WorkflowDefinition, WorkflowExecutionResponse, WorkflowInputSchemaResponse, - WorkflowItem, - WorkflowListResult, WorkflowOverview, WorkflowOverviewListResult, } from '@janus-idp/backstage-plugin-orchestrator-common'; @@ -22,21 +20,15 @@ export interface OrchestratorApi { businessKey?: string; }): Promise; - getWorkflow(workflowId: string): Promise; + getWorkflowDefinition(workflowId: string): Promise; - listWorkflows(): Promise; - - listWorkflowsOverview(): Promise; - - getInstances(): Promise; + getWorkflowSource(workflowId: string): Promise; getInstance( instanceId: string, includeAssessment: boolean, ): Promise; - getInstanceJobs(instanceId: string): Promise; - getWorkflowDataInputSchema(args: { workflowId: string; instanceId?: string; @@ -44,6 +36,10 @@ export interface OrchestratorApi { }): Promise; getWorkflowOverview(workflowId: string): Promise; + + listWorkflowOverviews(): Promise; + + listInstances(): Promise; } export const orchestratorApiRef = createApiRef({ diff --git a/plugins/orchestrator/src/components/ExecuteWorkflowPage/ExecuteWorkflowPage.stories.tsx b/plugins/orchestrator/src/components/ExecuteWorkflowPage/ExecuteWorkflowPage.stories.tsx index ac8ee65a20..99ed8b2628 100644 --- a/plugins/orchestrator/src/components/ExecuteWorkflowPage/ExecuteWorkflowPage.stories.tsx +++ b/plugins/orchestrator/src/components/ExecuteWorkflowPage/ExecuteWorkflowPage.stories.tsx @@ -9,8 +9,8 @@ import { WorkflowInputSchemaResponse } from '@janus-idp/backstage-plugin-orchest import { fakeDataInputSchemaDifferentTypes } from '../../__fixtures__/fakeWorkflowDataInputSchemaDifferentTypes'; import { fakeDataInputSchemaMultiStepResponse } from '../../__fixtures__/fakeWorkflowDataInputSchemaResponseMultiStep'; import { fakeDataInputSchemaMultiStepInitialStateResponse } from '../../__fixtures__/fakeWorkflowDataInputSchemaResponseMultiStepInitialState'; +import { fakeWorkflowDefinition } from '../../__fixtures__/fakeWorkflowDefinition'; import { fakeDataInputSchemaResponse } from '../../__fixtures__/fakeWorkflowInputSchemaResponse'; -import { fakeWorkflowItem } from '../../__fixtures__/fakeWorkflowItem'; import { orchestratorApiRef } from '../../api'; import { MockOrchestratorClient } from '../../api/MockOrchestratorClient'; import { orchestratorRootRouteRef } from '../../routes'; @@ -92,7 +92,7 @@ export const ExecuteWorkflowPageNoSchemaStory: Story = { name: 'No schema', args: { schemaResponse: () => ({ - workflowItem: fakeWorkflowItem, + workflowItem: fakeWorkflowDefinition, isComposedSchema: false, schemaSteps: [], }), diff --git a/plugins/orchestrator/src/components/ExecuteWorkflowPage/ExecuteWorkflowPage.tsx b/plugins/orchestrator/src/components/ExecuteWorkflowPage/ExecuteWorkflowPage.tsx index 9ce9d33292..ff5ddd6ebe 100644 --- a/plugins/orchestrator/src/components/ExecuteWorkflowPage/ExecuteWorkflowPage.tsx +++ b/plugins/orchestrator/src/components/ExecuteWorkflowPage/ExecuteWorkflowPage.tsx @@ -142,7 +142,7 @@ export const ExecuteWorkflowPage = () => { return ( diff --git a/plugins/orchestrator/src/components/OrchestratorPage.stories.tsx b/plugins/orchestrator/src/components/OrchestratorPage.stories.tsx index 3dd17cc728..9e8de17d33 100644 --- a/plugins/orchestrator/src/components/OrchestratorPage.stories.tsx +++ b/plugins/orchestrator/src/components/OrchestratorPage.stories.tsx @@ -8,7 +8,7 @@ import { Meta, StoryObj } from '@storybook/react'; import { WorkflowOverview } from '@janus-idp/backstage-plugin-orchestrator-common'; import { fakeProcessInstances } from '../__fixtures__/fakeProcessInstance'; -import { fakeWorkflowItem } from '../__fixtures__/fakeWorkflowItem'; +import { fakeWorkflowDefinition } from '../__fixtures__/fakeWorkflowDefinition'; import { fakeWorkflowOverviewList } from '../__fixtures__/fakeWorkflowOverviewList'; import { orchestratorApiRef } from '../api'; import { MockOrchestratorClient } from '../api/MockOrchestratorClient'; @@ -38,14 +38,14 @@ const meta = { ) => { const items = context.args.items || fakeWorkflowOverviewList; const mockApi = new MockOrchestratorClient({ - getInstancesResponse: Promise.resolve(fakeProcessInstances), - listWorkflowsOverviewResponse: Promise.resolve({ + listInstancesResponse: Promise.resolve(fakeProcessInstances), + listWorkflowOverviewsResponse: Promise.resolve({ limit: 0, offset: 0, totalCount: 0, items, }), - getWorkflowResponse: Promise.resolve(fakeWorkflowItem), + getWorkflowDefinitionResponse: Promise.resolve(fakeWorkflowDefinition), }); return wrapInTestApp( diff --git a/plugins/orchestrator/src/components/WorkflowDefinitionViewerPage/WorkflowDefinitionViewerPage.stories.tsx b/plugins/orchestrator/src/components/WorkflowDefinitionViewerPage/WorkflowDefinitionViewerPage.stories.tsx index d2ce9c8858..8e579528b9 100644 --- a/plugins/orchestrator/src/components/WorkflowDefinitionViewerPage/WorkflowDefinitionViewerPage.stories.tsx +++ b/plugins/orchestrator/src/components/WorkflowDefinitionViewerPage/WorkflowDefinitionViewerPage.stories.tsx @@ -7,7 +7,7 @@ import { Meta, StoryObj } from '@storybook/react'; import { WorkflowOverview } from '@janus-idp/backstage-plugin-orchestrator-common'; -import { fakeWorkflowItem } from '../../__fixtures__/fakeWorkflowItem'; +import { fakeWorkflowDefinition } from '../../__fixtures__/fakeWorkflowDefinition'; import { fakeWorkflowOverview } from '../../__fixtures__/fakeWorkflowOverview'; import { veryLongString } from '../../__fixtures__/veryLongString'; import { orchestratorApiRef } from '../../api'; @@ -36,7 +36,7 @@ const meta = { getWorkflowOverviewResponse: Promise.resolve( context.args.workflowOverview || fakeWorkflowOverview, ), - getWorkflowResponse: Promise.resolve(fakeWorkflowItem), + getWorkflowDefinitionResponse: Promise.resolve(fakeWorkflowDefinition), }); return wrapInTestApp( {}), - getWorkflowResponse: new Promise(() => {}), + getWorkflowDefinitionResponse: new Promise(() => {}), }), }, }; diff --git a/plugins/orchestrator/src/components/WorkflowEditor/WorkflowEditor.tsx b/plugins/orchestrator/src/components/WorkflowEditor/WorkflowEditor.tsx index 348ab54fda..1b8fcebd64 100644 --- a/plugins/orchestrator/src/components/WorkflowEditor/WorkflowEditor.tsx +++ b/plugins/orchestrator/src/components/WorkflowEditor/WorkflowEditor.tsx @@ -38,11 +38,12 @@ import { Diagnostic, DiagnosticSeverity } from 'vscode-languageserver-types'; import { DEFAULT_EDITOR_PATH, - extractWorkflowFormatFromUri, + extractWorkflowFormat, + fromWorkflowSource, ProcessInstance, toWorkflowString, + WorkflowDefinition, WorkflowFormat, - WorkflowItem, } from '@janus-idp/backstage-plugin-orchestrator-common'; import { orchestratorApiRef } from '../../api'; @@ -59,7 +60,7 @@ export enum EditorViewKind { export interface WorkflowEditorRef { validate: () => Promise; getContent: () => Promise; - workflowItem: WorkflowItem | undefined; + workflowDefinition: WorkflowDefinition | undefined; isReady: boolean; } @@ -88,8 +89,8 @@ const RefForwardingWorkflowEditor: ForwardRefRenderFunction< const { workflowId, kind, format, editorMode = 'full' } = props; const { editor, editorRef } = useEditorRef(); const [embeddedFile, setEmbeddedFile] = useState(); - const [workflowItemPromise, setWorkflowItemPromise] = - usePromiseState(); + const [workflowDefinitionPromise, setWorkflowDefinitionPromise] = + usePromiseState(); const [canRender, setCanRender] = useState(false); const [ready, setReady] = useState(false); const navigate = useNavigate(); @@ -203,11 +204,11 @@ const RefForwardingWorkflowEditor: ForwardRefRenderFunction< return { validate, getContent, - workflowItem: workflowItemPromise.data, + workflowDefinition: workflowDefinitionPromise.data, isReady: ready, }; }, - [validate, getContent, workflowItemPromise.data, ready], + [validate, getContent, workflowDefinitionPromise.data, ready], ); useCancelableEffect( @@ -216,23 +217,20 @@ const RefForwardingWorkflowEditor: ForwardRefRenderFunction< setCanRender(false); orchestratorApi - .getWorkflow(workflowId) - .then(item => { + .getWorkflowSource(workflowId) + .then(source => { if (canceled.get()) { return; } - setWorkflowItemPromise({ data: item }); + const definition = fromWorkflowSource(source); + setWorkflowDefinitionPromise({ data: definition }); - const workflowFormat = extractWorkflowFormatFromUri(item.uri); - const uriParts = item.uri.split('/'); - const fileName = uriParts[uriParts.length - 1]; - const fileNameParts = fileName.split('.'); - const fileExtension = fileNameParts[fileNameParts.length - 1]; + const workflowFormat = extractWorkflowFormat(source); if (format && workflowId && format !== workflowFormat) { const link = viewWorkflowLink({ workflowId: workflowId, - format: fileExtension, + format: workflowFormat, }); navigate(link, { replace: true }); @@ -240,25 +238,26 @@ const RefForwardingWorkflowEditor: ForwardRefRenderFunction< return; } + const filename = `workflow.sw.${workflowFormat}`; setEmbeddedFile({ - path: item.uri, + path: filename, getFileContents: async () => - toWorkflowString(item.definition, workflowFormat), + toWorkflowString(definition, workflowFormat), isReadOnly: true, - fileExtension, - fileName, + fileExtension: workflowFormat, + fileName: filename, }); setCanRender(true); }) .catch(e => { - setWorkflowItemPromise({ error: e }); + setWorkflowDefinitionPromise({ error: e }); }); }, [ orchestratorApi, workflowId, - setWorkflowItemPromise, + setWorkflowDefinitionPromise, format, viewWorkflowLink, navigate, @@ -269,12 +268,12 @@ const RefForwardingWorkflowEditor: ForwardRefRenderFunction< const embeddedEditorWrapper = useMemo( () => ( + promise={workflowDefinitionPromise} + resolved={workflowDefinition => canRender && embeddedFile && ( => { +): Promise => { if (workflowId === '__loading__') { await delay(5 * 1000); - return fakeWorkflowItem; + return fakeWorkflowDefinition; } - return fakeWorkflowItem; + return fakeWorkflowDefinition; }; const meta = { @@ -90,7 +90,7 @@ const meta = { [ orchestratorApiRef, new MockOrchestratorClient({ - getWorkflowResponse: getFakeWorkflowItem( + getWorkflowDefinitionResponse: getFakeWorkflowDefinition( context.args.instanceId, ), getInstanceResponse: () => { diff --git a/plugins/orchestrator/src/components/WorkflowRunsTabContent.stories.tsx b/plugins/orchestrator/src/components/WorkflowRunsTabContent.stories.tsx index 57b7b7fc4a..31f2e368a2 100644 --- a/plugins/orchestrator/src/components/WorkflowRunsTabContent.stories.tsx +++ b/plugins/orchestrator/src/components/WorkflowRunsTabContent.stories.tsx @@ -17,12 +17,12 @@ const meta = { decorators: [ (Story, context) => { const api = new MockOrchestratorClient({ - getInstancesResponse: Promise.resolve( + listInstancesResponse: Promise.resolve( generateFakeProcessInstances( (context.args as { length: number }).length, ), ), - listWorkflowsOverviewResponse: Promise.resolve({ + listWorkflowOverviewsResponse: Promise.resolve({ limit: 0, offset: 0, totalCount: 1, diff --git a/plugins/orchestrator/src/components/WorkflowRunsTabContent.tsx b/plugins/orchestrator/src/components/WorkflowRunsTabContent.tsx index fc52f78a8b..4e7554a3ff 100644 --- a/plugins/orchestrator/src/components/WorkflowRunsTabContent.tsx +++ b/plugins/orchestrator/src/components/WorkflowRunsTabContent.tsx @@ -49,7 +49,7 @@ export const WorkflowRunsTabContent = () => { ); const fetchInstances = React.useCallback(async () => { - const instances = await orchestratorApi.getInstances(); + const instances = await orchestratorApi.listInstances(); const clonedData: WorkflowRunDetail[] = instances.map( mapProcessInstanceToDetails, ); diff --git a/plugins/orchestrator/src/components/WorkflowsTabContent.tsx b/plugins/orchestrator/src/components/WorkflowsTabContent.tsx index 0058777968..9986c8e175 100644 --- a/plugins/orchestrator/src/components/WorkflowsTabContent.tsx +++ b/plugins/orchestrator/src/components/WorkflowsTabContent.tsx @@ -21,7 +21,7 @@ export const WorkflowsTabContent = () => { const { value, error, loading } = useAsync(async (): Promise< WorkflowOverview[] > => { - const data = await orchestratorApi.listWorkflowsOverview(); + const data = await orchestratorApi.listWorkflowOverviews(); return data.items; }, []); diff --git a/plugins/orchestrator/src/components/WorkflowsTable.stories.tsx b/plugins/orchestrator/src/components/WorkflowsTable.stories.tsx index 6e4c628b25..a5adbb5d60 100644 --- a/plugins/orchestrator/src/components/WorkflowsTable.stories.tsx +++ b/plugins/orchestrator/src/components/WorkflowsTable.stories.tsx @@ -23,7 +23,7 @@ const meta = { [ orchestratorApiRef, new MockOrchestratorClient({ - listWorkflowsOverviewResponse: Promise.resolve({ + listWorkflowOverviewsResponse: Promise.resolve({ limit: 0, offset: 0, totalCount: 1, diff --git a/plugins/orchestrator/src/dataFormatters/WorkflowOverviewFormatter.test.ts b/plugins/orchestrator/src/dataFormatters/WorkflowOverviewFormatter.test.ts index ac693dee77..b5b30cb419 100644 --- a/plugins/orchestrator/src/dataFormatters/WorkflowOverviewFormatter.test.ts +++ b/plugins/orchestrator/src/dataFormatters/WorkflowOverviewFormatter.test.ts @@ -15,7 +15,7 @@ describe('WorkflowOverviewAdapter', () => { category: 'Sample Category', avgDurationMs: 150000, description: 'Sample description', - uri: 'sample.workflow.sw.yaml', + format: 'yaml', }; const adaptedData: FormattedWorkflowOverview = @@ -37,6 +37,7 @@ describe('WorkflowOverviewAdapter', () => { // Mock data for testing const mockWorkflowOverview: WorkflowOverview = { workflowId: '123', + format: 'yaml', }; const adaptedData: FormattedWorkflowOverview = WorkflowOverviewFormatter.format(mockWorkflowOverview); diff --git a/plugins/orchestrator/src/dataFormatters/WorkflowOverviewFormatter.ts b/plugins/orchestrator/src/dataFormatters/WorkflowOverviewFormatter.ts index 4d93a81290..9943cd3cff 100644 --- a/plugins/orchestrator/src/dataFormatters/WorkflowOverviewFormatter.ts +++ b/plugins/orchestrator/src/dataFormatters/WorkflowOverviewFormatter.ts @@ -1,7 +1,6 @@ import moment from 'moment'; import { - extractWorkflowFormatFromUri, WorkflowFormat, WorkflowOverview, } from '@janus-idp/backstage-plugin-orchestrator-common'; @@ -30,17 +29,17 @@ const WorkflowOverviewFormatter: DataFormatter< format: (data: WorkflowOverview): FormattedWorkflowOverview => { return { id: data.workflowId, - name: data.name || VALUE_UNAVAILABLE, + name: data.name ?? VALUE_UNAVAILABLE, lastTriggered: data.lastTriggeredMs ? moment(data.lastTriggeredMs).toDate().toLocaleString() : VALUE_UNAVAILABLE, - lastRunStatus: data.lastRunStatus || VALUE_UNAVAILABLE, - category: data.category || VALUE_UNAVAILABLE, + lastRunStatus: data.lastRunStatus ?? VALUE_UNAVAILABLE, + category: data.category ?? VALUE_UNAVAILABLE, avgDuration: data.avgDurationMs ? formatDuration(data.avgDurationMs) : VALUE_UNAVAILABLE, - description: data.description || VALUE_UNAVAILABLE, - format: data.uri ? extractWorkflowFormatFromUri(data.uri) : 'yaml', + description: data.description ?? VALUE_UNAVAILABLE, + format: data.format, }; }, };