GenericPollingScheduledTask
Runs the polling command repeatedly, completes a blocking manual task when polling is done.
javascript · Common Scripts
Source
/** * GenericPollingScheduledTask * This task is ment to be scheduled by the ScheduleGenericPolling automation * Logic: * This task schedules itself to run 'interval' minutes from the current run, and decrease the 'timeout' accordingly. * This happens until the end condition is met: either the 'timeout' reaches 0, or all IDs are finished running. * Once the end condition is met, this task will complete the manual task with the given playbookID and tag. * * The 'dt' parameter, when applied to the context, should retrieve a list of ids which have not finished running. * Example: * dt = "Joe.Analysis(val.Status != 'finished').ID" * Breakdown: * Joe - integration name * Analysis - the object that contains the ID and the status * Status - can be 'submitted', 'running' or 'finished' * ID - the key that contains the id for polling */ // Constant to verify the minimum build number and XSIAM version for the new polling command (stopScheduleEntry feature). //const MINIMUM_XSIAM_VERSION = '8.3.0'; //const MINIMUM_BUILD_NUMBER_XSIAM = 313276; const MINIMUM_XSOAR_VERSION = '8.2.0'; const MINIMUM_BUILD_NUMBER_XSOAR = 309463; const SANITIZED_ARG_NAMES = ['additionalPollingCommandArgValues', 'additionalPollingCommandArgNames', 'pollingCommandArgName', 'pollingCommand'] function listOfStrings(v) { if (!Array.isArray(v)) { v = [v]; } for (var i = 0; i < v.length; i++) { v[i] = v[i].toString(); } return v; } // https://stackoverflow.com/questions/16227197/compute-intersection-of-two-arrays-in-javascript#16227294 function intersect(a, b) { var t; if (b.length > a.length) t = b, b = a, a = t; // indexOf to loop over shorter return a.filter(function (e) { return b.indexOf(e) > -1; }).filter(function (e, i, c) { // extra step to remove duplicates return c.indexOf(e) === i; }); } function finish(playbookId, tag, err, entryGUID) { var params = { 'id': tag }; if (err === undefined) { params.input = 'YES'; } else { params.input = 'NO'; } if (playbookId) { params.parentPlaybookID = playbookId; } logDebug('[GenericPollingScheduledTask] finish() completing task. input=' + params.input + ' tag=' + tag + ' entryGUID=' + entryGUID + (err === undefined ? '' : ' err=' + JSON.stringify('' + err))); if ((entryGUID !== undefined) && (entryGUID)) { var res = executeCommand("stopScheduleEntry", {'scheduledEntryGuid': entryGUID}); if (isError(res[0])) { logError('Failed to stop scheduled entry: ' + res[0]); } } return executeCommand("taskComplete", params); } function flatten_cmd_args(cmd_args = {}) { var ret_value = ''; for(var current_key in cmd_args){ ret_value += current_key + " " + cmd_args[current_key] + " "; } return ret_value.trim(); } //replace all occurences of textToReplace with replaceWith string String.prototype.replaceAll = function(textToReplace, replaceWith) { return this.split(textToReplace).join(replaceWith); }; function checkCommandSanitized(cmd = '', cmd_args = {}) { var cmd_lower = cmd.toLowerCase() + ' ' + flatten_cmd_args(cmd_args) for (var i = 0; i < SANITIZED_ARG_NAMES.length; i++) { var current_arg_name_lower = SANITIZED_ARG_NAMES[i].toLowerCase(); var regex = new RegExp(current_arg_name_lower, "g"); if ((cmd_lower.match(regex) || []).length > 1) { throw new Error('Error, The value of ' + SANITIZED_ARG_NAMES[i] + ' is malformed.'); } cmd_lower = cmd_lower.replaceAll(current_arg_name_lower, '') } } function setNextRun(ids, playbookId, pollingCommand, pollingCommandArgName, pendingIds, interval, timeout, tag, additionalArgNames, additionalArgValues, extractMode) { var idsStr = ids.replace(/"/g, '\\"'); var playbookIdStr = ''; if (playbookId !== undefined) { playbookIdStr = ' playbookId="' + playbookId + '"'; } var cmd = '!GenericPollingScheduledTask pollingCommand="' + pollingCommand + '" pollingCommandArgName="' + pollingCommandArgName + '"' + playbookIdStr; cmd += ' ids="' + idsStr + '" pendingIds="' + pendingIds.replace(/"/g,'\\"') + '" interval="' + interval + '" timeout="' + (parseInt(timeout) - parseInt(interval)) + '" tag="' + tag + '"'; cmd += ' additionalPollingCommandArgNames="' + additionalArgNames.replace(/"/g,'\\"') + '" additionalPollingCommandArgValues="' + additionalArgValues.replace(/"/g,'\\"') + '"'; if (extractMode !== undefined) { cmd += ' extractMode="' + extractMode + '" auto-extract="' + extractMode + '"'; } checkCommandSanitized(cmd) return executeCommand("ScheduleCommand", { 'command': cmd, 'cron': '*/' + interval + ' * * * *', 'times': 1 }); } function shouldRunWithGuid() { res = getDemistoVersion(); platform = res.platform; version = res.version; buildNumber = res.buildNumber; // conditions to add when the feature is supported in XSIAM: // ((platform === "x2") && (compareVersions(version, MINIMUM_XSIAM_VERSION) >= 0) && (parseInt(buildNumber) >= MINIMUM_BUILD_NUMBER_XSIAM)) // Checking if the stopScheduleEntry command is available. // If not, we are running on an older version of platform and we need to use the old polling mechanism. // The try/catch mechanism is to support development and to ignore parseInt errors. try { var platformMatch = (platform === "xsoar"); var versionMatch = (compareVersions(version, MINIMUM_XSOAR_VERSION) >= 0); var buildMatch = (parseInt(buildNumber) >= MINIMUM_BUILD_NUMBER_XSOAR); if (platformMatch && versionMatch && buildMatch) { logDebug('[GenericPollingScheduledTask] Using GUID flow. platform=' + platform + ' version=' + version + ' buildNumber=' + buildNumber); return true; } } catch (err) { logDebug('[GenericPollingScheduledTask] shouldRunWithGuid error, using OLD flow: ' + err); return false; } logDebug('[GenericPollingScheduledTask] Using OLD flow. platform=' + platform + ' version=' + version + ' buildNumber=' + buildNumber); } function genericPollingScheduled(){ try { shouldRunWithGuid = shouldRunWithGuid(); if (shouldRunWithGuid) { var endTime = stringToDate(args.endTime, "%Y-%m-%d %H:%M:%S"); var currentTime = new Date(); if (currentTime >= endTime) { logDebug('[GenericPollingScheduledTask] GUID flow: endTime reached, finishing (YES).'); return finish(args.playbookId, args.tag, undefined, args.scheduledEntryGuid); } } else { if (args.timeout <= 0) { logDebug('[GenericPollingScheduledTask] OLD flow: timeout reached, finishing (YES).'); return finish(args.playbookId, args.tag, undefined, args.scheduledEntryGuid); } } // Get ids that have not finished yet var ids = argToList(args.ids); for (var i = 0; i < ids.length; i++) { ids[i] = ids[i].replace(/[\\]*"/g, ''); } // Set the context of the scheduled task to the local playbook context var idsToPoll = ids; var pendingPath = args.pendingIds; if ('playbookId' in args) { playbookContext = 'subplaybook-' + args.playbookId; pendingPath = playbookContext + "." + args.pendingIds; } var pendings = dq(invContext, pendingPath); // Compatibility fix: on some platform versions dq() wraps the result in a // {"result": <value>} envelope instead of returning the bare value/list. // Unwrap it so the pending-vs-ids intersection behaves consistently across platforms. if (pendings && (typeof pendings === 'object') && !Array.isArray(pendings) && ('result' in pendings)) { logDebug('[GenericPollingScheduledTask] dq() returned a {"result":...} envelope - unwrapping.'); pendings = pendings.result; } logDebug('[GenericPollingScheduledTask] pendingPath=' + pendingPath + ' | pendings=' + JSON.stringify(pendings) + ' | ids=' + JSON.stringify(ids)); if (pendings === null) { logDebug('[GenericPollingScheduledTask] No pending ids found in context, finishing (YES).'); return finish(args.playbookId, args.tag, undefined, args.scheduledEntryGuid); } var idsStrArr = listOfStrings(ids); var pendingsStrArr = listOfStrings(pendings); idsToPoll = intersect(idsStrArr, pendingsStrArr); if (idsToPoll.length === 0) { logDebug('[GenericPollingScheduledTask] All ids finished (none still pending), finishing (YES).'); return finish(args.playbookId, args.tag, undefined, args.scheduledEntryGuid); } logDebug('[GenericPollingScheduledTask] Still polling ' + idsToPoll.length + ' id(s).'); // Run the polling command for each id var pollingCommandArgs = {}; var names = argToList(args.additionalPollingCommandArgNames); var values = argToList(args.additionalPollingCommandArgValues); for (var index = 0; index < names.length; index++) pollingCommandArgs[names[index]] = values[index]; pollingCommandArgs[args.pollingCommandArgName] = idsToPoll.join(','); checkCommandSanitized(args.pollingCommand, pollingCommandArgs); var res = executeCommand(args.pollingCommand, pollingCommandArgs); // Change the context output of the polling results to the local playbook context if ('playbookId' in args) { for (var i = 0; i < res.length; i++) { if ('EntryContext' in res[i]) { for (var k in res[i].EntryContext) { res[i].EntryContext[playbookContext + "." + k] = res[i].EntryContext[k]; delete res[i].EntryContext[k]; } } } } if (!shouldRunWithGuid) { // Schedule the next iteration, old version. var scheduleTaskRes = setNextRun(args.ids, args.playbookId, args.pollingCommand, args.pollingCommandArgName, args.pendingIds, args.interval, args.timeout, args.tag, args.additionalPollingCommandArgNames, args.additionalPollingCommandArgValues, args.extractMode); if (isError(scheduleTaskRes[0])) { logError('[GenericPollingScheduledTask] OLD flow: setNextRun FAILED - polling will NOT continue: ' + scheduleTaskRes[0]); res.push(scheduleTaskRes); } } return res; } catch (err) { finish(args.playbookId, args.tag, err, args.scheduledEntryGuid); throw err; } } function main() { return genericPollingScheduled(); } return main();
README
Runs the polling command repeatedly, completes a blocking manual task when polling is done.
Script Data
| Name | Description |
|---|---|
| Script Type | javascript |
| Cortex XSOAR Version | 5.0.0 |
Inputs
| Argument Name | Description |
|---|---|
| ids | List of IDs to poll |
| pendingIds | IDs with pending status |
| pollingCommand | Name of the polling command to run |
| pollingCommandArgName | Name of the argument of the polling command |
| interval | Polling frequency - how often the polling command should run (minutes) |
| timeout | How much time to poll before declaring a timeout and resuming the playbook (minutes) |
| playbookId | The ID of the playbook that contains the manual task which will be completed once the polling is done. |
| tag | The tag of the blocking manual task (“Wait For Polling Task To Finish”) |
| additionalPollingCommandArgNames | Names of additional arguments for the polling command (e.g. arg1,arg2,…) |
| additionalPollingCommandArgValues | Commas separated arguments values of the polling command |
| scheduledEntryGuid | The GUID of the scheduled entry that runs the polling command. |
| endTime | The time to end the polling. |
Outputs
There are no outputs for this script.