| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117 |
- /**
- * @node workflow-input
- * @name Workflow Input
- * @category triggers
- * @version 1.0.0
- * @description Start a workflow that another workflow calls, and say what it expects to be given
- * @trigger
- * @icon log-in
- */
- const configSchema = {
- type: 'object',
- // The names below are what this node produces, so the editor can offer
- // them to later nodes as soon as they are typed - without waiting for a run.
- outputsFromConfig: { from: 'fields', nameKey: 'name' },
- properties: {
- fields: {
- type: 'array',
- title: 'Expected Input',
- description: 'What the calling workflow must or may pass. A caller that leaves out a required one is refused here rather than failing somewhere deeper',
- items: {
- type: 'object',
- properties: {
- name: { type: 'string', title: 'Name', description: 'Key the caller passes, such as city' },
- required: { type: 'boolean', title: 'Required', description: 'Refuse the call when this is missing', default: true },
- description: { type: 'string', title: 'Description', description: 'What the caller should put here' },
- defaultValue: { type: 'string', title: 'Default', description: 'Used when an optional key is not passed' }
- }
- }
- },
- allowExtra: {
- type: 'boolean',
- title: 'Allow Anything Else',
- description: 'Pass through keys that are not listed above. Turn off to keep the contract exact and catch a caller sending the wrong thing',
- default: true
- }
- }
- };
- const inputSchema = { type: 'object', properties: { data: { type: 'any' } } };
- const outputSchema = {
- type: 'object',
- properties: {
- calledBy: { type: 'string', description: 'Execution id of the workflow that called this one, empty when run directly' }
- }
- };
- async function execute(config, input, context) {
- // The caller's input arrives as the trigger data, the same way a webhook
- // body does. Internal keys the engine put there are not part of it.
- const given = (input && typeof input === 'object') ? input : {};
- const fields = config.fields || [];
- const result = {};
- const missing = [];
- for (let i = 0; i < fields.length; i++) {
- const field = fields[i] || {};
- const name = String(field.name || '').trim();
- if (!name) {
- // A row with a description or a default but no name is a filled-in
- // row whose key went in the wrong box. Skipping it silently meant
- // the caller's value was never accepted and the failure surfaced
- // somewhere else entirely - a node further on complaining that
- // something it needed was missing.
- const described = String(field.description || '').trim();
- const fallback = String(field.defaultValue || '').trim();
- if (described || fallback || field.required === true) {
- throw new Error('Workflow Input: an expected input has no name' +
- (described ? ' (described as "' + described + '")' : '') +
- (fallback ? ', default "' + fallback + '"' : '') +
- '. Put the key the caller passes in the Name box');
- }
- continue;
- }
- const has = Object.prototype.hasOwnProperty.call(given, name) &&
- given[name] !== undefined && given[name] !== null && given[name] !== '';
- if (has) {
- result[name] = given[name];
- } else if (field.required !== false) {
- missing.push(name);
- } else if (field.defaultValue !== undefined && field.defaultValue !== '') {
- result[name] = field.defaultValue;
- }
- }
- // Refusing here, naming every missing key at once, beats failing three
- // nodes later on an undefined that has to be traced back.
- if (missing.length > 0) {
- throw new Error('Workflow Input: the caller did not pass ' + missing.join(', ') +
- '. ' + (missing.length === 1 ? 'It is' : 'They are') + ' marked required on this node');
- }
- if (config.allowExtra !== false) {
- const keys = Object.keys(given);
- for (let i = 0; i < keys.length; i++) {
- const key = keys[i];
- // Engine bookkeeping, not part of what the caller sent.
- if (key.charAt(0) === '_') continue;
- if (!Object.prototype.hasOwnProperty.call(result, key)) {
- result[key] = given[key];
- }
- }
- }
- result.calledBy = (given && given._calledBy) || '';
- smartbotic.log.info('Workflow Input: accepted ' +
- Object.keys(result).filter(function (k) { return k !== 'calledBy'; }).length + ' value(s)');
- return result;
- }
- module.exports = { configSchema, inputSchema, outputSchema, execute };
|