async saveStepAttribs()

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;
  }