Configuration of a task

Enonic version: 7.8.4
OS: MacOS

I want to create a task to fetch data from an external API. I followed the documentation here:
JS Frameworks > Tasks

I created the descriptor file as explained.

Where can access the form of the descriptor after deploying the APP ?

Hi,

There is no “accessing the form of the descriptor” in the sense that you describe. The XML descriptor is read by the JavaScript controller, and the purpose of the form is to describe the schema for the config object that you can pass into the task execution call.

If we look at the examples from the documentation page you linked to:

Descriptor example:

<task>
  <description>Background job</description>
  <form>
    <input type="Long" name="count">
      <label>Number of items to process</label>
      <default>42</default>
      <occurrences minimum="1" maximum="1"/>
    </input>
  </form>
</task>

Example of task execution call inside the JavaScript controller:

var taskLib = require('/lib/xp/task');
var taskId = taskLib.submitTask({
        name: `mytask`,
        config: {
            count: 10
        }
    }
);

In the descriptor XML, the default value of “count” is set to 42, while in the execution call, this value is overridden and set to 10.

What makes you want to access the form of the descriptor? Can you explain a bit more what you are stuck trying to do?

Hi,

I understand.

In my concrete use case I want to be able to configure the endpoint of the API. The API should be called periodically and the fetched data from API should be persisted in CMS-Repo.

So I would need a task called by a controller with a scheduler?
If so, where does the controller get the values from?
Is there an example app, who does something similar?

So, if I may guess a bit what you’re trying to do:

You plan on writing a task that regularly fetches data from an API endpoint outside of Enonic XP, and then stores the data it fetches in the CMS repo. You also want to be able to configure the location of the remote endpoint that you fetch data from.

Who should be able to configure the endpoint? Should it be restricted to admins who can modify the configuration files on the server, or do you want users to be able to configure the endpoint from within Content Studio?

Basically you need to:

  1. Create a task
  2. Task descriptor XML should have a TextLine input that you can call “endpoint” or something similar
  3. Task controller JavaScript should contain code that does the fetching
  4. App controller (main.js) should schedule the task

The JavaScript controller can get the endpoint from many different types of sources. Here are the two most common sources you can use for task configurations:

  • A configuration file myAppName.cfg which is deployed to $XP_HOME/config/ directory on the server, containing a one or more lines of myVariableName=myValue. Your JavaScript code can then access myValue from the global variable app.config.myVariableName (documentation link) –– This is in my experience the most common way to configure an endpoint for a scheduled task, especially if the endpoint needs a username/password or some other kind of secret key that is best kept locally on the server instead of in code or stored in a repo in XP.
  • If you have a site in Content Studio, you can add your app to that site and then users can edit the site configuration that you’ve specified in the site descriptor, site.xml. (documentation link) If you for some reason expect the endpoint to change often and you want editors to control the endpoint, then the site descriptor is a good place to have the config form for it.

Does this help you along the way?

1 Like

Hi Bjørnar,

yes, that helps a lot. I was not aware of the app-controller.
That was one missing piece for me.
Configure the endpoint in the config-file should do the job.
Thanks a lot for your detailed explanation.
I found that app-hmdb is a good starting point.

Thomas

I’d like to chip in that it is even possible to automatically trigger task scheduling via this configuration file (i.e. you don’t have to implement this in main.js if requirements are simple). :slight_smile:

1 Like

One more question:
When I define a default value in the task descriptor- I would expect that the value is available through the param parameter.

In the example given here:

in the controller:

var taskLib = require('/lib/xp/task');

exports.run = function (params) {

    var count = params.count || 42;
    taskLib.progress({info: 'Initializing task'});
...

instead of params.count || 42;

shouldn’t just params.count give 42 here ??? How is the default otherwise used??

I successfully finished steps 1 to 3. Now I am stucking on step 4:
I scheduled my task with the following code:

const schedulerLib = require('/lib/xp/scheduler');

const url = app.config.url || ""
const root = app.config.root || "/";

const scheduleNewsFetching = schedulerLib.create({
    name: 'Fetch News',
    descriptor: `${app.name}:fetchnews`,
    description: 'fetches news from news api',
    user: 'user:system:su',
    enabled: true,
    config: {
        url,
        root
    },
    schedule: {type: 'CRON', value: '5 * * * *', timeZone: 'GMT+1:00'}
});

log.info("created scheduler %s", scheduleNewsFetching._id);

I get the following error:

2022-04-09 16:28:03,960 ERROR c.e.xp.portal.impl.main.MainExecutor - Error while executing org.lienas.webpack Application controller
java.util.concurrent.CompletionException: com.enonic.xp.node.NodeAccessException: Access denied to [/] for [CREATE] by user [unknown] 

I also tried to change the context without success!

result = contextLib.run({
            principals: ["role:system.admin"],
            repository: 'system.scheduler',
            branch: 'master'

        }, callback);

Since the error is “Access denied to [/] for [CREATE] by user [unknown]”, and you’re calling the schedulerLib.create() function as user: 'user:system:su'
have you tried to specify the same su user in the contextLib.run() function?

result = contextLib.run({
        principals: ["role:system.admin"],
        repository: 'system.scheduler',
        branch: 'master',
        user: {
            login: 'su',
            userStore: 'system'
        }
    }, scheduleNewsFetching);

Or you could take a step back and try scheduling the task using a configuration file on the server, as tsi suggested in his comment above.

Hi @bhj ,

for any reason it now also works with:

 contextLib.run({
        principals: ["role:system.admin"],
        repository: 'system.scheduler',
        branch: 'master'

    }, callback);

I thought, that it is sufficient to set the user, when I call schedulerLib.create(…)

const scheduleNewsFetching = schedulerLib.create({
        name: "Fetch News",
        descriptor: `${app.name}:fetchnews`,
        description: 'fetches news from news api',
        user: "user:system:su",
        enabled: true,
        config: {
            url,
            root
        },
        // * * * * * means every minute for test - in production every 2h = 0 */2 * * *
        // calculator -> https://crontab.guru/
        schedule: {type: 'CRON', value: '*/15 * * * *', timeZone: 'GMT+1:00'}
    });

So I finally do not understand, why I have to change the context too!