From 93eddd1ee702f0b1e3289514586687934639a6f6 Mon Sep 17 00:00:00 2001 From: Paul Cowan Date: Wed, 22 Mar 2023 20:47:18 +0000 Subject: [PATCH 1/7] refactor to new API --- plugins/parodos/README.md | 10 +- .../src/components/errors/ErrorMessage.tsx | 1 - .../workflow/onboarding/Onboarding.tsx | 18 +- .../workflow/useGetProjectAssessmentSchema.ts | 35 ++- .../jsonSchemaFromWorkflowDefinition.ts | 69 ++---- .../mocks/workflowDefinitions/andromeda.ts | 97 ++++---- .../workflowDefinitions/deepRecursiveWorks.ts | 223 ++++++------------ .../workflowDefinitions/recursiveWorks.ts | 63 +++-- .../src/models/workflowDefinitionSchema.ts | 50 ++-- plugins/parodos/src/routes.ts | 36 --- plugins/parodos/src/setupTests.ts | 6 + 11 files changed, 244 insertions(+), 364 deletions(-) diff --git a/plugins/parodos/README.md b/plugins/parodos/README.md index b900a813b1..e7a1308161 100644 --- a/plugins/parodos/README.md +++ b/plugins/parodos/README.md @@ -58,6 +58,14 @@ For local development set the `PARODOS_AUTH_KEY` environment variable to 'Basic ```yaml proxy: '/parodos': + target: 'http://localhost:8080/api/v1' + changeOrigin: true + redirect: follow + cache: 'no-cache' headers: - Authorization: 'Basic dGVzdDp0ZXN0' + Content-Type: 'application/json' + accept: 'application/json' + Authorization: ${PARODOS_AUTH_KEY} ``` + +Running `yarn workspace @janus-idp/backstage-plugin-parodos dev` from the root will start both the backend proxy server and the frontend development server. diff --git a/plugins/parodos/src/components/errors/ErrorMessage.tsx b/plugins/parodos/src/components/errors/ErrorMessage.tsx index a789a5fbd4..5cf5c497da 100644 --- a/plugins/parodos/src/components/errors/ErrorMessage.tsx +++ b/plugins/parodos/src/components/errors/ErrorMessage.tsx @@ -12,7 +12,6 @@ export function ErrorMessage({ error }: ErrorPanelProps): JSX.Element | null { return; } - // simple error message to user errorApi.post(new Error('An error has occurred')); }, [errorApi]); diff --git a/plugins/parodos/src/components/workflow/onboarding/Onboarding.tsx b/plugins/parodos/src/components/workflow/onboarding/Onboarding.tsx index bab1a1fcb3..2eec805c57 100644 --- a/plugins/parodos/src/components/workflow/onboarding/Onboarding.tsx +++ b/plugins/parodos/src/components/workflow/onboarding/Onboarding.tsx @@ -73,18 +73,18 @@ export function Onboarding({ isNew }: OnboardingProps): JSX.Element { const payload = { projectId, workFlowName: workflow.name, - workFlowTasks: workflow.works.map(work => { + // TODO: top level parameters + // arguments: Object.entries(workflow.parameters ?? {}).map(([key]) => { + + // }), + works: workflow.works.map(work => { return { - name: work.name, - arguments: work.parameters?.map(param => { - const value = lodashGet( - formData, - `${work.name}.${param.key}`, - null, - ); + workName: work.name, + arguments: Object.entries(work.parameters ?? {}).map(([key]) => { + const value = lodashGet(formData, `${work.name}.${key}`, null); return { - key: param.key, + key: key, value: value, }; }), diff --git a/plugins/parodos/src/components/workflow/useGetProjectAssessmentSchema.ts b/plugins/parodos/src/components/workflow/useGetProjectAssessmentSchema.ts index eab4aa2682..a1e6de50fa 100644 --- a/plugins/parodos/src/components/workflow/useGetProjectAssessmentSchema.ts +++ b/plugins/parodos/src/components/workflow/useGetProjectAssessmentSchema.ts @@ -14,9 +14,8 @@ interface Props { } const newProjectChoice: WorkFlowTaskParameter = { - key: 'newProject', - type: 'BOOLEAN', - optional: true, + type: 'boolean', + required: true, default: true, }; @@ -30,30 +29,30 @@ export function useGetProjectAssessmentSchema({ const cloned = JSON.parse(JSON.stringify(definition)) as WorkflowDefinition; + cloned.works[0].parameters = cloned.works[0].parameters ?? {}; + if (newProject) { - cloned.works[0].parameters?.unshift({ - key: 'Name', + cloned.works[0].parameters.Name = { description: 'New Project', - optional: false, - type: 'TEXT', - }); + required: false, + format: 'text', + type: 'string', + }; - cloned.works[0].parameters?.unshift(newProjectChoice); + cloned.works[0].parameters.newProject = { ...newProjectChoice }; } else { - cloned.works[0].parameters = []; - - cloned.works[0].parameters?.unshift({ - key: 'project', - optional: false, - type: 'TEXT', + cloned.works[0].parameters.project = { + required: false, + type: 'string', + format: 'text', field: 'ProjectPicker', disabled: !hasProjects, - }); + }; - cloned.works[0].parameters?.unshift({ + cloned.works[0].parameters.newProject = { ...newProjectChoice, description: 'Search for an existing project to execute a new workflow:', - }); + }; } const formSchema = jsonSchemaFromWorkflowDefinition(cloned); diff --git a/plugins/parodos/src/hooks/useWorkflowDefinitionToJsonSchema/jsonSchemaFromWorkflowDefinition.ts b/plugins/parodos/src/hooks/useWorkflowDefinitionToJsonSchema/jsonSchemaFromWorkflowDefinition.ts index 707261ad98..3b43f5d5bc 100644 --- a/plugins/parodos/src/hooks/useWorkflowDefinitionToJsonSchema/jsonSchemaFromWorkflowDefinition.ts +++ b/plugins/parodos/src/hooks/useWorkflowDefinitionToJsonSchema/jsonSchemaFromWorkflowDefinition.ts @@ -1,5 +1,5 @@ import { - type WorkFlowTaskParameterType, + type ParameterFormat, type WorkflowDefinition, type WorkType, } from '../../models/workflowDefinitionSchema'; @@ -8,39 +8,37 @@ import set from 'lodash.set'; import get from 'lodash.get'; import { taskDisplayName } from '../../utils/string'; -export function getJsonSchemaType(type: WorkFlowTaskParameterType) { +export function getJsonSchemaType(type: ParameterFormat) { switch (type) { - case 'PASSWORD': - case 'TEXT': + case 'password': + case 'text': return { type: 'string', }; - case 'NUMBER': + case 'number': return { type: 'number', }; - case 'DATE': + case 'date': return { type: 'string', format: 'date', }; - case 'EMAIL': + case 'email': return { type: 'string', format: 'email', }; - case 'URL': + case 'url': return { type: 'string', pattern: '^(https?)://', // TODO: better regex }; - case 'BOOLEAN': { + case 'boolean': { return { type: 'boolean', }; } - case 'MOCK-SELECT': - return {}; default: return { type: 'string', @@ -48,29 +46,29 @@ export function getJsonSchemaType(type: WorkFlowTaskParameterType) { } } -export function getUiSchema(type: WorkFlowTaskParameterType) { +export function getUiSchema(type: ParameterFormat) { switch (type) { - case 'PASSWORD': + case 'password': return { 'ui:emptyValue': undefined, 'ui:widget': 'password', }; - case 'TEXT': + case 'text': return { 'ui:emptyValue': undefined, }; - case 'EMAIL': + case 'email': return { 'ui:widget': 'email', }; - case 'BOOLEAN': { + case 'boolean': { return { // TODO: what if needs to be a checkbox list? 'ui:widget': 'radio', }; } - case 'URL': - case 'NUMBER': + case 'url': + case 'number': return {}; default: return {}; @@ -101,41 +99,22 @@ function transformWorkToStep(work: WorkType): Step { 'ui:hidden': true, }; - for (const { + for (const [ key, - type, - description, - optional, - default: fieldDefault, - field, - options = [], - } of work.parameters ?? []) { + { description, type, required, format, default: fieldDefault, field }, + ] of Object.entries(work.parameters ?? {})) { const propertiesPath = `properties.${work.name}.properties.${key}`; - const required = !optional; set(schema, propertiesPath, { title: `${key}`, - ...getJsonSchemaType(type), + ...getJsonSchemaType(format ?? (type as ParameterFormat)), ...{ default: fieldDefault }, }); - if (options && options.length > 0) { - set( - schema, - `${propertiesPath}.enum`, - options.map(option => option.key), - ); - set( - schema, - `${propertiesPath}.enumNames`, - options.map(option => option.value), - ); - } - const objectPath = `${work.name}.${key}`; set(uiSchema, objectPath, { - ...getUiSchema(type), + ...getUiSchema(format ?? (type as ParameterFormat)), 'ui:field': field, 'ui:help': description, 'ui:autocomplete': 'Off', @@ -186,12 +165,12 @@ export function jsonSchemaFromWorkflowDefinition( steps: [], }; - const parameters = workflowDefinition.parameters ?? []; + const parameters = workflowDefinition.parameters ?? {}; - if (parameters.length > 0) { + if (Object.keys(parameters).length > 0) { const step = transformWorkToStep({ name: workflowDefinition.name, - parameters: [...parameters], + parameters, } as WorkType); result.steps.push(step); diff --git a/plugins/parodos/src/mocks/workflowDefinitions/andromeda.ts b/plugins/parodos/src/mocks/workflowDefinitions/andromeda.ts index 679d4af012..533ec28c2e 100644 --- a/plugins/parodos/src/mocks/workflowDefinitions/andromeda.ts +++ b/plugins/parodos/src/mocks/workflowDefinitions/andromeda.ts @@ -15,80 +15,79 @@ export const mockAndromedaWorkflowDefinition: WorkflowDefinition = { { id: 'c7ba1d55-82e0-4037-9549-26a73fe40599', name: 'adGroupWorkFlowTask', - parameters: [ - { - key: 'api-server', + parameters: { + ['api-server']: { description: 'The api server', - optional: false, - type: 'URL', + required: false, + type: 'string', + format: 'url', }, - ], + }, workType: 'TASK', outputs: ['OTHER'], }, { id: 'mock-task-1', name: 'mock-task-1', - parameters: [ - { - key: 'param1', + parameters: { + param1: { description: 'Some text only.', - optional: false, - type: 'TEXT', + required: false, + type: 'string', + format: 'text', }, - ], + }, workType: 'TASK', outputs: ['OTHER'], }, ], }; -export const mockWorkflowParams: WorkFlowTaskParameter[] = [ - { - key: 'param1', +export const mockWorkflowParams: Record = { + param1: { description: 'Some text only.', - optional: false, - type: 'TEXT', + required: false, + type: 'string', + format: 'text', }, - { - key: 'param2', + param2: { description: 'An URL parameter', - optional: true, - type: 'URL', + required: true, + type: 'string', + format: 'url', }, - { - key: 'param3', + param3: { description: 'Date parameter type.', - optional: false, - type: 'DATE', + required: false, + type: 'string', + format: 'date', }, - { - key: 'param4', + param4: { description: 'Email parameter', - optional: true, - type: 'EMAIL', + required: true, + format: 'email', + type: 'string', }, - { - key: 'param5', + param5: { description: 'Numeric parameter', - optional: false, - type: 'NUMBER', + required: false, + type: 'number', }, - { - key: 'param6', + param6: { description: 'Password parameter', - optional: true, - type: 'PASSWORD', + required: true, + type: 'string', + format: 'password', }, - { - // TODO: swagger is missing this type - key: 'param7', - description: 'Select parameter', - optional: false, - type: 'MOCK-SELECT', - options: [ - { key: 'option1', value: 'Option 1' }, - { key: 'option2', value: 'Option 2' }, - ], - }, -]; + // { + // // TODO: not sure about this + // key: 'param7', + // description: 'Select parameter', + // required: false, + // type: 'MOCK-SELECT', + // options: [ + // { key: 'option1', value: 'Option 1' }, + // { key: 'option2', value: 'Option 2' }, + // ], + // }, +}; diff --git a/plugins/parodos/src/mocks/workflowDefinitions/deepRecursiveWorks.ts b/plugins/parodos/src/mocks/workflowDefinitions/deepRecursiveWorks.ts index 8d102d4914..f1441edaba 100644 --- a/plugins/parodos/src/mocks/workflowDefinitions/deepRecursiveWorks.ts +++ b/plugins/parodos/src/mocks/workflowDefinitions/deepRecursiveWorks.ts @@ -1,179 +1,100 @@ import { type WorkflowDefinition } from '../../models/workflowDefinitionSchema'; export const mockDeepRecursiveWorks: WorkflowDefinition = { - id: '5ba64c7c-5f3a-47cc-9237-ed82985f9860', - name: 'masterWorkFlow', + id: '527da27e-f461-4fac-8618-8d667c89166a', + name: 'subWorkFlowThree', type: 'INFRASTRUCTURE', - processingType: 'SEQUENTIAL', + processingType: 'PARALLEL', author: null, - createDate: '2023-03-17T15:53:08.496+00:00', - modifyDate: '2023-03-17T15:53:08.497+00:00', - parameters: [ - { - key: 'workloadId', - description: 'The workload id', - optional: false, - type: 'TEXT', - }, + createDate: '2023-03-22T16:16:01.434+00:00', + modifyDate: '2023-03-22T16:16:01.435+00:00', + works: [ { - key: 'projectUrl', - description: 'The project url', - optional: true, - type: 'URL', + id: '30897703-0d9a-45a4-97b4-66cf95c122de', + name: 'sslCertificationWorkFlowTask', + workType: 'TASK', + parameters: { + domainName: { + format: 'url', + description: 'The domain name', + type: 'string', + required: true, + }, + ipAddress: { + format: 'text', + description: 'The api address', + type: 'string', + required: true, + }, + }, + outputs: ['HTTP2XX'], }, - ], - works: [ { - id: 'cf549b50-db51-44cb-8c27-cb0cf820e06e', - name: 'subWorkFlowThree', + id: '772cfa8b-da7d-49af-8f8c-0abc95fd086b', + name: 'subWorkFlowTwo', workType: 'WORKFLOW', - processingType: 'PARALLEL', + processingType: 'SEQUENTIAL', works: [ { - id: '121eb0c7-6b38-4142-8dd4-5ef62d876d95', - name: 'sslCertificationWorkFlowTask', - parameters: [ - { - key: 'domainName', - description: 'The domain name', - optional: false, - type: 'URL', - }, - { - key: 'ipAddress', - description: 'The api address', - optional: false, - type: 'TEXT', - }, - ], - workType: 'TASK', - outputs: ['HTTP2XX'], - }, - { - id: 'ac71fad0-1a71-452c-92ea-1c05dca66263', - name: 'subWorkFlowTwo', + id: '8c5659e6-6776-427e-bf1e-daede1f764dc', + name: 'subWorkFlowOne', workType: 'WORKFLOW', - processingType: 'SEQUENTIAL', + processingType: 'PARALLEL', works: [ { - id: '5c0f8071-860a-4433-afa4-7c58bdaeb71f', - name: 'subWorkFlowOne', - parameters: [ - { - key: 'comment', - description: 'The workflow comment', - optional: false, - type: 'TEXT', - }, - ], - workType: 'WORKFLOW', - processingType: 'PARALLEL', - works: [ - { - id: 'dfa1da17-9bc2-4be2-9703-06bce8168bd3', - name: 'adGroupsWorkFlowTask', - parameters: [ - { - key: 'adGroups', - description: 'The ad groups', - optional: false, - type: 'TEXT', - }, - { - key: 'userId', - description: 'The user id', - optional: false, - type: 'TEXT', - }, - ], - workType: 'TASK', - outputs: ['HTTP2XX', 'EXCEPTION'], + id: '5d596e07-e245-46e6-98fa-1a98143c68e0', + name: 'adGroupsWorkFlowTask', + workType: 'TASK', + parameters: { + adGroups: { + format: 'text', + description: 'The ad groups', + type: 'string', + required: true, }, - { - id: '088b87b3-5f81-4411-88c1-053be4aa3955', - name: 'splunkMonitoringWorkFlowTask', - parameters: [ - { - key: 'clusterName', - description: 'The cluster name', - optional: false, - type: 'TEXT', - }, - { - key: 'hostname', - description: 'The hostname', - optional: false, - type: 'TEXT', - }, - ], - workType: 'TASK', - outputs: ['OTHER'], + userId: { + format: 'text', + description: 'The user id', + type: 'string', + required: true, }, - ], + }, + outputs: ['HTTP2XX', 'EXCEPTION'], }, { - id: 'b17a9260-3846-4b25-a661-11955de1a1ce', - name: 'namespaceWorkFlowTask', - parameters: [ - { - key: 'projectId', - description: 'The project id', - optional: false, - type: 'NUMBER', - }, - ], + id: '397b452a-9833-455a-a22d-6bc985b8cd52', + name: 'splunkMonitoringWorkFlowTask', workType: 'TASK', - outputs: ['HTTP2XX'], + parameters: { + hostname: { + format: 'text', + description: 'The hostname', + type: 'string', + required: true, + }, + clusterName: { + format: 'text', + description: 'The cluster name', + type: 'string', + required: true, + }, + }, + outputs: ['OTHER'], }, ], }, - ], - }, - { - id: '6b38a6e5-f4d2-4f98-ac40-6bbfec16a1dc', - name: 'subWorkFlowFour', - workType: 'WORKFLOW', - processingType: 'PARALLEL', - works: [ { - id: '2f721120-7412-40ab-a2ec-bb6d915ed7ea', - name: 'loadBalancerWorkFlowTask', - parameters: [ - { - key: 'hostname', - description: 'The hostname', - optional: false, - type: 'URL', - }, - { - key: 'appId', - description: 'The app id', - optional: false, - type: 'TEXT', - }, - ], + id: '3616aeda-1daf-4cce-bccc-f0857bff8482', + name: 'namespaceWorkFlowTask', workType: 'TASK', - outputs: ['HTTP2XX'], - }, - { - id: '4743c53d-bbdc-4c5d-9141-f50033127d5d', - name: 'singleSignOnWorkFlowTask', - parameters: [ - { - key: 'userId', - description: 'The user id', - optional: false, - type: 'TEXT', + parameters: { + projectId: { + description: 'The project id', + type: 'number', + required: true, }, - { - key: 'password', - description: 'The password', - optional: false, - type: 'PASSWORD', - }, - ], - workType: 'TASK', - outputs: ['OTHER'], + }, + outputs: ['HTTP2XX'], }, ], }, diff --git a/plugins/parodos/src/mocks/workflowDefinitions/recursiveWorks.ts b/plugins/parodos/src/mocks/workflowDefinitions/recursiveWorks.ts index 1cfa300148..89c57bce3c 100644 --- a/plugins/parodos/src/mocks/workflowDefinitions/recursiveWorks.ts +++ b/plugins/parodos/src/mocks/workflowDefinitions/recursiveWorks.ts @@ -19,64 +19,63 @@ export const mockRecursiveWorksWorkflowDefinition: WorkflowDefinition = { id: '684d5cc0-3da7-4b18-9712-a60622748c5a', name: 'adGroupsWorkFlowTask', workType: 'TASK', - parameters: [ - { + parameters: { + adGroups: { description: 'The ad groups', - optional: false, - type: 'TEXT', - key: 'adGroups', + required: false, + type: 'string', + format: 'text', }, - { + userId: { description: 'The user id', - optional: false, - type: 'TEXT', - key: 'userId', + required: false, + type: 'string', + format: 'text', }, - ], + }, outputs: ['HTTP2XX', 'EXCEPTION'], }, { id: '09d93c82-8865-45fe-9348-bd6ef5b9aeb3', name: 'splunkMonitoringWorkFlowTask', workType: 'TASK', - parameters: [ - { + parameters: { + clusterName: { description: 'The cluster name', - optional: false, - type: 'TEXT', - key: 'clusterName', + required: false, + type: 'string', + format: 'text', }, - { + hostname: { description: 'The hostname', - optional: false, - type: 'TEXT', - key: 'hostname', + required: false, + type: 'string', + format: 'text', }, - ], + }, outputs: ['OTHER'], }, ], - parameters: [ - { - key: 'comment', - type: 'TEXT', + parameters: { + comment: { + type: 'string', + format: 'text', description: 'The workflow comment', - optional: false, + required: false, }, - ], + }, }, { id: 'e8d23ee9-8406-423c-beb7-c4e4f3ba0a21', name: 'namespaceWorkFlowTask', workType: 'TASK', - parameters: [ - { + parameters: { + projectId: { description: 'The project id', - optional: false, - type: 'NUMBER', - key: 'projectId', + required: false, + type: 'number', }, - ], + }, outputs: ['HTTP2XX'], }, ], diff --git a/plugins/parodos/src/models/workflowDefinitionSchema.ts b/plugins/parodos/src/models/workflowDefinitionSchema.ts index 7fceb3f775..9e1c33791a 100644 --- a/plugins/parodos/src/models/workflowDefinitionSchema.ts +++ b/plugins/parodos/src/models/workflowDefinitionSchema.ts @@ -1,14 +1,19 @@ import { z } from 'zod'; const parameterTypes = z.union([ - z.literal('PASSWORD'), - z.literal('TEXT'), - z.literal('EMAIL'), - z.literal('DATE'), - z.literal('NUMBER'), - z.literal('MOCK-SELECT'), - z.literal('URL'), - z.literal('BOOLEAN'), + z.literal('string'), + z.literal('number'), + z.literal('boolean'), +]); + +const parameterFormat = z.union([ + z.literal('password'), + z.literal('text'), + z.literal('email'), + z.literal('date'), + z.literal('number'), + z.literal('url'), + z.literal('boolean'), ]); const processingType = z.union([ @@ -17,19 +22,12 @@ const processingType = z.union([ ]); export const workFlowTaskParameterTypeSchema = z.object({ - key: z.string(), description: z.string().optional(), - optional: z.boolean(), + required: z.coerce.boolean(), type: parameterTypes, - options: z - .array( - z.object({ - key: z.string(), - value: z.string(), - }), - ) - .optional() - .nullable(), + format: parameterFormat.optional(), + minLength: z.number().optional(), + maxLength: z.number().optional(), default: z.any().optional(), field: z.string().optional(), disabled: z.boolean().default(false).optional(), @@ -38,8 +36,11 @@ export const workFlowTaskParameterTypeSchema = z.object({ export const baseWorkSchema = z.object({ id: z.string(), name: z.string(), - parameters: z.array(workFlowTaskParameterTypeSchema).optional().nullable(), - workType: z.string(), // TODO: could this be a union? + parameters: z + .record(z.string(), workFlowTaskParameterTypeSchema) + .optional() + .nullable(), + workType: z.union([z.literal('TASK'), z.literal('WORKFLOW')]), // TODO: could this be a union? processingType: processingType.optional(), author: z.string().optional().nullable(), outputs: z @@ -70,7 +71,10 @@ export const workflowDefinitionSchema = z.object({ author: z.string().optional().nullable(), createDate: z.string(), modifyDate: z.string(), - parameters: z.array(workFlowTaskParameterTypeSchema).optional(), + parameters: z + .record(z.string(), workFlowTaskParameterTypeSchema) + .optional() + .nullable(), works: z.array(workSchema), }); @@ -81,3 +85,5 @@ export type WorkFlowTaskParameter = z.infer< >; export type WorkFlowTaskParameterType = WorkFlowTaskParameter['type']; + +export type ParameterFormat = WorkFlowTaskParameter['format']; diff --git a/plugins/parodos/src/routes.ts b/plugins/parodos/src/routes.ts index 17aab3ca37..1b6adf51d0 100644 --- a/plugins/parodos/src/routes.ts +++ b/plugins/parodos/src/routes.ts @@ -3,39 +3,3 @@ import { createRouteRef } from '@backstage/core-plugin-api'; export const rootRouteRef = createRouteRef({ id: 'parodos', }); - -// export const projectOverviewRouteRef = createSubRouteRef({ -// id: 'project-overview', -// parent: rootRouteRef, -// path: '/project-overview', -// }); - -// export const workflowRouteRef = createSubRouteRef({ -// id: 'workflow', -// parent: rootRouteRef, -// path: '/workflow', -// }); - -// export const deployRouteRef = createSubRouteRef({ -// id: 'deploy', -// parent: rootRouteRef, -// path: '/deploy', -// }); - -// export const notificationRouteRef = createSubRouteRef({ -// id: 'notification', -// parent: rootRouteRef, -// path: '/notification', -// }); - -// export const trainingRouteRef = createSubRouteRef({ -// id: 'training', -// parent: rootRouteRef, -// path: '/training', -// }); - -// export const metricsRouteRef = createSubRouteRef({ -// id: 'metrics', -// parent: rootRouteRef, -// path: '/metrics', -// }); diff --git a/plugins/parodos/src/setupTests.ts b/plugins/parodos/src/setupTests.ts index 48c09b5346..945c81e571 100644 --- a/plugins/parodos/src/setupTests.ts +++ b/plugins/parodos/src/setupTests.ts @@ -1,2 +1,8 @@ import '@testing-library/jest-dom'; import 'cross-fetch/polyfill'; + +jest.mock('@material-ui/core/styles', () => ({ + createStyles: () => () => ({}), + makeStyles: () => () => ({}), + withStyles: () => () => ({}), +})); From d3d341bc452ec74abfd4367b37b3d968c6cc4bc4 Mon Sep 17 00:00:00 2001 From: Paul Cowan Date: Thu, 23 Mar 2023 07:57:50 +0000 Subject: [PATCH 2/7] refactor to new API --- .../components/ParodosPage/ParodosPage.tsx | 13 ++++---- .../src/components/ParodosPage/TabLabel.tsx | 30 ++++++++++--------- .../src/components/workflow/Workflow.tsx | 1 - .../workflow/onboarding/Onboarding.tsx | 14 +++++---- .../workflow/useGetProjectAssessmentSchema.ts | 22 +++++++++----- .../jsonSchemaFromWorkflowDefinition.test.ts | 17 +++++------ .../jsonSchemaFromWorkflowDefinition.ts | 15 ++++++++-- plugins/parodos/src/setupTests.ts | 1 + 8 files changed, 67 insertions(+), 46 deletions(-) diff --git a/plugins/parodos/src/components/ParodosPage/ParodosPage.tsx b/plugins/parodos/src/components/ParodosPage/ParodosPage.tsx index 969c19f2cd..2b49319a98 100644 --- a/plugins/parodos/src/components/ParodosPage/ParodosPage.tsx +++ b/plugins/parodos/src/components/ParodosPage/ParodosPage.tsx @@ -4,7 +4,7 @@ import { useLocation } from 'react-router-dom'; import { PageHeader } from '../PageHeader'; import type { PropsFromComponent } from '../types'; import { useNavigate } from 'react-router-dom'; -import { TabLabel, TabLabelProps } from './TabLabel'; +import { tabLabelCreator } from './TabLabel'; import { navigationMap, pluginRoutePrefix } from './navigationMap'; import { useStore } from '../../stores/workflowStore/workflowStore'; import { ErrorMessage } from '../errors/ErrorMessage'; @@ -40,13 +40,10 @@ export const ParodosPage: FC = ({ children, ...props }) => { id: index.toString(), label, tabProps: { - component: (p: TabLabelProps) => ( - - ), + component: tabLabelCreator({ + icon: navigationMap[index].icon, + highlighted, + }), }, }; }), diff --git a/plugins/parodos/src/components/ParodosPage/TabLabel.tsx b/plugins/parodos/src/components/ParodosPage/TabLabel.tsx index 8c58ad7f42..21ee51fb00 100644 --- a/plugins/parodos/src/components/ParodosPage/TabLabel.tsx +++ b/plugins/parodos/src/components/ParodosPage/TabLabel.tsx @@ -1,4 +1,4 @@ -import React, { type ReactNode } from 'react'; +import React, { forwardRef, type ReactNode } from 'react'; import { makeStyles } from '@material-ui/core'; import StarIcon from '@material-ui/icons/Star'; @@ -17,19 +17,21 @@ export interface TabLabelProps { children: ReactNode; } -export function TabLabel({ - children, +export const tabLabelCreator = ({ icon, highlighted, - ...props -}: TabLabelProps) { - const styles = useStyles(); +}: { + icon: ReactNode; + highlighted: boolean; +}) => + forwardRef(({ children, ...props }, ref) => { + const styles = useStyles(); - return ( - - {icon} - {highlighted && } - {children} - - ); -} + return ( + + {icon} + {highlighted && } + {children} + + ); + }); diff --git a/plugins/parodos/src/components/workflow/Workflow.tsx b/plugins/parodos/src/components/workflow/Workflow.tsx index 86a16014f7..bb8ac3dc5e 100644 --- a/plugins/parodos/src/components/workflow/Workflow.tsx +++ b/plugins/parodos/src/components/workflow/Workflow.tsx @@ -34,7 +34,6 @@ const useStyles = makeStyles(theme => ({ height: '100%', }, form: { - marginTop: theme.spacing(2), '& .field-boolean > div > label': { display: 'inline-block', marginBottom: theme.spacing(2), diff --git a/plugins/parodos/src/components/workflow/onboarding/Onboarding.tsx b/plugins/parodos/src/components/workflow/onboarding/Onboarding.tsx index 2eec805c57..0e68fb15b6 100644 --- a/plugins/parodos/src/components/workflow/onboarding/Onboarding.tsx +++ b/plugins/parodos/src/components/workflow/onboarding/Onboarding.tsx @@ -73,10 +73,14 @@ export function Onboarding({ isNew }: OnboardingProps): JSX.Element { const payload = { projectId, workFlowName: workflow.name, - // TODO: top level parameters - // arguments: Object.entries(workflow.parameters ?? {}).map(([key]) => { + arguments: Object.entries(workflow.parameters ?? {}).map(([key]) => { + const value = lodashGet(formData, `${workflow.name}.${key}`, null); - // }), + return { + key, + value, + }; + }), works: workflow.works.map(work => { return { workName: work.name, @@ -84,8 +88,8 @@ export function Onboarding({ isNew }: OnboardingProps): JSX.Element { const value = lodashGet(formData, `${work.name}.${key}`, null); return { - key: key, - value: value, + key, + value, }; }), }; diff --git a/plugins/parodos/src/components/workflow/useGetProjectAssessmentSchema.ts b/plugins/parodos/src/components/workflow/useGetProjectAssessmentSchema.ts index a1e6de50fa..e44cffefaf 100644 --- a/plugins/parodos/src/components/workflow/useGetProjectAssessmentSchema.ts +++ b/plugins/parodos/src/components/workflow/useGetProjectAssessmentSchema.ts @@ -32,15 +32,22 @@ export function useGetProjectAssessmentSchema({ cloned.works[0].parameters = cloned.works[0].parameters ?? {}; if (newProject) { + cloned.works[0].parameters.newProject = { ...newProjectChoice }; + cloned.works[0].parameters.Name = { description: 'New Project', required: false, format: 'text', type: 'string', }; - - cloned.works[0].parameters.newProject = { ...newProjectChoice }; } else { + cloned.works[0].parameters = {}; + + cloned.works[0].parameters.newProject = { + ...newProjectChoice, + description: 'Search for an existing project to execute a new workflow:', + }; + cloned.works[0].parameters.project = { required: false, type: 'string', @@ -48,15 +55,16 @@ export function useGetProjectAssessmentSchema({ field: 'ProjectPicker', disabled: !hasProjects, }; - - cloned.works[0].parameters.newProject = { - ...newProjectChoice, - description: 'Search for an existing project to execute a new workflow:', - }; } const formSchema = jsonSchemaFromWorkflowDefinition(cloned); + set(formSchema, `steps[0].uiSchema.onboardingAssessmentTask.['ui:order']`, [ + 'newProject', + 'Name', + '*', + ]); + // TODO: should be able to do this with ui:title set( formSchema, diff --git a/plugins/parodos/src/hooks/useWorkflowDefinitionToJsonSchema/jsonSchemaFromWorkflowDefinition.test.ts b/plugins/parodos/src/hooks/useWorkflowDefinitionToJsonSchema/jsonSchemaFromWorkflowDefinition.test.ts index ca50c48fd7..3b9ba818a6 100644 --- a/plugins/parodos/src/hooks/useWorkflowDefinitionToJsonSchema/jsonSchemaFromWorkflowDefinition.test.ts +++ b/plugins/parodos/src/hooks/useWorkflowDefinitionToJsonSchema/jsonSchemaFromWorkflowDefinition.test.ts @@ -30,18 +30,17 @@ describe('jsonSchemaFromWorkflowDefinition', () => { it('transforms deeply nested recursive structure', () => { const result = jsonSchemaFromWorkflowDefinition(mockDeepRecursiveWorks); - const comment = get( - result.steps[1]?.schema, - 'properties.subWorkFlowThree.properties.works.items[1].properties.subWorkFlowTwo.properties.works.items[0].properties.subWorkFlowOne.properties.comment.title', + const domainName = get( + result.steps[0]?.schema, + 'properties.sslCertificationWorkFlowTask.properties.domainName.title', ); - expect(comment).toBe('comment'); - - const singleSignOn = get( - result.steps[2]?.schema, - 'properties.subWorkFlowFour.properties.works.items[1].title', + expect(domainName).toBe('domainName'); + const clusterName = get( + result.steps[1]?.schema, + 'properties.subWorkFlowTwo.properties.works.items[0].properties.subWorkFlowOne.properties.works.items[1].properties.splunkMonitoringWorkFlowTask.properties.clusterName.title', ); - expect(singleSignOn).toBe('Single Sign On Work Flow Task'); + expect(clusterName).toBe('clusterName'); }); }); diff --git a/plugins/parodos/src/hooks/useWorkflowDefinitionToJsonSchema/jsonSchemaFromWorkflowDefinition.ts b/plugins/parodos/src/hooks/useWorkflowDefinitionToJsonSchema/jsonSchemaFromWorkflowDefinition.ts index 3b43f5d5bc..db12062667 100644 --- a/plugins/parodos/src/hooks/useWorkflowDefinitionToJsonSchema/jsonSchemaFromWorkflowDefinition.ts +++ b/plugins/parodos/src/hooks/useWorkflowDefinitionToJsonSchema/jsonSchemaFromWorkflowDefinition.ts @@ -101,7 +101,16 @@ function transformWorkToStep(work: WorkType): Step { for (const [ key, - { description, type, required, format, default: fieldDefault, field }, + { + description, + type, + required, + format, + default: fieldDefault, + field, + minLength, + maxLength, + }, ] of Object.entries(work.parameters ?? {})) { const propertiesPath = `properties.${work.name}.properties.${key}`; @@ -109,6 +118,8 @@ function transformWorkToStep(work: WorkType): Step { title: `${key}`, ...getJsonSchemaType(format ?? (type as ParameterFormat)), ...{ default: fieldDefault }, + minLength, + maxLength, }); const objectPath = `${work.name}.${key}`; @@ -117,7 +128,7 @@ function transformWorkToStep(work: WorkType): Step { ...getUiSchema(format ?? (type as ParameterFormat)), 'ui:field': field, 'ui:help': description, - 'ui:autocomplete': 'Off', + // 'ui:autocomplete': 'Off', }); if (required) { diff --git a/plugins/parodos/src/setupTests.ts b/plugins/parodos/src/setupTests.ts index 945c81e571..201dd6ff43 100644 --- a/plugins/parodos/src/setupTests.ts +++ b/plugins/parodos/src/setupTests.ts @@ -5,4 +5,5 @@ jest.mock('@material-ui/core/styles', () => ({ createStyles: () => () => ({}), makeStyles: () => () => ({}), withStyles: () => () => ({}), + createTheme: () => () => ({}), })); From 035583241ec00405b84208702d14f84580fd5da1 Mon Sep 17 00:00:00 2001 From: Paul Cowan Date: Thu, 23 Mar 2023 08:01:27 +0000 Subject: [PATCH 3/7] fix README --- plugins/parodos/README.md | 10 +--------- .../components/layouts/FluidObjectFieldTemplate.tsx | 3 --- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/plugins/parodos/README.md b/plugins/parodos/README.md index e7a1308161..b900a813b1 100644 --- a/plugins/parodos/README.md +++ b/plugins/parodos/README.md @@ -58,14 +58,6 @@ For local development set the `PARODOS_AUTH_KEY` environment variable to 'Basic ```yaml proxy: '/parodos': - target: 'http://localhost:8080/api/v1' - changeOrigin: true - redirect: follow - cache: 'no-cache' headers: - Content-Type: 'application/json' - accept: 'application/json' - Authorization: ${PARODOS_AUTH_KEY} + Authorization: 'Basic dGVzdDp0ZXN0' ``` - -Running `yarn workspace @janus-idp/backstage-plugin-parodos dev` from the root will start both the backend proxy server and the frontend development server. diff --git a/plugins/parodos/src/components/layouts/FluidObjectFieldTemplate.tsx b/plugins/parodos/src/components/layouts/FluidObjectFieldTemplate.tsx index e60be33d71..392f0d646f 100644 --- a/plugins/parodos/src/components/layouts/FluidObjectFieldTemplate.tsx +++ b/plugins/parodos/src/components/layouts/FluidObjectFieldTemplate.tsx @@ -23,9 +23,6 @@ const useStyles = makeStyles(theme => ({ boxShadow: 'none', }, }, - item2: { - border: '10px solid red', - }, title: { fontSize: '1rem', fontWeight: theme.typography From f73aa576011fed284b5b440a42cb28acb5301e76 Mon Sep 17 00:00:00 2001 From: Paul Cowan Date: Thu, 23 Mar 2023 08:27:55 +0000 Subject: [PATCH 4/7] remove spurious comments --- .../components/workflow/workflowDetail/WorkFlowLogViewer.tsx | 2 -- plugins/parodos/src/models/workflowDefinitionSchema.ts | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/plugins/parodos/src/components/workflow/workflowDetail/WorkFlowLogViewer.tsx b/plugins/parodos/src/components/workflow/workflowDetail/WorkFlowLogViewer.tsx index 0068d57f91..97cdc9055b 100644 --- a/plugins/parodos/src/components/workflow/workflowDetail/WorkFlowLogViewer.tsx +++ b/plugins/parodos/src/components/workflow/workflowDetail/WorkFlowLogViewer.tsx @@ -8,8 +8,6 @@ const ParodosLogViewer = withStyles(theme => ({ }, log: { background: theme.palette.background.paper, - // fontSize: theme.typography.body2.fontSize, - // fontFamily: theme.typography.fontFamily, }, }))(LogViewer); diff --git a/plugins/parodos/src/models/workflowDefinitionSchema.ts b/plugins/parodos/src/models/workflowDefinitionSchema.ts index 9e1c33791a..206071cb29 100644 --- a/plugins/parodos/src/models/workflowDefinitionSchema.ts +++ b/plugins/parodos/src/models/workflowDefinitionSchema.ts @@ -40,7 +40,7 @@ export const baseWorkSchema = z.object({ .record(z.string(), workFlowTaskParameterTypeSchema) .optional() .nullable(), - workType: z.union([z.literal('TASK'), z.literal('WORKFLOW')]), // TODO: could this be a union? + workType: z.union([z.literal('TASK'), z.literal('WORKFLOW')]), processingType: processingType.optional(), author: z.string().optional().nullable(), outputs: z From 3ca2c4c8afea31f2542501a9bbe336e5ced1717f Mon Sep 17 00:00:00 2001 From: Paul Cowan Date: Thu, 23 Mar 2023 10:25:19 +0000 Subject: [PATCH 5/7] use transform instead of coercion for required --- .../jsonSchemaFromWorkflowDefinition.test.ts | 7 +++-- .../workflowDefinitions/recursiveWorks.ts | 16 +++++------ .../models/workflowDefinitionSchema.test.ts | 28 ++++++++++++++++--- .../src/models/workflowDefinitionSchema.ts | 23 ++++++++++++--- 4 files changed, 55 insertions(+), 19 deletions(-) diff --git a/plugins/parodos/src/hooks/useWorkflowDefinitionToJsonSchema/jsonSchemaFromWorkflowDefinition.test.ts b/plugins/parodos/src/hooks/useWorkflowDefinitionToJsonSchema/jsonSchemaFromWorkflowDefinition.test.ts index 3b9ba818a6..0d77e55efc 100644 --- a/plugins/parodos/src/hooks/useWorkflowDefinitionToJsonSchema/jsonSchemaFromWorkflowDefinition.test.ts +++ b/plugins/parodos/src/hooks/useWorkflowDefinitionToJsonSchema/jsonSchemaFromWorkflowDefinition.test.ts @@ -1,13 +1,16 @@ import { mockRecursiveWorksWorkflowDefinition } from '../../mocks/workflowDefinitions/recursiveWorks'; import { jsonSchemaFromWorkflowDefinition } from './jsonSchemaFromWorkflowDefinition'; import get from 'lodash.get'; -import { WorkType } from '../../models/workflowDefinitionSchema'; +import { + WorkflowDefinition, + WorkType, +} from '../../models/workflowDefinitionSchema'; import { mockDeepRecursiveWorks } from '../../mocks/workflowDefinitions/deepRecursiveWorks'; describe('jsonSchemaFromWorkflowDefinition', () => { it('transforms a workflow definition with recursive works', () => { const result = jsonSchemaFromWorkflowDefinition( - mockRecursiveWorksWorkflowDefinition, + mockRecursiveWorksWorkflowDefinition as unknown as WorkflowDefinition, ); expect(result.steps.length).toBeGreaterThan(0); diff --git a/plugins/parodos/src/mocks/workflowDefinitions/recursiveWorks.ts b/plugins/parodos/src/mocks/workflowDefinitions/recursiveWorks.ts index 89c57bce3c..96fdf6c6d2 100644 --- a/plugins/parodos/src/mocks/workflowDefinitions/recursiveWorks.ts +++ b/plugins/parodos/src/mocks/workflowDefinitions/recursiveWorks.ts @@ -1,6 +1,4 @@ -import { WorkflowDefinition } from '../../models/workflowDefinitionSchema'; - -export const mockRecursiveWorksWorkflowDefinition: WorkflowDefinition = { +export const mockRecursiveWorksWorkflowDefinition = { id: 'ea22c6ed-b7d4-48bf-98d2-f7c1c78643d8', name: 'subWorkFlowTwo', type: 'INFRASTRUCTURE', @@ -22,13 +20,13 @@ export const mockRecursiveWorksWorkflowDefinition: WorkflowDefinition = { parameters: { adGroups: { description: 'The ad groups', - required: false, + required: 'false', type: 'string', format: 'text', }, userId: { description: 'The user id', - required: false, + required: 'false', type: 'string', format: 'text', }, @@ -42,13 +40,13 @@ export const mockRecursiveWorksWorkflowDefinition: WorkflowDefinition = { parameters: { clusterName: { description: 'The cluster name', - required: false, + required: 'false', type: 'string', format: 'text', }, hostname: { description: 'The hostname', - required: false, + required: 'false', type: 'string', format: 'text', }, @@ -61,7 +59,7 @@ export const mockRecursiveWorksWorkflowDefinition: WorkflowDefinition = { type: 'string', format: 'text', description: 'The workflow comment', - required: false, + required: 'false', }, }, }, @@ -72,7 +70,7 @@ export const mockRecursiveWorksWorkflowDefinition: WorkflowDefinition = { parameters: { projectId: { description: 'The project id', - required: false, + required: 'false', type: 'number', }, }, diff --git a/plugins/parodos/src/models/workflowDefinitionSchema.test.ts b/plugins/parodos/src/models/workflowDefinitionSchema.test.ts index 4206d70511..97585c96b4 100644 --- a/plugins/parodos/src/models/workflowDefinitionSchema.test.ts +++ b/plugins/parodos/src/models/workflowDefinitionSchema.test.ts @@ -1,18 +1,38 @@ import { assert } from 'assert-ts'; +import { type SafeParseReturnType } from 'zod'; import { mockRecursiveWorksWorkflowDefinition } from '../mocks/workflowDefinitions/recursiveWorks'; -import { workflowDefinitionSchema } from './workflowDefinitionSchema'; +import { + WorkflowDefinition, + workflowDefinitionSchema, +} from './workflowDefinitionSchema'; describe('WorkflowDefinitionSchema', () => { - it('parses the API workflow definitions', () => { - const result = workflowDefinitionSchema.safeParse( + let result: SafeParseReturnType; + + beforeEach(() => { + result = workflowDefinitionSchema.safeParse( mockRecursiveWorksWorkflowDefinition, ); + }); + it('parses the API workflow definitions', () => { expect(result.success).toBe(true); assert(result.success); - // recursive works check expect(result.data.works[0]?.works?.[0].name).toBe('adGroupsWorkFlowTask'); }); + + it('should coerce required correctly', () => { + expect(result.success).toBe(true); + + assert(result.success); + + const required = + result.data.works[0]?.works?.[0].parameters?.adGroups?.required; + + expect(typeof required).toBe('boolean'); + + expect(required).toBe(false); + }); }); diff --git a/plugins/parodos/src/models/workflowDefinitionSchema.ts b/plugins/parodos/src/models/workflowDefinitionSchema.ts index 206071cb29..70254bfff0 100644 --- a/plugins/parodos/src/models/workflowDefinitionSchema.ts +++ b/plugins/parodos/src/models/workflowDefinitionSchema.ts @@ -23,7 +23,7 @@ const processingType = z.union([ export const workFlowTaskParameterTypeSchema = z.object({ description: z.string().optional(), - required: z.coerce.boolean(), + required: z.string().transform(val => val === 'true'), type: parameterTypes, format: parameterFormat.optional(), minLength: z.number().optional(), @@ -33,6 +33,12 @@ export const workFlowTaskParameterTypeSchema = z.object({ disabled: z.boolean().default(false).optional(), }); +type Parameter = z.infer; + +type InputParameter = { + [K in keyof Parameter]: K extends 'required' ? string : Parameter[K]; +}; + export const baseWorkSchema = z.object({ id: z.string(), name: z.string(), @@ -59,9 +65,18 @@ export type WorkType = z.infer & { works?: WorkType[]; }; -export const workSchema: z.ZodType = baseWorkSchema.extend({ - works: z.lazy(() => workSchema.array()).optional(), -}); +export type WorkTypeInput = { + [K in keyof WorkType]: K extends 'parameters' + ? Record | null + : K extends 'works' + ? WorkTypeInput[] + : WorkType[K]; +}; + +export const workSchema: z.ZodType = + baseWorkSchema.extend({ + works: z.lazy(() => workSchema.array()).optional(), + }); export const workflowDefinitionSchema = z.object({ id: z.string(), From 470a7d30947fb1554b41b2c4cdd4049d11f0cf73 Mon Sep 17 00:00:00 2001 From: Paul Cowan Date: Thu, 23 Mar 2023 10:31:46 +0000 Subject: [PATCH 6/7] test true required case --- .../mocks/workflowDefinitions/recursiveWorks.ts | 2 +- .../src/models/workflowDefinitionSchema.test.ts | 15 +++++++++++---- .../src/models/workflowDefinitionSchema.ts | 2 +- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/plugins/parodos/src/mocks/workflowDefinitions/recursiveWorks.ts b/plugins/parodos/src/mocks/workflowDefinitions/recursiveWorks.ts index 96fdf6c6d2..65d52db337 100644 --- a/plugins/parodos/src/mocks/workflowDefinitions/recursiveWorks.ts +++ b/plugins/parodos/src/mocks/workflowDefinitions/recursiveWorks.ts @@ -26,7 +26,7 @@ export const mockRecursiveWorksWorkflowDefinition = { }, userId: { description: 'The user id', - required: 'false', + required: 'true', type: 'string', format: 'text', }, diff --git a/plugins/parodos/src/models/workflowDefinitionSchema.test.ts b/plugins/parodos/src/models/workflowDefinitionSchema.test.ts index 97585c96b4..6653408706 100644 --- a/plugins/parodos/src/models/workflowDefinitionSchema.test.ts +++ b/plugins/parodos/src/models/workflowDefinitionSchema.test.ts @@ -23,16 +23,23 @@ describe('WorkflowDefinitionSchema', () => { expect(result.data.works[0]?.works?.[0].name).toBe('adGroupsWorkFlowTask'); }); - it('should coerce required correctly', () => { + it('should transform required correctly', () => { expect(result.success).toBe(true); assert(result.success); - const required = + const falseRequired = result.data.works[0]?.works?.[0].parameters?.adGroups?.required; - expect(typeof required).toBe('boolean'); + expect(typeof falseRequired).toBe('boolean'); - expect(required).toBe(false); + expect(falseRequired).toBe(false); + + const trueRequired = + result.data.works[0]?.works?.[0].parameters?.userId?.required; + + expect(typeof trueRequired).toBe('boolean'); + + expect(trueRequired).toBe(true); }); }); diff --git a/plugins/parodos/src/models/workflowDefinitionSchema.ts b/plugins/parodos/src/models/workflowDefinitionSchema.ts index 70254bfff0..947f86b977 100644 --- a/plugins/parodos/src/models/workflowDefinitionSchema.ts +++ b/plugins/parodos/src/models/workflowDefinitionSchema.ts @@ -67,7 +67,7 @@ export type WorkType = z.infer & { export type WorkTypeInput = { [K in keyof WorkType]: K extends 'parameters' - ? Record | null + ? Record | null : K extends 'works' ? WorkTypeInput[] : WorkType[K]; From a4a41dd572fa712247273132627707a0b32b9488 Mon Sep 17 00:00:00 2001 From: Paul Cowan Date: Thu, 23 Mar 2023 11:15:53 +0000 Subject: [PATCH 7/7] make project required --- .../src/components/workflow/useGetProjectAssessmentSchema.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/parodos/src/components/workflow/useGetProjectAssessmentSchema.ts b/plugins/parodos/src/components/workflow/useGetProjectAssessmentSchema.ts index e44cffefaf..5cac9b9c56 100644 --- a/plugins/parodos/src/components/workflow/useGetProjectAssessmentSchema.ts +++ b/plugins/parodos/src/components/workflow/useGetProjectAssessmentSchema.ts @@ -36,7 +36,7 @@ export function useGetProjectAssessmentSchema({ cloned.works[0].parameters.Name = { description: 'New Project', - required: false, + required: true, format: 'text', type: 'string', }; @@ -49,7 +49,7 @@ export function useGetProjectAssessmentSchema({ }; cloned.works[0].parameters.project = { - required: false, + required: true, type: 'string', format: 'text', field: 'ProjectPicker',