Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Maint/march 2021 #129

Merged
merged 10 commits into from
Mar 31, 2021
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cwlts",
"version": "1.20.1",
"version": "1.21.0",
"description": "TypeScript data model for Common Workflow Language",
"scripts": {
"tsc": "npx tsc",
Expand Down
13 changes: 1 addition & 12 deletions src/models/d2sb/SBDraft2CommandLineToolModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,18 +320,7 @@ export class SBDraft2CommandLineToolModel extends CommandLineToolModel implement

this.baseCommand = [];

(<Array<string | Expression>> tool.baseCommand).reduce((acc, curr) => {
if (typeof curr === "string") {
if (typeof acc[acc.length - 1] === "string") {
acc[acc.length - 1] += ` ${curr}`;
return acc;
} else {
return acc.concat([curr]);
}
} else {
return acc.concat([curr]);
}
}, []).forEach((cmd) => {
tool.baseCommand.forEach((cmd) => {
this.addBaseCommand(cmd);
});

Expand Down
4 changes: 2 additions & 2 deletions src/models/d2sb/SBDraft2CommandOutputBindingModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export class SBDraft2CommandOutputBindingModel extends CommandOutputBindingModel
}

set glob(value: SBDraft2ExpressionModel) {
this.setGlob(value, SBDraft2ExpressionModel);
this.setGlobExpression(value, SBDraft2ExpressionModel);
}

protected _outputEval: SBDraft2ExpressionModel;
Expand Down Expand Up @@ -152,4 +152,4 @@ export class SBDraft2CommandOutputBindingModel extends CommandOutputBindingModel
});
}
}
}
}
47 changes: 26 additions & 21 deletions src/models/generic/CommandOutputBindingModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ export abstract class CommandOutputBindingModel extends ValidationBase implement

secondaryFiles: ExpressionModel[];

glob: ExpressionModel;
glob: ExpressionModel | Array<string>;

protected _glob: ExpressionModel;
protected _glob: ExpressionModel | Array<string>;

loadContents: boolean;

Expand All @@ -36,27 +36,21 @@ export abstract class CommandOutputBindingModel extends ValidationBase implement

abstract deserialize(attr: any): void;

protected setGlob(value: ExpressionModel, exprConstructor: new (...args: any[]) => ExpressionModel) {
let val = value.serialize();
this._glob.clearIssue(ErrorCode.ALL);
this._glob = new exprConstructor(val, `${this.loc}.glob`, this.eventHub);
this._glob.setValidationCallback(err => this.updateValidity(err));
this.validateGlob();
}

protected validateGlob() {
if (!this._glob) return;

if (this._glob.serialize() === undefined) {
this._glob.setIssue({
[`${this.loc}.glob`]: {
message: "Glob should be specified",
type: "warning",
code: ErrorCode.OUTPUT_GLOB_MISSING
}
}, true);
} else {
this._glob.clearIssue(ErrorCode.OUTPUT_GLOB_MISSING);
if (this._glob instanceof ExpressionModel) {
if (this._glob.serialize() === undefined) {
this._glob.setIssue({
[`${this.loc}.glob`]: {
message: "Glob should be specified",
type: "warning",
code: ErrorCode.OUTPUT_GLOB_MISSING
}
}, true);
} else {
this._glob.clearIssue(ErrorCode.OUTPUT_GLOB_MISSING);
}
}
}

Expand All @@ -67,4 +61,15 @@ export abstract class CommandOutputBindingModel extends ValidationBase implement
this._outputEval = new exprConstructor(value.serialize(), `${this.loc}.outputEval`, this.eventHub);
this._outputEval.setValidationCallback(err => this.updateValidity(err));
}
}

protected setGlobExpression(expression: ExpressionModel, exprConstructor: new (...args: any[]) => ExpressionModel) {
if (this._glob instanceof ExpressionModel) {
this._glob.clearIssue(ErrorCode.ALL);
}

this._glob = new exprConstructor(expression.serialize(), `${this.loc}.glob`, this.eventHub);
this._glob.setValidationCallback(err => this.updateValidity(err));
this.validateGlob();
}

}
15 changes: 15 additions & 0 deletions src/models/helpers/Glob.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {V1ExpressionModel} from "../v1.0";

export function toExpression(array: Array<string>): V1ExpressionModel {
return array.length === 1 ? new V1ExpressionModel(array[0]) : new V1ExpressionModel();
}

export function toArray(expression: V1ExpressionModel): Array<string> {
const strExpression = expression.serialize();

if (expression.isExpression) {
return [];
}

return strExpression ? [strExpression] : [];
}
75 changes: 74 additions & 1 deletion src/models/helpers/utils.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import {
CommandInputParameterModel,
CommandOutputParameterModel,
ExpressionModel,
ParameterTypeModel,
WorkflowOutputParameterModel
StepModel,
WorkflowInputParameterModel,
WorkflowOutputParameterModel,
} from "../generic";
import {InputParameterModel} from "../generic/InputParameterModel";
import {ErrorCode, Issue, ValidityError} from "./validation";
Expand Down Expand Up @@ -288,6 +291,36 @@ export const checkIfConnectionIsValid = (pointA, pointB, ltr = true) => {
}
};

const stepHasScatterInput = (step: StepModel, scatter: string) => {
return ensureArray(step.scatter).some(s => s == scatter);
};

