UNPKG

now-sync

Version:

A tool to help developers sync their JavaScript resources with ServiceNow.

215 lines (191 loc) 6.75 kB
const _ = require('lodash'); const moment = require('moment'); const { get, put } = require('./api'); const { parseConfigFile } = require('./config'); /** * Creates a Table API url based on given table name and options. See http://wiki.servicenow.com/index.php?title=Table_API#GET_.2Fapi.2Fnow.2Fv1.2Ftable.2F.28tableName.29 for details. * * @param {string} tableName The ServiceNow Table’s API name * @param {object} opts Table API options * @param {string[]} opts.fields The field names to retrieve values for. * @param {string} opts.view The ServiceNow view for that Table. * @param {number} [opts.limit=10000] The maximum number of records to retrieve. * @param {number} opts.offset The starting row of the result set. * @param {(string|boolean)} [opts.displayValue='all'] * @param {boolean} opts.excludeReferenceLink * @param {string} opts.query The contents of the sysparm_query url parameter in list views. * @returns {string} the Table API url */ function buildTableApiUrl(tableName, opts) { return opts ? `${buildTableApiBaseUrl(tableName)}?${buildTableApiOptions(opts)}` : buildTableApiBaseUrl(tableName); } exports.buildTableApiUrl = buildTableApiUrl; /** * Creates a Table API url based on a given table name. * * @param {string} tableName The ServiceNow Table’s API Name * @returns {string} Table API url */ function buildTableApiBaseUrl(tableName) { const url = getInstanceUrl(); return `${url}/api/now/v1/table/${tableName}`; } exports.buildTableApiBaseUrl = buildTableApiBaseUrl; /** * Creates a query parameter string based on options. See http://wiki.servicenow.com/index.php?title=Table_API#GET_.2Fapi.2Fnow.2Fv1.2Ftable.2F.28tableName.29 for details. * * @param {object} opts Table API options * @param {string[]} opts.fields The field names to retrieve values for. * @param {string} opts.view The ServiceNow view for that Table. * @param {number} [opts.limit=10000] The maximum number of records to retrieve. * @param {number} opts.offset The starting row of the result set. * @param {(string|boolean)} [opts.displayValue='all'] * @param {boolean} opts.excludeReferenceLink * @param {string} opts.query The contents of the sysparm_query url parameter in list views. * @returns {string} the url parameter string */ function buildTableApiOptions(opts) { const defaultOpts = { fields: [], view: '', limit: 10000, offset: 0, displayValue: 'all', excludeReferenceLink: true, query: '' // needs to be URI-encoded (copy-pasting from the URL bar in ServiceNow is fine) }; const optsQueryMap = { displayValue: 'sysparm_display_value', excludeReferenceLink: 'sysparm_exclude_reference_link', fields: 'sysparm_fields', limit: 'sysparm_limit', offset: 'sysparm_offset', query: 'sysparm_query', view: 'sysparm_view' }; const finalOpts = _.assign({}, defaultOpts, opts); if (finalOpts.fields.indexOf('sys_id') === -1) { finalOpts.fields.push('sys_id'); } finalOpts.fields = finalOpts.fields.join(','); // Input: { limit: 10, query: 'stateIN0%2C2%5EORDERBYpriority' } // Output: 'sysparm_limit=10&sysparm_query=stateIN0%2C2%5EORDERBYpriority' const optStr = _.map( _.keys(finalOpts), optKey => `${optsQueryMap[optKey]}=${finalOpts[optKey]}` ).join('&'); return optStr; } exports.buildTableApiOptions = buildTableApiOptions; /** * Converts a datetime string from a ServiceNow record to a moment object in the UTC timezone. * * @param {string} datetimeStr A ServiceNow datetime string * @returns {moment} The moment format of the original date */ function convertServiceNowDatetimeToMoment(datetimeStr) { const recordDatetimeSplit = datetimeStr.split(' '); const isoStr = `${recordDatetimeSplit[0]}T${recordDatetimeSplit[1]}.000Z`; return moment.utc(isoStr); } exports.convertServiceNowDatetimeToMoment = convertServiceNowDatetimeToMoment; /** * Converts a Javascript Date instance to a ServiceNow datetime string. * * @param {date} dateObj A Date instance * @returns {string} The ServiceNow datetime */ function convertDateToServiceNowDatetime(dateObj) { return moment.utc(dateObj.toISOString()).format('YYYY-MM-DD HH:mm:ss'); } exports.convertDateToServiceNowDatetime = convertDateToServiceNowDatetime; /** * Returns the configured ServiceNow instance URL. * * @returns {string} The instance url */ function getInstanceUrl() { const { url } = parseConfigFile(true); return url; } exports.getInstanceUrl = getInstanceUrl; /** * Retrieves field values for a ServiceNow record. * * @param {string} table ServiceNow table’s API Name * @param {string} sysId ServiceNow record’s sys_id * @param {string[]} fields Array of field names * @returns {promise} A promise resolving to the record object */ function getRecord(table, sysId, fields) { const url = `${buildTableApiBaseUrl(table)}/${sysId}?${buildTableApiOptions({ fields, displayValue: false })}`; return get(url) .then(response => { if (response.error && response.error.detail) { throw new Error(response.error.detail); } return response.result; }) .then(record => { if (record['sys_scope.scope']) { record.sys_scope = record['sys_scope.scope']; delete record['sys_scope.scope']; } if (record['web_service_definition.service_id']) { record.web_service_definition = record['web_service_definition.service_id']; delete record['web_service_definition.service_id']; } return record; }) .catch(e => { let errorMsg; switch (e.name) { case 'TypeError': { errorMsg = `Record ${table}.${sysId} not found.`; break; } default: { errorMsg = `An error occurred when retrieving record ${table}.${sysId}: ${e}`; break; } } const formattedError = new Error(errorMsg); formattedError.stack = e.stack; formattedError.name = e.name; throw formattedError; }); } exports.getRecord = getRecord; /** * Updates a ServiceNow record with given field values * * @param {string} table Table API Name * @param {string} sysId Record sys_id * @param {object} body An object containing field:value hashes * @returns {promise} The promise of the API call */ async function updateRecord(table, sysId, body) { const url = `${buildTableApiBaseUrl(table)}/${sysId}`; const filteredBody = _.omit(body, ['sys_id']); const opts = { body: JSON.stringify(filteredBody) }; try { const response = await put(url, opts); return { response, body, table, sysId }; } catch (e) { throw e; } } exports.updateRecord = updateRecord;