diff --git a/server-node/src/routes/api/workflow-controller.ts b/server-node/src/routes/api/workflow-controller.ts index 8b1951449..4ee0d6f8e 100644 --- a/server-node/src/routes/api/workflow-controller.ts +++ b/server-node/src/routes/api/workflow-controller.ts @@ -48,7 +48,7 @@ router.post("/:workflowUuid?", [ if(req.body.async === "false") { console.log("Don't do async"); - traverseOperations(workflow.operations, inputs, (state) => { + traverseOperations(workflow.actions, inputs, (state) => { console.log("State: ", state); }).then(async (pdfResults) => { console.log("Download"); @@ -88,7 +88,7 @@ router.post("/:workflowUuid?", [ } }); - traverseOperations(workflow.operations, inputs, (state) => { + traverseOperations(workflow.actions, inputs, (state) => { console.log("State: ", state); if(activeWorkflow.eventStream) activeWorkflow.eventStream.write(`data: ${state}\n\n`); diff --git a/shared-operations/src/functions/impose.ts b/shared-operations/src/functions/impose.ts index 19910776c..e6a254c3b 100644 --- a/shared-operations/src/functions/impose.ts +++ b/shared-operations/src/functions/impose.ts @@ -45,11 +45,11 @@ export class Impose extends Operator { static type: string = "impose"; /** - * Validation + * Validation & Localisation */ - static inputSchema = JoiPDFFileSchema.label(translationObject.inputs.pdfFile.name).description(translationObject.inputs.pdfFile.description); - static valueSchema = Joi.object({ + protected static inputSchema = JoiPDFFileSchema.label(translationObject.inputs.pdfFile.name).description(translationObject.inputs.pdfFile.description); + protected static valueSchema = Joi.object({ nup: Joi.number().integer().valid(2, 3, 4, 8, 9, 12, 16).required() .label(translationObject.operators.nup.values.nup.friendlyName).description(translationObject.operators.nup.values.nup.description) .example("3").example("4"), @@ -92,12 +92,11 @@ export class Impose extends Operator { "JIS-B0", "JIS-B1", "JIS-B2", "JIS-B3", "JIS-B4", "JIS-B5", "JIS-B6", "JIS-B7", "JIS-B8", "JIS-B9", "JIS-B10", "JIS-B11", "JIS-B12", "Shirokuban4", "Shirokuban5", "Shirokuban6", "Kiku4", "Kiku5", "AB", "B40", "Shikisen" - ].flatMap(size => [size, size + "P", size + "L"])) - .required() + ].flatMap(size => [size, size + "P", size + "L"])).required() .label(translationObject.operators.nup.values.format.friendlyName).description(translationObject.operators.nup.values.format.description) .example("A4").example("A3L") }); - static outputSchema = JoiPDFFileSchema.label(translationObject.outputs.pdfFile.name).description(translationObject.outputs.pdfFile.description); + protected static outputSchema = JoiPDFFileSchema.label(translationObject.outputs.pdfFile.name).description(translationObject.outputs.pdfFile.description); static schema = Joi.object({ input: Impose.inputSchema.required(), @@ -105,6 +104,7 @@ export class Impose extends Operator { output: Impose.outputSchema.optional() }).label(translationObject.operators.nup.friendlyName).description(translationObject.operators.nup.description); + /** * Logic */ diff --git a/shared-operations/src/functions/index.ts b/shared-operations/src/functions/index.ts index 07022e6c9..7d62cd086 100644 --- a/shared-operations/src/functions/index.ts +++ b/shared-operations/src/functions/index.ts @@ -18,10 +18,14 @@ export class Operator { static type: string; /** The Joi validators & decorators */ - static inputSchema: Joi.Schema; - static valueSchema: Joi.Schema; - static outputSchema: Joi.Schema; - static schema: Joi.Schema; + protected static inputSchema: Joi.Schema; + protected static valueSchema: Joi.Schema; + protected static outputSchema: Joi.Schema; + static schema: Joi.ObjectSchema<{ + input: Joi.Schema; + values: Joi.Schema; + output: Joi.Schema; + }>; actionValues: any; diff --git a/shared-operations/src/workflow/validateOperations.ts b/shared-operations/src/workflow/validateOperations.ts index 1cb94cd64..55039806a 100644 --- a/shared-operations/src/workflow/validateOperations.ts +++ b/shared-operations/src/workflow/validateOperations.ts @@ -1,26 +1,54 @@ +import { Operator } from "functions"; import { Action } from "../../declarations/Action"; import { getOperatorByName } from "./getOperatorByName"; /** This function validates the "workflow-json" from the API */ export function validateOperations(actions: Action[]): { valid: boolean, reason?: string} { - for (const action of actions) { - if (action.type === "wait" || action.type === "done") { - // TODO: Validate these too ): - return { valid: true }; - } - else { - const operator = getOperatorByName(action.type); - if(!operator) { - return { valid: false, reason: `action.type ${action.type} does not exist` } - } - const validationResult = new operator(action).validate(); + const done: Action[] = []; - if(!validationResult.valid) { - return validationResult; + for (const action of actions) { + if (action.type === "done") { + if(done[action.values.id] !== undefined) { + return { valid: false, reason: "There is a duplicate id in the done actions." }; } + done[action.values.id] = action; + continue; + } + + const operator = getOperatorByName(action.type); + if(!operator) { + return { valid: false, reason: `action.type ${action.type} does not exist` } + } + const validationResult = new operator(action).validate(); + + if(!validationResult.valid) { + return validationResult; } if (action.actions) { + // Check io compatibility of the operators + for (const childAction of action.actions) { + if (childAction.type === "wait") { + if(done[childAction.values.id] === undefined) { + return { valid: false, reason: "There is a wait action that does not have an associated done action." }; + } + + for (const afterDoneChild of done[childAction.values.id].actions) { + if(!ioCompatible(operator, getOperatorByName(afterDoneChild.type))) { + return { valid: false, reason: `Ouput of action ${action.type} is not compatible with input of action ${afterDoneChild.type}` }; + } + } + } + else if (action.type === "done") { + return { valid: false, reason: `There shouldn't be a done action here.` }; + } + else { + if(!ioCompatible(operator, getOperatorByName(childAction.type))) { + return { valid: false, reason: `Ouput of action ${action.type} is not compatible with input of action ${childAction.type}` }; + } + } + } + const validationResult = validateOperations(action.actions); if(!validationResult.valid) { @@ -29,4 +57,10 @@ export function validateOperations(actions: Action[]): { valid: boolean, reason? } } return { valid: true }; +} + +function ioCompatible(outputingOperator: typeof Operator, recievingOperator: typeof Operator): boolean { + const outputType = outputingOperator.schema.describe().keys.output.label; + const inputType = recievingOperator.schema.describe().keys.input.label; + return outputType == inputType; } \ No newline at end of file