const checkBothPointsForSameScatter = () => {
if (pointA instanceof WorkflowInputParameterModel ||
pointA instanceof WorkflowOutputParameterModel ||
pointB instanceof WorkflowInputParameterModel ||
pointB instanceof WorkflowOutputParameterModel) {
return true;
}

if (pointB.parentStep && pointA.parentStep) {
const stepBHasDefinedScatter = stepHasScatterInput(pointB.parentStep, pointB.id);
const stepAHasDefinedScatter = stepHasScatterInput(pointA.parentStep, pointB.id);

if ((!stepAHasDefinedScatter && stepBHasDefinedScatter) ||
(stepAHasDefinedScatter && !stepBHasDefinedScatter)) {
throw new ValidityError(
`Invalid connection. Scatter '${pointB.id}' is making a mismatch in connection`,
ErrorCode.CONNECTION_SCATTER_TYPE
);
}

return true;
}

return true;
}

// fetch type
const pointAType = pointA.type.type;
const pointBType = pointB.type.type;
Expand Down Expand Up @@ -334,10 +367,50 @@ export const checkIfConnectionIsValid = (pointA, pointB, ltr = true) => {
}
}

if (pointAType === pointBType) {
checkBothPointsForSameScatter();
}

if (pointB.secondaryFiles.length) {

if (pointB.secondaryFiles.length > pointA.secondaryFiles.length) {
throw new ValidityError(`Input connection is missing required secondary files`, ErrorCode.CONNECTION_SEC_FILES);
}

const isRequired = (secondaryFile): boolean => secondaryFile.required !== undefined ? secondaryFile.required : true;
const getPattern = (secondaryFile): ExpressionModel => secondaryFile.pattern ? secondaryFile.pattern : secondaryFile;

const requiredSecondaryFiles = pointB.secondaryFiles
.filter(isRequired)
.map(getPattern);

const outputSecondaryFiles = pointA.secondaryFiles.map(getPattern);

requiredSecondaryFiles.forEach(secondaryFile => {
if (secondaryFile.isExpression) {
return;
}

const secondaryFilePattern = `${secondaryFile}`;
const foundSamePattern = outputSecondaryFiles.find(sf => sf.toString().toUpperCase() === secondaryFilePattern.toUpperCase());

if (!foundSamePattern) {
throw new ValidityError(`Input connection is missing required secondary files with a pattern: ${secondaryFilePattern}`, ErrorCode.CONNECTION_SEC_FILES);
}
});

return true;
}

// if not file or fileTypes not defined
return true;
}

// mark connection as valid if pointB has scatter and pointA[]
if ((pointAItems === pointBType) && stepHasScatterInput(pointB.parentStep, pointB.id)) {
return true;
}

// if types are both defined and do not match
const pointATypeOutput = pointAItems ? `"${pointAItems}[]"` : `"${pointAType}"`;
const pointBTypeOutput = pointBItems ? `"${pointBItems}[]"` : `"${pointBType}"`;
Expand Down
2 changes: 2 additions & 0 deletions src/models/helpers/validation/ErrorCode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export enum ErrorCode {
CONNECTION_TYPE = 301,
CONNECTION_FILE_TYPE = 302,
CONNECTION_SAME_STEP = 303,
CONNECTION_SCATTER_TYPE = 304,
CONNECTION_SEC_FILES = 305,

OUTPUT_ALL = 400,
OUTPUT_GLOB_MISSING = 401,
Expand Down
38 changes: 27 additions & 11 deletions src/models/v1.0/V1CommandOutputBindingModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,18 @@ export class V1CommandOutputBindingModel extends CommandOutputBindingModel {
static INHERIT_REGEX = /.*(?:\s*)inheritMetadata\((?:\s*)self(?:\s*),(?:\s*)inputs.(.*?)(?:\s*)\)(?:\s*).*/g;
public inheritMetadataFrom: string;

protected _glob: V1ExpressionModel;
protected _glob: V1ExpressionModel | Array<string>;

get glob(): V1ExpressionModel {
get glob(): V1ExpressionModel | Array<string> {
return this._glob;
}

set glob(value: V1ExpressionModel) {
this.setGlob(value, V1ExpressionModel);
set glob(value: V1ExpressionModel | Array<string>) {
if (value instanceof V1ExpressionModel) {
this.setGlobExpression(value, V1ExpressionModel);
} else {
this._glob = value;
}
}

protected _outputEval: V1ExpressionModel;
Expand Down Expand Up @@ -81,15 +85,14 @@ export class V1CommandOutputBindingModel extends CommandOutputBindingModel {
}

public deserialize(binding: CommandOutputBinding) {
let glob = binding.glob;

if (Array.isArray(binding.glob)) {
glob = binding.glob[0];
this._glob = binding.glob;
return;
}

this.loadContents = binding.loadContents === true;

this._glob = new V1ExpressionModel(<string> glob, `${this.loc}.glob`, this.eventHub);
this._glob = new V1ExpressionModel(<string> binding.glob, `${this.loc}.glob`, this.eventHub);
this._glob.setValidationCallback(err => this.updateValidity(err));
this.validateGlob();

Expand All @@ -108,10 +111,23 @@ export class V1CommandOutputBindingModel extends CommandOutputBindingModel {
public serialize(): CommandOutputBinding {
let base: CommandOutputBinding = <CommandOutputBinding> {};

if (this.loadContents) base.loadContents = true;
if (this.loadContents) {
base.loadContents = true;
}

if (this._glob) {
const globSerialized = this._glob instanceof V1ExpressionModel
? this._glob.serialize()
: this._glob;

if (this._glob && this._glob.serialize() !== undefined) base.glob = this._glob.serialize();
if (this._outputEval && this._outputEval.serialize() !== undefined) base.outputEval = this._outputEval.serialize();
if (globSerialized) {
base.glob = globSerialized;
}
}

if (this._outputEval && this._outputEval.serialize() !== undefined) {
base.outputEval = this._outputEval.serialize();
}

return base;
}
Expand Down