Flow Scripting advice for extracting YAML and Issues

Hi,

I am trying to write a function that uses the Architect Scripting SDK to extract a flow's YAML and Validation Issues. In the self-contained example below I capture these as output. However, my code has two problems:

  • The process is hanging, suggesting I'm not handling a promise properly
  • The use of Promises and Callbacks is making it hard to determine how I should be chaining promises

It would be exceedingly helpful if someone with experience of this SDK wouldn't mind casting their discerning eyes over my code, and offering some improvements please? I'm certain even this small amount of the code is committing many sins.

import architectScripting, {
  ArchValidationIssue,
  ArchValidationResults,
} from 'purecloud-flow-scripting-api-sdk-javascript';

const scriptingSession = architectScripting.environment.archSession;
const flowFactory = architectScripting.factories.archFactoryFlows;
const archEnums = architectScripting.enums.archEnums;

const flowId = process.env.FLOW_ID!;
const region = process.env.GENESYSCLOUD_REGION!;
const oAuthClientId = process.env.GENESYSCLOUD_OAUTHCLIENT_ID!;
const oAuthClientSecret = process.env.GENESYSCLOUD_OAUTHCLIENT_SECRET!;

// In the production code I want to continue to process the `output` variable,
// so do not want the process to be terminated
scriptingSession.endTerminatesProcess = false;

(async () => {
  const output: {
    issues?: ArchValidationIssue[];
    flowYaml?: { content: string; filename: string };
  } = {};

  await scriptingSession.startWithClientIdAndSecret(
    region,
    () => {
      return flowFactory.loadFlowByFlowIdAsync(
        flowId,
        archEnums.FLOW_TYPES.inboundShortMessage,
        'latest',
        (flow) => {
          return Promise.all([
            flow.exportToObjectAsync(
              (exportObject) => (output.flowYaml = exportObject),
              archEnums.FLOW_FORMAT_TYPES.yaml,
            ),
            flow.validateAsync().then((a: ArchValidationResults) => {
              output.issues = a.issues;
            }),
          ]);
        },
      );
    },
    oAuthClientId,
    oAuthClientSecret,
    void 0,
    true,
  );

  return output;
})()
  .then(
    // In production version I will be processing the result of the async above
    console.dir
  )
  .catch(console.error);

Many thanks!

Hi @Lucas1,

I found that it was a little easier to handle when breaking apart the processing. I believe this should get you what you want! I was able to run this within my org and get the yaml export and issues. Let me know if this works for you at all, and I think you will be able to modify it to fit your needs. And just as a heads up, I just ran this as a regular TS file using ts-node.

import * as scripting from 'purecloud-flow-scripting-api-sdk-javascript'
import {ArchBaseFlow, ArchValidationIssue} from 'purecloud-flow-scripting-api-sdk-javascript';


const scriptingSession = scripting.environment.archSession;
const flowFactory = scripting.factories.archFactoryFlows;
const archEnums = scripting.enums.archEnums;

const flowId = process.env.FLOW_ID!;
const region = process.env.GENESYSCLOUD_REGION!;
const oAuthClientId = process.env.GENESYSCLOUD_OAUTHCLIENT_ID!;
const oAuthClientSecret = process.env.GENESYSCLOUD_OAUTHCLIENT_SECRET!;

const output: {
    issues?: ArchValidationIssue[],
    flowYaml?: {content: string, filename: string}
} = {};
async function scriptMain() {
    const loadedFlow: ArchBaseFlow = await flowFactory.loadFlowByFlowIdAsync(flowId, archEnums.FLOW_TYPES.inboundShortMessage, 'latest');
    output.flowYaml = await loadedFlow.exportToObjectAsync((exportObject) => {return exportObject}, archEnums.FLOW_FORMAT_TYPES.yaml);
    const validationResults = await loadedFlow.validateAsync();
    output.issues = validationResults.issues;
}

async function runScripting() {
    // In the production code I want to continue to process the `output` variable,
    // so do not want the process to be terminated
    scriptingSession.endTerminatesProcess = false;
    await scriptingSession.startWithClientIdAndSecret(region, scriptMain, oAuthClientId, oAuthClientSecret, undefined, true);
    console.log('Waited for client to resolve');

    // do output processing here?
    console.log(`Number of issues: ${output.issues?.length}`);
    console.log(output.flowYaml?.content)
}

runScripting();

Thanks,
Jon

1 Like

That is so much more readable, thank you!

My process is still hanging, but your refactored version makes it clear that all the promises are resolving. So I shall look for the problem elsewhere.

Thanks again!

I don't wish to take up anymore of your time, I'm very grateful for what you'll offered so far. But thought I'd share this incase it proves useful...

In pursuit of the code causing the script to hang I just ran wtfnode to track open handles. It returned the following 2 timers and 1 interval running in the Architect Scripting SDK:

Timers

(2000 ~ 2 s) l @ /<redacted>/node_modules/purecloud-flow-scripting-api-sdk-javascript/build-scripting/release/scripting.bundle.js:1286

debounce: function(e, t, i) {
  var n, r, o, a, s, l = function() {
    var u = ke() - r;
    t > u ? n = setTimeout(l, t - u) : (n = null, i || (a = e.apply(s, o)), n || (o = s = null));
  }, u = y((function(u) {
    return s = this, o = u, r = ke(), n || (n = setTimeout(l, t), i && (a = e.apply(s, o))), a;
  }));
  return u.cancel = function() {
    clearTimeout(n), n = o = s = null;
  }, u;
},

(1 ~ 1 undefined) bound @ /<redacted>/node_modules/purecloud-flow-scripting-api-sdk-javascript/build-scripting/release/scripting.bundle.js:121351

startNext() {
  setTimeout(function() {
    this._currentDefer = this._nextDefer, this._nextDefer = void 0, this._currentDefer && this.executeInternal(this._currentDefer);
  }.bind(this), 0);
}

Intervals

(180000 ~ 3 min) u @ /<redacted>/node_modules/purecloud-flow-scripting-api-sdk-javascript/build-scripting/release/scripting.bundle.js:100060

r.getAuthenticatedPromise().then((function() {
  f(0, h.length), setInterval(y, p);
}));

This topic was automatically closed 31 days after the last reply. New replies are no longer allowed.