Skip to content

Commit

Permalink
Short wf outputs issue (#46)
Browse files Browse the repository at this point in the history
* Fixed rollup error (Async functions producing an error "ReferenceError: regeneratorRuntime is not defined")

* fixed tests

* Replaced gulp-uglify with uglify-es

* fixed 'parse is undefined' issue

* Added empty wdl string check

* added links for short WF outputs section syntax (both with wildcards and without them)

* added outputs to workflow in action map

* updated tests

* coverage increased

* fixed error "Assignment to constant variable"
  • Loading branch information
TimSPb89 authored and sidoruka committed Feb 8, 2018
1 parent 85a88c5 commit e39bed5
Show file tree
Hide file tree
Showing 5 changed files with 672 additions and 70 deletions.
3 changes: 3 additions & 0 deletions gulpfile.babel.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ const uglifyConfig = {
output: {
comments: /copyright/i,
},
compress: {
inline: 1,
},
};

gulp.task('build:js', () =>
Expand Down
52 changes: 39 additions & 13 deletions src/parser/WDL/entities/Context.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ export default class Context {
.map((wfNode) => {
const workflow = new Workflow(wfNode.attributes.name.source_string, { ast: _.cloneDeep(wfNode) });
workflow.i = Context.getInputsWorkflow(_.cloneDeep(wfNode.attributes.body));
workflow.o = Context.getOutputsWorkflow(_.cloneDeep(wfNode.attributes));
workflow.o = Context.getOutputsWorkflow(_.cloneDeep(wfNode.attributes.body));
return workflow;
});

Expand All @@ -154,11 +154,11 @@ export default class Context {

/**
* Get all workflow inputs
* @param {ast} ast - ast tree node
* @param {ast} wfNodeBody - ast tree node.attributes.body
*/
static getInputsWorkflow(ast) {
static getInputsWorkflow(wfNodeBody) {
const inputs = {};
ast.list.filter(item => item.name.toLowerCase() === Constants.DECLARATION)
wfNodeBody.list.filter(item => item.name.toLowerCase() === Constants.DECLARATION)
.forEach((v) => {
inputs[v.attributes.name.source_string] = {
type: extractType(v.attributes.type),
Expand All @@ -175,21 +175,47 @@ export default class Context {

/**
* Get all workflow outputs
* @param {ast} ast - ast tree node
* @param {ast} wfNodeBody - ast tree node.attributes.body
*/
static getOutputsWorkflow(ast) {
static getOutputsWorkflow(wfNodeBody) {
const outputs = {};
ast.body.list.filter(item => item.name.toLowerCase() === Constants.WF_OUTPUTS)
wfNodeBody.list.filter(item => item.name.toLowerCase() === Constants.WF_OUTPUTS)
.forEach((workflowoutputs) => {
workflowoutputs.attributes.outputs.list.forEach((v) => {
const node = v.attributes;
outputs[node.name.source_string] = {
type: extractType(node.type),
default: extractExpression(node.expression).string,
};
workflowoutputs.attributes.outputs.list.forEach((wfOutput) => {
const node = wfOutput.attributes;
Context.proceedExpression(node, outputs);
Context.proceedWildcard(node, outputs);
});
});

return outputs;
}

static proceedWildcard(node, outputs) {
if (!node.fqn) {
return;
}

const outputString = node.wildcard
? `${node.fqn.source_string}.${node.wildcard.source_string}`
: node.fqn.source_string;

outputs[outputString] = {
type: '',
multi: !!node.wildcard,
default: outputString,
};
}

static proceedExpression(node, outputs) {
if (!node.name && !node.type && !node.expression) {
return;
}

outputs[node.name.source_string] = {
type: extractType(node.type),
default: extractExpression(node.expression).string,
};
}
}

53 changes: 48 additions & 5 deletions src/parser/WDL/entities/WDLWorkflow.js
Original file line number Diff line number Diff line change
Expand Up @@ -237,10 +237,10 @@ export default class WDLWorkflow {

/**
* Build the expressioned outputs
* @param {list<ast>} outputList - Array of ast nodes representing each output
* @param {[<ast>]} outputList - Array of ast nodes representing each output
*/
processExpressions(outputList) {
return outputList.forEach((item) => {
outputList.forEach((item) => {
if (!item.name && !item.type && !item.expression) {
return;
}
Expand Down Expand Up @@ -288,16 +288,16 @@ export default class WDLWorkflow {

/**
* Build the wildcard outputs <deprecated syntax>
* @param {list<ast>} outputList - Array of ast nodes representing each output
* @param {[<ast>]} outputList - Array of ast nodes representing each output
*/
processWilds(outputList) {
outputList.forEach((item) => {
if (!item.fqn && !item.wildcard) {
if (!item.fqn) {
return;
}
const fqn = item.fqn;
const wildcard = item.wildcard;
const res = ((fqn ? fqn.source_string : '') + (wildcard ? `.${wildcard.source_string}` : '')).trim();
const res = (fqn.source_string + (wildcard ? `.${wildcard.source_string}` : '')).trim();

const obj = {};
obj[res] = {
Expand All @@ -307,6 +307,49 @@ export default class WDLWorkflow {
this.workflowStep.action.addPorts({
o: obj,
});
// WF output connections
if (!wildcard) { // syntax: call_name.output_name
const callOutput = fqn.source_string.split('.');
if (callOutput.length < 2) {
return;
}
const callName = callOutput[0];
const outputName = callOutput[1];
const startStep = WDLWorkflow.findStepInStructureRecursively(this.workflowStep, callName);

if (startStep) {
if (startStep.o[outputName]) {
this.workflowStep.o[fqn.source_string].bind(startStep.o[outputName]);
} else {
throw new WDLParserError(
`In '${this.workflowStep.name}'
output block undeclared variable is referenced: '${callName}.${outputName}'`);
}
} else {
throw new WDLParserError(
`In '${this.workflowStep.name}'
output block undeclared call is referenced: '${callName}'`);
}
} else { // syntax: call_name.* (all call's outputs)
const callName = fqn.source_string;
const startStep = WDLWorkflow.findStepInStructureRecursively(this.workflowStep, callName);

if (startStep) {
if (_.size(startStep.o)) {
_.forEach(startStep.o, (output, outputName) => {
this.workflowStep.o[`${fqn.source_string}.*`].bind(startStep.o[outputName]);
});
} else {
throw new WDLParserError(
`In '${this.workflowStep.name}'
output block undeclared variable is referenced: '${callName}.* (${callName} doesn't have any outputs)`);
}
} else {
throw new WDLParserError(
`In '${this.workflowStep.name}'
output block undeclared call is referenced: '${callName}'`);
}
}
});
}

Expand Down
154 changes: 102 additions & 52 deletions test/parser/WDL/entities/ContextTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,64 +113,55 @@ describe('parser/WDL/entities/Context', () => {
const outputValueLhs = 'task1';
const outputValueRhs = 'rawVCF';
const partAst = {
name: {
id: 39,
str: 'identifier',
source_string: 'workflow123',
line: 1,
col: 10,
},
body: {
list: [
{
name: 'WorkflowOutputs',
attributes: {
outputs: {
list: [
{
name: 'WorkflowOutputDeclaration',
attributes: {
type: {
id: 41,
str: 'type',
source_string: outputType,
line: 12,
col: 5,
},
name: {
id: 39,
str: 'identifier',
source_string: outputName,
line: 12,
col: 10,
},
expression: {
name: 'MemberAccess',
attributes: {
lhs: {
id: 39,
str: 'identifier',
source_string: outputValueLhs,
line: 12,
col: 19,
},
rhs: {
id: 39,
str: 'identifier',
source_string: outputValueRhs,
line: 12,
col: 25,
},
list: [
{
name: 'WorkflowOutputs',
attributes: {
outputs: {
list: [
{
name: 'WorkflowOutputDeclaration',
attributes: {
type: {
id: 41,
str: 'type',
source_string: outputType,
line: 12,
col: 5,
},
name: {
id: 39,
str: 'identifier',
source_string: outputName,
line: 12,
col: 10,
},
expression: {
name: 'MemberAccess',
attributes: {
lhs: {
id: 39,
str: 'identifier',
source_string: outputValueLhs,
line: 12,
col: 19,
},
rhs: {
id: 39,
str: 'identifier',
source_string: outputValueRhs,
line: 12,
col: 25,
},
},
},
},
],
},
},
],
},
},
],
},
},
],
};

const outputsWorkflow = Context.getOutputsWorkflow(partAst);
Expand All @@ -179,5 +170,64 @@ describe('parser/WDL/entities/Context', () => {
expect(outputsWorkflow[outputName].type).to.be.equal(outputType);
expect(outputsWorkflow[outputName].default).to.be.equal(`${outputValueLhs}.${outputValueRhs}`);
});

it('returns outputs for workflow with wildcards', () => {
const outputWildcardName = 'foo';
const outputNullWildcardName = 'bar.out';
const partAst = {
list: [
{
name: 'WorkflowOutputs',
attributes: {
outputs: {
list: [
{
name: 'WorkflowOutputWildcard',
attributes: {
fqn: {
id: 11,
str: 'fqn',
source_string: outputWildcardName,
line: 7,
col: 6,
},
wildcard: {
id: 15,
str: 'asterisk',
source_string: '*',
line: 7,
col: 10,
},
},
},
{
name: 'WorkflowOutputWildcard',
attributes: {
fqn: {
id: 11,
str: 'fqn',
source_string: outputNullWildcardName,
line: 7,
col: 5,
},
wildcard: null,
},
},
],
},
},
},
],
};

const outputsWorkflow = Context.getOutputsWorkflow(partAst);

expect(outputsWorkflow[`${outputWildcardName}.*`]).to.be.not.empty;
expect(outputsWorkflow[outputNullWildcardName]).to.be.not.empty;
expect(outputsWorkflow[`${outputWildcardName}.*`].type).to.be.empty;
expect(outputsWorkflow[outputNullWildcardName].type).to.be.empty;
expect(outputsWorkflow[`${outputWildcardName}.*`].default).to.be.equal(`${outputWildcardName}.*`);
expect(outputsWorkflow[outputNullWildcardName].default).to.be.equal(outputNullWildcardName);
});
});
});
Loading

0 comments on commit e39bed5

Please sign in to comment.