Exec from custom module

Is it possible to run a Library workflow (the Exec action) from within a custom module?

I’m not sure what you are trying. Is it for Server Connect?

You can have a look at the core module which holds the exec action.

In NodeJS the important part of the exec action is:

await this.exec(await fs.readJSON(`app/modules/lib/${options.exec}.json`), true);

That will load the json and execute it. The exec action also has some code to create a new data scope.

Thanks Patrick.

I have a custom module that creates a queue using the node package “Bull”.

I’m using this to fulfill callbacks of webhook subscriptions. So an event is dropped into the queue for processing. Right now that processing just calls an http method to trigger the processing but I thought it would be better to execute the actions directly from a library.

@patrick no luck on this yet.

Here is what is working:

When setting up the bull queue, I specify that the processQueue function be run each time a job is added to the queue. That function does a POST to a Wappler api link.

webhookQueue = new Queue('webhook-queue');

webhookQueue.process(async (job) => {
     return await processQueue(job, this.global.data.$_SERVER.BASE_URL, '/api/events/members/member_updated');
    
});

function processQueue(job, base_url, api_path) {

    return new Promise((resolve, reject) => {
        axios
            .post(base_url + api_path, job)
            .then(res => {
                resolve();
            })
            .catch(error => {
                console.error(error)
            });

    });

}

I was thinking I could modify the processQueue function so that it would load a library file instead of a POST request. I tried the this.exec which indeed finds the file, but the steps are not executed.

If you don’t have some simple solution to this, I’ll just stick with what is working – I am only trying to optimize at this point.

I think what you have is also fine. I see a small issue with the processQueue function, when there is an error in the axios post it will not resolve the promise and never return.

Since axios is already returning a promise you don’t have to wrap it.

function processQueue(job, base_url, api_path) {
  return axios.post(base_url + api_path, job).catch(console.error);
}
1 Like

Thanks Patrick, I probably would not have caught that!

Hi @patrick
Was trying this out, but getting an error saying this.exec is not a function.
Do I need to include some system files/packages to my custom extension JS?

Does this mean I can send some params to this exec function as well?

Purpose: Sending an email when an exception occurs. This email is configured in Wappler library action.
Alternate: Configure email sending in a SA and call the SA like an API as Ken is doing.

Important is that you use anonymous function in your custom module and not arrow functions.

// this works
exports.run = function(options) {
  return this.exec(options.exec, true);
}

// this doesn't work
exports.run = (options) => {
  return this.exec(options.exec, true);
}

Here the exec action from the core module with some comments

exports.exec = async function(options) {
  // initialize empty object for the data that will be returned
  var data = {};

  // check if the exec option is set and if a action file with that name exists
  if (options.exec && fs.existsSync(`app/modules/lib/${options.exec}.json`)) {
    // store current app data in temporary variable
    let parentData = this.data;
    // make the app data empty
    this.data = {};
    // create a new child scope with some data
    this.scope = this.scope.create({ $_PARAM: this.parse(options.params) });
    // execute the action file
    await this.exec(await fs.readJSON(`app/modules/lib/${options.exec}.json`), true);
    // store the new data into the local data object
    data = this.data;
    // reset the scope to the parent scope
    this.scope = this.scope.parent;
    // restore the original data
    this.data = parentData;
  } else {
    // throw an error when the action doesn't exist
    throw new Error(`There is no action called '${options.exec}' found in the library.`);
  }

  // return the data, depending on the output option the data will be merged with the output data
  return data;
}

Thank you for the explanation. My main RUN method is as required.

I have implemented https://github.com/kibertoad/toad-scheduler package.
In this, I have a function which gets called as per set duration.
I need to call the exec in this function when I catch an exception here. But I see that this function itself is calling other functions using =>, so I guess it will not work.

I was able to setup node-fetch, and that is working for now.
Will see if I can convert => implementation. I am not very comfortable yet with promises stuff in JS. :sweat_smile:

Why don’t you use the schedule that is already available in server connect?

I need a web based UI to manage that.
We are using Wappler’s scheduler SA for projects where user does not need control over what runs when.