in components/base-workflow/packages/base-workflow-core/lib/workflow/workflow-instance-service.js [123:208]
async saveStepAttribs(requestContext, input) {
// TODO: Workflow Permissions Management is not implemented yet
// For now, only admins are allowed to save additional attributes against steps in a running workflow
// Once Workflow Permissions Management is implemented, modify this to honor those permissions.
//
// Since manual pause and play of a workflow uses this feature of saving a flag against a step to mark it
// paused/resumed this also means that only admins can resume a workflow manually as of now
await ensureAdmin(requestContext);
const [jsonSchemaValidationService, workflowService] = await this.service([
'jsonSchemaValidationService',
'workflowService',
]);
await jsonSchemaValidationService.ensureValid(input, saveStepAttributesSchema);
const { instanceId, stepIndex, attribs } = input;
if (stepIndex < 0) {
throw this.boom.badRequest(
`Invalid stepIndex specified. It must be non-zero index corresponding to
the step in the workflow for which you want to save attributes`,
true,
);
}
// update state in DynamoDB
const [dbService] = await this.service(['dbService']);
const table = this.tableName;
const workflowInstance = await this.findInstance(requestContext, { id: instanceId });
if (!workflowInstance) {
throw this.boom.badRequest(`Workflow instance "${instanceId}" does not exist`, true);
}
const { wfId, wfVer, stAttribs: existingStepAttribs } = workflowInstance;
const workflow = await workflowService.mustFindVersion(requestContext, { id: wfId, v: wfVer });
if (stepIndex > workflow.selectedSteps.length) {
throw this.boom.badRequest(
`Invalid stepIndex specified. It must be non-zero index corresponding to
the step in the workflow for which you want to save attributes.
There is no step in the workflow at the specified step index`,
true,
);
}
const stepAttribsToSet = existingStepAttribs || [];
// The "stepAttribsToSet" array contains additional step attributes for each step
if (stepAttribsToSet.length - 1 < stepIndex) {
// This is the first time some additional step attributes are being set for this step
// The array may not have been expanded yet to accommodate attribs for this step yet
// Fill array with empty objects as step attributes up to the step for which we are saving additional
// step attributes. This approach allows for lazily fitting the step attributes into the stAttribs array
// instead of populating them at item creation time in db
for (let i = 0; i < stepIndex; i += 1) {
if (_.isNil(stepAttribsToSet[i])) {
// There are no additional attributes stored against the step at index = i so initialize it with empty object
stepAttribsToSet[i] = {};
}
}
}
stepAttribsToSet[stepIndex] = attribs;
const result = await runAndCatch(
async () => {
let op = dbService.helper
.updater()
.table(table)
.condition('attribute_exists(id)') // yes we need this
.key({ id: instanceId });
if (!_.isUndefined(attribs)) {
op = op
.set(`#stAttribs = :stAttribs`)
.names({ '#stAttribs': 'stAttribs' })
.values({ ':stAttribs': stepAttribsToSet });
}
return op.update();
},
async () => {
throw this.boom.badRequest(`Workflow instance "${instanceId}" does not exist`, true);
},
);
// Write audit event
await this.audit(requestContext, { action: 'save-workflow-instance-step-attributes', body: result });
return result;
}