Workflow validation: Operator interop

This commit is contained in:
Felix Kaspar 2023-12-21 15:57:51 +01:00
parent ba2588ea24
commit 993d44b9b8
4 changed files with 63 additions and 25 deletions

View File

@ -48,7 +48,7 @@ router.post("/:workflowUuid?", [
if(req.body.async === "false") { if(req.body.async === "false") {
console.log("Don't do async"); console.log("Don't do async");
traverseOperations(workflow.operations, inputs, (state) => { traverseOperations(workflow.actions, inputs, (state) => {
console.log("State: ", state); console.log("State: ", state);
}).then(async (pdfResults) => { }).then(async (pdfResults) => {
console.log("Download"); console.log("Download");
@ -88,7 +88,7 @@ router.post("/:workflowUuid?", [
} }
}); });
traverseOperations(workflow.operations, inputs, (state) => { traverseOperations(workflow.actions, inputs, (state) => {
console.log("State: ", state); console.log("State: ", state);
if(activeWorkflow.eventStream) if(activeWorkflow.eventStream)
activeWorkflow.eventStream.write(`data: ${state}\n\n`); activeWorkflow.eventStream.write(`data: ${state}\n\n`);

View File

@ -45,11 +45,11 @@ export class Impose extends Operator {
static type: string = "impose"; static type: string = "impose";
/** /**
* Validation * Validation & Localisation
*/ */
static inputSchema = JoiPDFFileSchema.label(translationObject.inputs.pdfFile.name).description(translationObject.inputs.pdfFile.description); protected static inputSchema = JoiPDFFileSchema.label(translationObject.inputs.pdfFile.name).description(translationObject.inputs.pdfFile.description);
static valueSchema = Joi.object({ protected static valueSchema = Joi.object({
nup: Joi.number().integer().valid(2, 3, 4, 8, 9, 12, 16).required() 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) .label(translationObject.operators.nup.values.nup.friendlyName).description(translationObject.operators.nup.values.nup.description)
.example("3").example("4"), .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-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", "JIS-B7", "JIS-B8", "JIS-B9", "JIS-B10", "JIS-B11", "JIS-B12",
"Shirokuban4", "Shirokuban5", "Shirokuban6", "Kiku4", "Kiku5", "AB", "B40", "Shikisen" "Shirokuban4", "Shirokuban5", "Shirokuban6", "Kiku4", "Kiku5", "AB", "B40", "Shikisen"
].flatMap(size => [size, size + "P", size + "L"])) ].flatMap(size => [size, size + "P", size + "L"])).required()
.required()
.label(translationObject.operators.nup.values.format.friendlyName).description(translationObject.operators.nup.values.format.description) .label(translationObject.operators.nup.values.format.friendlyName).description(translationObject.operators.nup.values.format.description)
.example("A4").example("A3L") .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({ static schema = Joi.object({
input: Impose.inputSchema.required(), input: Impose.inputSchema.required(),
@ -105,6 +104,7 @@ export class Impose extends Operator {
output: Impose.outputSchema.optional() output: Impose.outputSchema.optional()
}).label(translationObject.operators.nup.friendlyName).description(translationObject.operators.nup.description); }).label(translationObject.operators.nup.friendlyName).description(translationObject.operators.nup.description);
/** /**
* Logic * Logic
*/ */

View File

@ -18,10 +18,14 @@ export class Operator {
static type: string; static type: string;
/** The Joi validators & decorators */ /** The Joi validators & decorators */
static inputSchema: Joi.Schema; protected static inputSchema: Joi.Schema;
static valueSchema: Joi.Schema; protected static valueSchema: Joi.Schema;
static outputSchema: Joi.Schema; protected static outputSchema: Joi.Schema;
static schema: Joi.Schema; static schema: Joi.ObjectSchema<{
input: Joi.Schema;
values: Joi.Schema;
output: Joi.Schema;
}>;
actionValues: any; actionValues: any;

View File

@ -1,26 +1,54 @@
import { Operator } from "functions";
import { Action } from "../../declarations/Action"; import { Action } from "../../declarations/Action";
import { getOperatorByName } from "./getOperatorByName"; import { getOperatorByName } from "./getOperatorByName";
/** This function validates the "workflow-json" from the API */ /** This function validates the "workflow-json" from the API */
export function validateOperations(actions: Action[]): { valid: boolean, reason?: string} { export function validateOperations(actions: Action[]): { valid: boolean, reason?: string} {
for (const action of actions) { const done: Action[] = [];
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();
if(!validationResult.valid) { for (const action of actions) {
return validationResult; 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) { 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); const validationResult = validateOperations(action.actions);
if(!validationResult.valid) { if(!validationResult.valid) {
@ -29,4 +57,10 @@ export function validateOperations(actions: Action[]): { valid: boolean, reason?
} }
} }
return { valid: true }; 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;
} }