diff --git a/packages/nodes-base/credentials/ServiceNowOAuth2Api.credentials.ts b/packages/nodes-base/credentials/ServiceNowOAuth2Api.credentials.ts
new file mode 100644
index 0000000000..d12b0cd8f1
--- /dev/null
+++ b/packages/nodes-base/credentials/ServiceNowOAuth2Api.credentials.ts
@@ -0,0 +1,62 @@
+import {
+ ICredentialType,
+ NodePropertyTypes,
+} from 'n8n-workflow';
+
+export class ServiceNowOAuth2Api implements ICredentialType {
+ name = 'serviceNowOAuth2Api';
+ extends = [
+ 'oAuth2Api',
+ ];
+ displayName = 'ServiceNow OAuth2 API';
+ documentationUrl = 'serviceNow';
+ properties = [
+ {
+ displayName: 'Subdomain',
+ name: 'subdomain',
+ type: 'string' as NodePropertyTypes,
+ default: '',
+ placeholder: 'n8n',
+ description: 'The subdomain of your ServiceNow environment',
+ required: true,
+ },
+ {
+ displayName: 'Authorization URL',
+ name: 'authUrl',
+ type: 'hidden' as NodePropertyTypes,
+ default: '=https://{{$self["subdomain"]}}.service-now.com/oauth_auth.do',
+ required: true,
+ },
+ {
+ displayName: 'Access Token URL',
+ name: 'accessTokenUrl',
+ type: 'hidden' as NodePropertyTypes,
+ default: '=https://{{$self["subdomain"]}}.service-now.com/oauth_token.do',
+ required: true,
+ },
+ {
+ displayName: 'Scope',
+ name: 'scope',
+ type: 'hidden' as NodePropertyTypes,
+ default: 'useraccount',
+ },
+ {
+ displayName: 'Auth URI Query Parameters',
+ name: 'authQueryParameters',
+ type: 'hidden' as NodePropertyTypes,
+ default: 'response_type=code',
+ },
+ {
+ displayName: 'Auth URI Query Parameters',
+ name: 'authQueryParameters',
+ type: 'hidden' as NodePropertyTypes,
+ default: 'grant_type=authorization_code',
+ },
+ {
+ displayName: 'Authentication',
+ name: 'authentication',
+ type: 'hidden' as NodePropertyTypes,
+ default: 'header',
+ },
+ ];
+}
diff --git a/packages/nodes-base/nodes/ServiceNow/BusinessServiceDescription.ts b/packages/nodes-base/nodes/ServiceNow/BusinessServiceDescription.ts
new file mode 100644
index 0000000000..650480e9f2
--- /dev/null
+++ b/packages/nodes-base/nodes/ServiceNow/BusinessServiceDescription.ts
@@ -0,0 +1,137 @@
+import {
+ INodeProperties,
+} from 'n8n-workflow';
+
+export const businessServiceOperations = [
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ displayOptions: {
+ show: {
+ resource: [
+ 'businessService',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Get All',
+ value: 'getAll',
+ },
+ ],
+ default: 'getAll',
+ },
+] as INodeProperties[];
+
+export const businessServiceFields = [
+
+ /* -------------------------------------------------------------------------- */
+ /* businessService:getAll */
+ /* -------------------------------------------------------------------------- */
+ {
+ displayName: 'Return All',
+ name: 'returnAll',
+ type: 'boolean',
+ displayOptions: {
+ show: {
+ operation: [
+ 'getAll',
+ ],
+ resource: [
+ 'businessService',
+ ],
+ },
+ },
+ default: false,
+ description: 'If all results should be returned or only up to a given limit',
+ },
+ {
+ displayName: 'Limit',
+ name: 'limit',
+ type: 'number',
+ displayOptions: {
+ show: {
+ operation: [
+ 'getAll',
+ ],
+ resource: [
+ 'businessService',
+ ],
+ returnAll: [
+ false,
+ ],
+ },
+ },
+ typeOptions: {
+ minValue: 1,
+ maxValue: 500,
+ },
+ default: 50,
+ description: 'The max number of results to return',
+ },
+ {
+ displayName: 'Options',
+ name: 'options',
+ type: 'collection',
+ placeholder: 'Add Field',
+ displayOptions: {
+ show: {
+ resource: [
+ 'businessService',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ },
+ },
+ default: {},
+ options: [
+ {
+ displayName: 'Exclude Reference Link',
+ name: 'sysparm_exclude_reference_link',
+ type: 'boolean',
+ default: false,
+ description: 'Whether to exclude Table API links for reference fields',
+ },
+ {
+ displayName: 'Fields',
+ name: 'sysparm_fields',
+ type: 'multiOptions',
+ typeOptions: {
+ loadOptionsMethod: 'getColumns',
+ },
+ default: '',
+ description: 'A list of fields to return',
+ },
+ {
+ displayName: 'Filter',
+ name: 'sysparm_query',
+ type: 'string',
+ default: '',
+ description: 'An encoded query string used to filter the results. More info',
+ },
+ {
+ displayName: 'Return Values',
+ name: 'sysparm_display_value',
+ type: 'options',
+ options: [
+ {
+ name: 'Actual Values',
+ value: 'false',
+ },
+ {
+ name: 'Both',
+ value: 'all',
+ },
+ {
+ name: 'Display Values',
+ value: 'true',
+ },
+ ],
+ default: 'false',
+ description: 'Choose which values to return',
+ },
+ ],
+ },
+] as INodeProperties[];
diff --git a/packages/nodes-base/nodes/ServiceNow/ConfigurationItemsDescription.ts b/packages/nodes-base/nodes/ServiceNow/ConfigurationItemsDescription.ts
new file mode 100644
index 0000000000..bb95b3a955
--- /dev/null
+++ b/packages/nodes-base/nodes/ServiceNow/ConfigurationItemsDescription.ts
@@ -0,0 +1,137 @@
+import {
+ INodeProperties,
+} from 'n8n-workflow';
+
+export const configurationItemsOperations = [
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ displayOptions: {
+ show: {
+ resource: [
+ 'configurationItems',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Get All',
+ value: 'getAll',
+ },
+ ],
+ default: 'getAll',
+ },
+] as INodeProperties[];
+
+export const configurationItemsFields = [
+
+ /* -------------------------------------------------------------------------- */
+ /* configurationItems:getAll */
+ /* -------------------------------------------------------------------------- */
+ {
+ displayName: 'Return All',
+ name: 'returnAll',
+ type: 'boolean',
+ displayOptions: {
+ show: {
+ operation: [
+ 'getAll',
+ ],
+ resource: [
+ 'configurationItems',
+ ],
+ },
+ },
+ default: false,
+ description: 'If all results should be returned or only up to a given limit',
+ },
+ {
+ displayName: 'Limit',
+ name: 'limit',
+ type: 'number',
+ displayOptions: {
+ show: {
+ operation: [
+ 'getAll',
+ ],
+ resource: [
+ 'configurationItems',
+ ],
+ returnAll: [
+ false,
+ ],
+ },
+ },
+ typeOptions: {
+ minValue: 1,
+ maxValue: 500,
+ },
+ default: 50,
+ description: 'The max number of results to return',
+ },
+ {
+ displayName: 'Options',
+ name: 'options',
+ type: 'collection',
+ placeholder: 'Add Field',
+ displayOptions: {
+ show: {
+ resource: [
+ 'configurationItems',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ },
+ },
+ default: {},
+ options: [
+ {
+ displayName: 'Exclude Reference Link',
+ name: 'sysparm_exclude_reference_link',
+ type: 'boolean',
+ default: false,
+ description: 'Whether to exclude Table API links for reference fields',
+ },
+ {
+ displayName: 'Fields',
+ name: 'sysparm_fields',
+ type: 'multiOptions',
+ typeOptions: {
+ loadOptionsMethod: 'getColumns',
+ },
+ default: '',
+ description: 'A list of fields to return',
+ },
+ {
+ displayName: 'Filter',
+ name: 'sysparm_query',
+ type: 'string',
+ default: '',
+ description: 'An encoded query string used to filter the results. More info',
+ },
+ {
+ displayName: 'Return Values',
+ name: 'sysparm_display_value',
+ type: 'options',
+ options: [
+ {
+ name: 'Actual Values',
+ value: 'false',
+ },
+ {
+ name: 'Both',
+ value: 'all',
+ },
+ {
+ name: 'Display Values',
+ value: 'true',
+ },
+ ],
+ default: 'false',
+ description: 'Choose which values to return',
+ },
+ ],
+ },
+] as INodeProperties[];
diff --git a/packages/nodes-base/nodes/ServiceNow/DepartmentDescription.ts b/packages/nodes-base/nodes/ServiceNow/DepartmentDescription.ts
new file mode 100644
index 0000000000..cc4a3871b4
--- /dev/null
+++ b/packages/nodes-base/nodes/ServiceNow/DepartmentDescription.ts
@@ -0,0 +1,137 @@
+import {
+ INodeProperties,
+} from 'n8n-workflow';
+
+export const departmentOperations = [
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ displayOptions: {
+ show: {
+ resource: [
+ 'department',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Get All',
+ value: 'getAll',
+ },
+ ],
+ default: 'getAll',
+ },
+] as INodeProperties[];
+
+export const departmentFields = [
+
+ /* -------------------------------------------------------------------------- */
+ /* department:getAll */
+ /* -------------------------------------------------------------------------- */
+ {
+ displayName: 'Return All',
+ name: 'returnAll',
+ type: 'boolean',
+ displayOptions: {
+ show: {
+ operation: [
+ 'getAll',
+ ],
+ resource: [
+ 'department',
+ ],
+ },
+ },
+ default: false,
+ description: 'If all results should be returned or only up to a given limit',
+ },
+ {
+ displayName: 'Limit',
+ name: 'limit',
+ type: 'number',
+ displayOptions: {
+ show: {
+ operation: [
+ 'getAll',
+ ],
+ resource: [
+ 'department',
+ ],
+ returnAll: [
+ false,
+ ],
+ },
+ },
+ typeOptions: {
+ minValue: 1,
+ maxValue: 500,
+ },
+ default: 50,
+ description: 'The max number of results to return',
+ },
+ {
+ displayName: 'Options',
+ name: 'options',
+ type: 'collection',
+ placeholder: 'Add Field',
+ displayOptions: {
+ show: {
+ resource: [
+ 'department',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ },
+ },
+ default: {},
+ options: [
+ {
+ displayName: 'Exclude Reference Link',
+ name: 'sysparm_exclude_reference_link',
+ type: 'boolean',
+ default: false,
+ description: 'Whether to exclude Table API links for reference fields',
+ },
+ {
+ displayName: 'Fields',
+ name: 'sysparm_fields',
+ type: 'multiOptions',
+ typeOptions: {
+ loadOptionsMethod: 'getColumns',
+ },
+ default: '',
+ description: 'A list of fields to return',
+ },
+ {
+ displayName: 'Filter',
+ name: 'sysparm_query',
+ type: 'string',
+ default: '',
+ description: 'An encoded query string used to filter the results. More info',
+ },
+ {
+ displayName: 'Return Values',
+ name: 'sysparm_display_value',
+ type: 'options',
+ options: [
+ {
+ name: 'Actual Values',
+ value: 'false',
+ },
+ {
+ name: 'Both',
+ value: 'all',
+ },
+ {
+ name: 'Display Values',
+ value: 'true',
+ },
+ ],
+ default: 'false',
+ description: 'Choose which values to return',
+ },
+ ],
+ },
+] as INodeProperties[];
diff --git a/packages/nodes-base/nodes/ServiceNow/DictionaryDescription.ts b/packages/nodes-base/nodes/ServiceNow/DictionaryDescription.ts
new file mode 100644
index 0000000000..fd0f14cd4f
--- /dev/null
+++ b/packages/nodes-base/nodes/ServiceNow/DictionaryDescription.ts
@@ -0,0 +1,137 @@
+import {
+ INodeProperties,
+} from 'n8n-workflow';
+
+export const dictionaryOperations = [
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ displayOptions: {
+ show: {
+ resource: [
+ 'dictionary',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Get All',
+ value: 'getAll',
+ },
+ ],
+ default: 'getAll',
+ },
+] as INodeProperties[];
+
+export const dictionaryFields = [
+
+ /* -------------------------------------------------------------------------- */
+ /* dictionary:getAll */
+ /* -------------------------------------------------------------------------- */
+ {
+ displayName: 'Return All',
+ name: 'returnAll',
+ type: 'boolean',
+ displayOptions: {
+ show: {
+ operation: [
+ 'getAll',
+ ],
+ resource: [
+ 'dictionary',
+ ],
+ },
+ },
+ default: false,
+ description: 'If all results should be returned or only up to a given limit',
+ },
+ {
+ displayName: 'Limit',
+ name: 'limit',
+ type: 'number',
+ displayOptions: {
+ show: {
+ operation: [
+ 'getAll',
+ ],
+ resource: [
+ 'dictionary',
+ ],
+ returnAll: [
+ false,
+ ],
+ },
+ },
+ typeOptions: {
+ minValue: 1,
+ maxValue: 500,
+ },
+ default: 50,
+ description: 'The max number of results to return',
+ },
+ {
+ displayName: 'Options',
+ name: 'options',
+ type: 'collection',
+ placeholder: 'Add Field',
+ displayOptions: {
+ show: {
+ resource: [
+ 'dictionary',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ },
+ },
+ default: {},
+ options: [
+ {
+ displayName: 'Exclude Reference Link',
+ name: 'sysparm_exclude_reference_link',
+ type: 'boolean',
+ default: false,
+ description: 'Whether to exclude Table API links for reference fields',
+ },
+ {
+ displayName: 'Fields',
+ name: 'sysparm_fields',
+ type: 'multiOptions',
+ typeOptions: {
+ loadOptionsMethod: 'getColumns',
+ },
+ default: '',
+ description: 'A list of fields to return',
+ },
+ {
+ displayName: 'Filter',
+ name: 'sysparm_query',
+ type: 'string',
+ default: '',
+ description: 'An encoded query string used to filter the results. More info',
+ },
+ {
+ displayName: 'Return Values',
+ name: 'sysparm_display_value',
+ type: 'options',
+ options: [
+ {
+ name: 'Actual Values',
+ value: 'false',
+ },
+ {
+ name: 'Both',
+ value: 'all',
+ },
+ {
+ name: 'Display Values',
+ value: 'true',
+ },
+ ],
+ default: 'false',
+ description: 'Choose which values to return',
+ },
+ ],
+ },
+] as INodeProperties[];
diff --git a/packages/nodes-base/nodes/ServiceNow/GenericFunctions.ts b/packages/nodes-base/nodes/ServiceNow/GenericFunctions.ts
new file mode 100644
index 0000000000..4b069a4929
--- /dev/null
+++ b/packages/nodes-base/nodes/ServiceNow/GenericFunctions.ts
@@ -0,0 +1,98 @@
+import {
+ OptionsWithUri
+} from 'request';
+
+import {
+ IExecuteFunctions,
+ ILoadOptionsFunctions,
+} from 'n8n-core';
+
+import {
+ IDataObject,
+ INodePropertyOptions,
+ NodeApiError,
+} from 'n8n-workflow';
+
+export async function serviceNowApiRequest(this: IExecuteFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise { // tslint:disable-line:no-any
+
+ const credentials = this.getCredentials('serviceNowOAuth2Api');
+
+ const options: OptionsWithUri = {
+ headers: {},
+ method,
+ qs,
+ body,
+ uri: uri || `https://${credentials?.subdomain}.service-now.com/api${resource}`,
+ json: true,
+ };
+ if (!Object.keys(body).length) {
+ delete options.body;
+ }
+
+ if (Object.keys(option).length !== 0) {
+ Object.assign(options, option);
+ }
+
+ if (options.qs.limit) {
+ delete options.qs.limit;
+ }
+
+ try {
+
+ return await this.helpers.requestOAuth2!.call(this, 'serviceNowOAuth2Api', options);
+
+ } catch (error) {
+ throw new NodeApiError(this.getNode(), error);
+ }
+}
+
+export async function serviceNowRequestAllItems(this: IExecuteFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, query: IDataObject = {}): Promise { // tslint:disable-line:no-any
+
+ const returnData: IDataObject[] = [];
+ let responseData;
+
+ const page = 100;
+
+ query.sysparm_limit = page;
+
+ responseData = await serviceNowApiRequest.call(this, method, resource, body, query, undefined, { resolveWithFullResponse: true });
+ returnData.push.apply(returnData, responseData.body.result);
+
+ const quantity = responseData.headers['x-total-count'];
+ const iterations = Math.round(quantity / page) + (quantity % page ? 1 : 0);
+
+ for (let iteration = 1; iteration < iterations; iteration++) {
+ query.sysparm_limit = page;
+ query.sysparm_offset = iteration * page;
+ responseData = await serviceNowApiRequest.call(this, method, resource, body, query, undefined, { resolveWithFullResponse: true });
+
+ returnData.push.apply(returnData, responseData.body.result);
+ }
+
+ return returnData;
+}
+
+
+export const mapEndpoint = (resource: string, operation: string) => {
+ const resourceEndpoint = new Map([
+ ['tableRecord', 'sys_dictionary'],
+ ['businessService', 'cmdb_ci_service'],
+ ['configurationItems', 'cmdb_ci'],
+ ['department', 'cmn_department'],
+ ['dictionary', 'sys_dictionary'],
+ ['incident', 'incident'],
+ ['user', 'sys_user'],
+ ['userGroup', 'sys_user_group'],
+ ['userRole', 'sys_user_role'],
+ ]);
+ return resourceEndpoint.get(resource);
+};
+
+export const sortData = (returnData: INodePropertyOptions[]): INodePropertyOptions[] => {
+ returnData.sort((a, b) => {
+ if (a.name < b.name) { return -1; }
+ if (a.name > b.name) { return 1; }
+ return 0;
+ });
+ return returnData;
+};
diff --git a/packages/nodes-base/nodes/ServiceNow/IncidentDescription.ts b/packages/nodes-base/nodes/ServiceNow/IncidentDescription.ts
new file mode 100644
index 0000000000..41d1537ab5
--- /dev/null
+++ b/packages/nodes-base/nodes/ServiceNow/IncidentDescription.ts
@@ -0,0 +1,673 @@
+import {
+ INodeProperties,
+} from 'n8n-workflow';
+
+export const incidentOperations = [
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ displayOptions: {
+ show: {
+ resource: [
+ 'incident',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Create',
+ value: 'create',
+ },
+ {
+ name: 'Delete',
+ value: 'delete',
+ },
+ {
+ name: 'Get',
+ value: 'get',
+ },
+ {
+ name: 'Get All',
+ value: 'getAll',
+ },
+ {
+ name: 'Update',
+ value: 'update',
+ },
+ ],
+ default: 'get',
+ },
+] as INodeProperties[];
+
+export const incidentFields = [
+ /* -------------------------------------------------------------------------- */
+ /* incident:create */
+ /* -------------------------------------------------------------------------- */
+
+ {
+ displayName: 'Short Description',
+ name: 'short_description',
+ type: 'string',
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'incident',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ required: true,
+ description: 'Short description of the incident',
+ },
+ {
+ displayName: 'Additional Fields',
+ name: 'additionalFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ displayOptions: {
+ show: {
+ resource: [
+ 'incident',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ default: {},
+ options: [
+ {
+ displayName: 'Assigned To',
+ name: 'assigned_to',
+ type: 'options',
+ typeOptions: {
+ loadOptionsMethod: 'getUsers',
+ loadOptionsDependsOn: [
+ 'additionalFields.assignment_group',
+ ],
+ },
+ default: '',
+ description: 'Which user is the incident assigned to. Requires the selection of an assignment group',
+ },
+ {
+ displayName: 'Assignment Group',
+ name: 'assignment_group',
+ type: 'options',
+ typeOptions: {
+ loadOptionsMethod: 'getAssignmentGroups',
+ },
+ default: '',
+ description: 'The assignment group of the incident',
+ },
+ {
+ displayName: 'Business Service',
+ name: 'business_service',
+ type: 'options',
+ typeOptions: {
+ loadOptionsMethod: 'getBusinessServices',
+ },
+ default: '',
+ description: 'The business service',
+ },
+ {
+ displayName: 'Caller ID',
+ name: 'caller_id',
+ type: 'string',
+ default: '',
+ description: 'The unique identifier of the caller of the incident',
+ },
+ {
+ displayName: 'Category',
+ name: 'category',
+ type: 'options',
+ typeOptions: {
+ loadOptionsMethod: 'getIncidentCategories',
+ },
+ default: '',
+ description: 'The category of the incident',
+ },
+ {
+ displayName: 'Close Notes',
+ name: 'close_notes',
+ type: 'string',
+ default: '',
+ description: 'The close notes for the incident',
+ },
+ {
+ displayName: 'Configuration Items',
+ name: 'cmdb_ci',
+ type: 'multiOptions',
+ typeOptions: {
+ loadOptionsMethod: 'getConfigurationItems',
+ },
+ default: '',
+ description: 'Configuration Items, \'cmdb_ci\' in metadata',
+ },
+ {
+ displayName: 'Contact Type',
+ name: 'contact_type',
+ type: 'options',
+ options: [
+ {
+ name: 'Email',
+ value: 'email',
+ },
+ {
+ name: 'Phone',
+ value: 'phone',
+ },
+ {
+ name: 'Self Service',
+ value: 'self-service',
+ },
+ {
+ name: 'Walk In',
+ value: 'walk-in',
+ },
+ ],
+ default: '',
+ description: 'The contact type',
+ },
+ {
+ displayName: 'Description',
+ name: 'description',
+ type: 'string',
+ default: '',
+ description: 'The description of the incident',
+ },
+ {
+ displayName: 'Impact',
+ name: 'impact',
+ type: 'options',
+ options: [
+ {
+ name: 'Low',
+ value: 1,
+ },
+ {
+ name: 'Medium',
+ value: 2,
+ },
+ {
+ name: 'High',
+ value: 3,
+ },
+ ],
+ default: '',
+ description: 'The impact of the incident',
+ },
+ {
+ displayName: 'Resolution Code',
+ name: 'close_code',
+ type: 'options',
+ typeOptions: {
+ loadOptionsMethod: 'getIncidentResolutionCodes',
+ },
+ default: '',
+ description: 'The resolution code of the incident. \'close_code\' in metadata',
+ },
+ {
+ displayName: 'State',
+ name: 'state',
+ type: 'options',
+ typeOptions: {
+ loadOptionsMethod: 'getIncidentStates',
+ },
+ default: '',
+ description: 'The state of the incident',
+ },
+ {
+ displayName: 'Subcategory',
+ name: 'subcategory',
+ type: 'options',
+ typeOptions: {
+ loadOptionsMethod: 'getIncidentSubcategories',
+ loadOptionsDependsOn: [
+ 'additionalFields.category',
+ ],
+ },
+ default: '',
+ description: 'The subcategory of the incident',
+ },
+ {
+ displayName: 'Urgency',
+ name: 'urgency',
+ type: 'options',
+ options: [
+ {
+ name: 'Low',
+ value: 1,
+ },
+ {
+ name: 'Medium',
+ value: 2,
+ },
+ {
+ name: 'High',
+ value: 3,
+ },
+ ],
+ default: '',
+ description: 'The urgency of the incident',
+ },
+ ],
+ },
+
+ /* -------------------------------------------------------------------------- */
+ /* incident:getAll */
+ /* -------------------------------------------------------------------------- */
+ {
+ displayName: 'Return All',
+ name: 'returnAll',
+ type: 'boolean',
+ displayOptions: {
+ show: {
+ operation: [
+ 'getAll',
+ ],
+ resource: [
+ 'incident',
+ ],
+ },
+ },
+ default: false,
+ description: 'If all results should be returned or only up to a given limit',
+ },
+ {
+ displayName: 'Limit',
+ name: 'limit',
+ type: 'number',
+ displayOptions: {
+ show: {
+ operation: [
+ 'getAll',
+ ],
+ resource: [
+ 'incident',
+ ],
+ returnAll: [
+ false,
+ ],
+ },
+ },
+ typeOptions: {
+ minValue: 1,
+ maxValue: 500,
+ },
+ default: 50,
+ description: 'The max number of results to return',
+ },
+ {
+ displayName: 'Options',
+ name: 'options',
+ type: 'collection',
+ placeholder: 'Add Option',
+ displayOptions: {
+ show: {
+ resource: [
+ 'incident',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ },
+ },
+ default: {},
+ options: [
+ {
+ displayName: 'Exclude Reference Link',
+ name: 'sysparm_exclude_reference_link',
+ type: 'boolean',
+ default: false,
+ description: 'Whether to exclude Table API links for reference fields',
+ },
+ {
+ displayName: 'Fields',
+ name: 'sysparm_fields',
+ type: 'multiOptions',
+ typeOptions: {
+ loadOptionsMethod: 'getColumns',
+ },
+ default: '',
+ description: 'A list of fields to return',
+ },
+ {
+ displayName: 'Filter',
+ name: 'sysparm_query',
+ type: 'string',
+ default: '',
+ description: 'An encoded query string used to filter the results. More info',
+ },
+ {
+ displayName: 'Return Values',
+ name: 'sysparm_display_value',
+ type: 'options',
+ options: [
+ {
+ name: 'Actual Values',
+ value: 'false',
+ },
+ {
+ name: 'Both',
+ value: 'all',
+ },
+ {
+ name: 'Display Values',
+ value: 'true',
+ },
+ ],
+ default: 'false',
+ description: 'Choose which values to return',
+ },
+ ],
+ },
+
+ /* -------------------------------------------------------------------------- */
+ /* incident:get/delete */
+ /* -------------------------------------------------------------------------- */
+ {
+ displayName: 'Incident ID',
+ name: 'id',
+ type: 'string',
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'incident',
+ ],
+ operation: [
+ 'delete',
+ 'get',
+ ],
+ },
+ },
+ required: true,
+ description: 'Unique identifier of the incident',
+ },
+ {
+ displayName: 'Options',
+ name: 'options',
+ type: 'collection',
+ placeholder: 'Add Option',
+ displayOptions: {
+ show: {
+ resource: [
+ 'incident',
+ ],
+ operation: [
+ 'get',
+ ],
+ },
+ },
+ default: {},
+ options: [
+ {
+ displayName: 'Exclude Reference Link',
+ name: 'sysparm_exclude_reference_link',
+ type: 'boolean',
+ default: false,
+ description: 'Whether to exclude Table API links for reference fields',
+ },
+ {
+ displayName: 'Fields',
+ name: 'sysparm_fields',
+ type: 'multiOptions',
+ typeOptions: {
+ loadOptionsMethod: 'getColumns',
+ },
+ default: '',
+ description: 'A list of fields to return',
+ },
+ {
+ displayName: 'Return Values',
+ name: 'sysparm_display_value',
+ type: 'options',
+ options: [
+ {
+ name: 'Actual Values',
+ value: 'false',
+ },
+ {
+ name: 'Both',
+ value: 'all',
+ },
+ {
+ name: 'Display Values',
+ value: 'true',
+ },
+ ],
+ default: 'false',
+ description: 'Choose which values to return',
+ },
+ ],
+ },
+
+ /* -------------------------------------------------------------------------- */
+ /* incident:update */
+ /* -------------------------------------------------------------------------- */
+ {
+ displayName: 'Incident ID',
+ name: 'id',
+ type: 'string',
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'incident',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ required: true,
+ description: 'Unique identifier of the incident',
+ },
+ {
+ displayName: 'Update Fields',
+ name: 'updateFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ displayOptions: {
+ show: {
+ resource: [
+ 'incident',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ default: {},
+ options: [
+ {
+ displayName: 'Assigned To',
+ name: 'assigned_to',
+ type: 'options',
+ typeOptions: {
+ loadOptionsMethod: 'getUsers',
+ loadOptionsDependsOn: [
+ 'additionalFields.assignment_group',
+ ],
+ },
+ default: '',
+ description: 'Which user is the incident assigned to. Requires the selection of an assignment group',
+ },
+ {
+ displayName: 'Assignment Group',
+ name: 'assignment_group',
+ type: 'options',
+ typeOptions: {
+ loadOptionsMethod: 'getAssignmentGroups',
+ },
+ default: '',
+ description: 'The assignment group of the incident',
+ },
+ {
+ displayName: 'Business Service',
+ name: 'business_service',
+ type: 'options',
+ typeOptions: {
+ loadOptionsMethod: 'getBusinessServices',
+ },
+ default: '',
+ description: 'The business service',
+ },
+ {
+ displayName: 'Caller ID',
+ name: 'caller_id',
+ type: 'string',
+ default: '',
+ description: 'The unique identifier of the caller of the incident',
+ },
+ {
+ displayName: 'Category',
+ name: 'category',
+ type: 'options',
+ typeOptions: {
+ loadOptionsMethod: 'getIncidentCategories',
+ },
+ default: '',
+ description: 'The category of the incident',
+ },
+ {
+ displayName: 'Close Notes',
+ name: 'close_notes',
+ type: 'string',
+ default: '',
+ description: 'The close notes for the incident',
+ },
+ {
+ displayName: 'Configuration Items',
+ name: 'cmdb_ci',
+ type: 'multiOptions',
+ typeOptions: {
+ loadOptionsMethod: 'getConfigurationItems',
+ },
+ default: '',
+ description: 'Configuration Items, \'cmdb_ci\' in metadata',
+ },
+ {
+ displayName: 'Contact Type',
+ name: 'contact_type',
+ type: 'options',
+ options: [
+ {
+ name: 'Email',
+ value: 'email',
+ },
+ {
+ name: 'Phone',
+ value: 'phone',
+ },
+ {
+ name: 'Self Service',
+ value: 'self-service',
+ },
+ {
+ name: 'Walk In',
+ value: 'walk-in',
+ },
+ ],
+ default: '',
+ description: 'The contact type',
+ },
+ {
+ displayName: 'Description',
+ name: 'description',
+ type: 'string',
+ default: '',
+ description: 'The description of the incident',
+ },
+ {
+ displayName: 'Impact',
+ name: 'impact',
+ type: 'options',
+ options: [
+ {
+ name: 'Low',
+ value: 1,
+ },
+ {
+ name: 'Medium',
+ value: 2,
+ },
+ {
+ name: 'High',
+ value: 3,
+ },
+ ],
+ default: '',
+ description: 'The impact of the incident',
+ },
+ {
+ displayName: 'Resolution Code',
+ name: 'close_code',
+ type: 'options',
+ typeOptions: {
+ loadOptionsMethod: 'getIncidentResolutionCodes',
+ },
+ default: '',
+ description: 'The resolution code of the incident. \'close_code\' in metadata',
+ },
+ {
+ displayName: 'On Hold Reason',
+ name: 'hold_reason',
+ type: 'options',
+ typeOptions: {
+ loadOptionsMethod: 'getIncidentHoldReasons',
+ },
+ default: '',
+ description: 'The on hold reason for the incident. It applies if the state is On Hold
',
+ },
+ {
+ displayName: 'State',
+ name: 'state',
+ type: 'options',
+ typeOptions: {
+ loadOptionsMethod: 'getIncidentStates',
+ },
+ default: '',
+ description: 'The state of the incident',
+ },
+ {
+ displayName: 'Subcategory',
+ name: 'subcategory',
+ type: 'options',
+ typeOptions: {
+ loadOptionsMethod: 'getIncidentSubcategories',
+ loadOptionsDependsOn: [
+ 'additionalFields.category',
+ ],
+ },
+ default: '',
+ description: 'The subcategory of the incident',
+ },
+ {
+ displayName: 'Urgency',
+ name: 'urgency',
+ type: 'options',
+ options: [
+ {
+ name: 'Low',
+ value: 1,
+ },
+ {
+ name: 'Medium',
+ value: 2,
+ },
+ {
+ name: 'High',
+ value: 3,
+ },
+ ],
+ default: '',
+ description: 'The urgency of the incident',
+ },
+ ],
+ },
+] as INodeProperties[];
diff --git a/packages/nodes-base/nodes/ServiceNow/ServiceNow.node.ts b/packages/nodes-base/nodes/ServiceNow/ServiceNow.node.ts
new file mode 100644
index 0000000000..05eda728da
--- /dev/null
+++ b/packages/nodes-base/nodes/ServiceNow/ServiceNow.node.ts
@@ -0,0 +1,788 @@
+import {
+ IExecuteFunctions,
+ ILoadOptionsFunctions,
+} from 'n8n-core';
+
+import {
+ IDataObject,
+ INodeExecutionData,
+ INodePropertyOptions,
+ INodeType,
+ INodeTypeDescription,
+ NodeOperationError,
+} from 'n8n-workflow';
+
+import {
+ mapEndpoint,
+ serviceNowApiRequest,
+ serviceNowRequestAllItems,
+ sortData
+} from './GenericFunctions';
+
+import {
+ businessServiceFields,
+ businessServiceOperations,
+} from './BusinessServiceDescription';
+
+import {
+ configurationItemsFields,
+ configurationItemsOperations,
+} from './ConfigurationItemsDescription';
+
+import {
+ departmentFields,
+ departmentOperations,
+} from './DepartmentDescription';
+
+import {
+ dictionaryFields,
+ dictionaryOperations,
+} from './DictionaryDescription';
+
+import {
+ incidentFields,
+ incidentOperations,
+} from './IncidentDescription';
+
+import {
+ tableRecordFields,
+ tableRecordOperations,
+} from './TableRecordDescription';
+
+import {
+ userFields,
+ userOperations,
+} from './UserDescription';
+
+import {
+ userGroupFields,
+ userGroupOperations,
+} from './UserGroupDescription';
+
+import {
+ userRoleFields,
+ userRoleOperations,
+} from './UserRoleDescription';
+
+export class ServiceNow implements INodeType {
+ description: INodeTypeDescription = {
+ displayName: 'ServiceNow',
+ name: 'serviceNow',
+ icon: 'file:servicenow.svg',
+ group: ['output'],
+ version: 1,
+ subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
+ description: 'Consume ServiceNow API',
+ defaults: {
+ name: 'ServiceNow',
+ color: '#81b5a1',
+ },
+ inputs: ['main'],
+ outputs: ['main'],
+ credentials: [
+ {
+ name: 'serviceNowOAuth2Api',
+ required: true,
+ },
+ ],
+ properties: [
+ {
+ displayName: 'Resource',
+ name: 'resource',
+ type: 'options',
+ options: [
+ {
+ name: 'Business Service',
+ value: 'businessService',
+ },
+ {
+ name: 'Configuration Items',
+ value: 'configurationItems',
+ },
+ {
+ name: 'Department',
+ value: 'department',
+ },
+ {
+ name: 'Dictionary',
+ value: 'dictionary',
+ },
+ {
+ name: 'Incident',
+ value: 'incident',
+ },
+ {
+ name: 'Table Record',
+ value: 'tableRecord',
+ },
+ {
+ name: 'User',
+ value: 'user',
+ },
+ {
+ name: 'User Group',
+ value: 'userGroup',
+ },
+ {
+ name: 'User Role',
+ value: 'userRole',
+ },
+ ],
+ default: 'user',
+ description: 'Resource to consume',
+ },
+
+ // BUSINESS SERVICE
+ ...businessServiceOperations,
+ ...businessServiceFields,
+ // CONFIGURATION ITEMS
+ ...configurationItemsOperations,
+ ...configurationItemsFields,
+ // DEPARTMENT
+ ...departmentOperations,
+ ...departmentFields,
+ // DICTIONARY
+ ...dictionaryOperations,
+ ...dictionaryFields,
+ // INCIDENT
+ ...incidentOperations,
+ ...incidentFields,
+ // TABLE RECORD
+ ...tableRecordOperations,
+ ...tableRecordFields,
+ // USER
+ ...userOperations,
+ ...userFields,
+ // USER GROUP
+ ...userGroupOperations,
+ ...userGroupFields,
+ // USER ROLE
+ ...userRoleOperations,
+ ...userRoleFields,
+ ],
+ };
+
+ methods = {
+ loadOptions: {
+ async getTables(this: ILoadOptionsFunctions): Promise {
+ const returnData: INodePropertyOptions[] = [];
+ const response = await serviceNowApiRequest.call(this, 'GET', `/now/doc/table/schema`, {}, {});
+ for (const table of response.result) {
+ returnData.push({
+ name: table.label,
+ value: table.value,
+ description: table.value,
+ });
+ }
+ return sortData(returnData);
+ },
+ // Get all the table column to display them to user
+ async getColumns(this: ILoadOptionsFunctions): Promise {
+ const resource = this.getNodeParameter('resource', 0) as string;
+ const operation = this.getNodeParameter('operation', 0) as string;
+ const returnData: INodePropertyOptions[] = [];
+ let tableName;
+ if (resource === 'tableRecord') {
+ tableName = this.getNodeParameter('tableName') as string;
+ } else {
+ tableName = mapEndpoint(resource, operation);
+ }
+
+ const qs = {
+ sysparm_query: `name=${tableName}`,
+ sysparm_fields: 'column_label,element',
+ };
+ const response = await serviceNowApiRequest.call(this, 'GET', `/now/table/sys_dictionary`, {}, qs);
+ for (const column of response.result) {
+ if (column.element) {
+ returnData.push({
+ name: column.column_label,
+ value: column.element,
+ });
+ }
+ }
+ return sortData(returnData);
+ },
+ async getBusinessServices(this: ILoadOptionsFunctions): Promise {
+ const returnData: INodePropertyOptions[] = [];
+ const qs = {
+ sysparm_fields: 'name,sys_id',
+ };
+ const response = await serviceNowApiRequest.call(this, 'GET', `/now/table/cmdb_ci_service`, {}, qs);
+
+ for (const column of response.result) {
+ returnData.push({
+ name: column.name,
+ value: column.sys_id,
+ });
+ }
+ return sortData(returnData);
+ },
+ async getUsers(this: ILoadOptionsFunctions): Promise {
+ const returnData: INodePropertyOptions[] = [];
+ const resource = this.getNodeParameter('resource', 0) as string;
+ const operation = this.getNodeParameter('operation', 0) as string;
+
+ const qs = {
+ sysparm_fields: 'sys_id,user_name',
+ };
+ if (resource === 'incident' && operation === 'create') {
+ const additionalFields = this.getNodeParameter('additionalFields') as IDataObject;
+ const group = additionalFields.assignment_group;
+
+ const response = await serviceNowRequestAllItems.call(this, 'GET', '/now/table/sys_user_grmember', {}, {
+ sysparm_query: `group=${group}^`,
+ });
+
+ for (const column of response) {
+ if (column.user) {
+
+ const responseData = await serviceNowApiRequest.call(this, 'GET', `/now/table/sys_user/${column.user.value}`, {}, {});
+ const user = responseData.result;
+
+ returnData.push({
+ name: user.user_name,
+ value: user.sys_id,
+ });
+ }
+ }
+ } else {
+
+ const response = await serviceNowRequestAllItems.call(this, 'GET', '/now/table/sys_user', {}, qs);
+
+ for (const column of response) {
+ if (column.user_name) {
+ returnData.push({
+ name: column.user_name,
+ value: column.sys_id,
+ });
+ }
+ }
+ }
+ return sortData(returnData);
+ },
+ async getAssignmentGroups(this: ILoadOptionsFunctions): Promise {
+ const returnData: INodePropertyOptions[] = [];
+ const qs = {
+ sysparm_fields: 'sys_id,name',
+ };
+ const response = await serviceNowRequestAllItems.call(this, 'GET', '/now/table/sys_user_group', {}, qs);
+
+ for (const column of response) {
+ if (column.name) {
+ returnData.push({
+ name: column.name,
+ value: column.sys_id,
+ });
+ }
+ }
+ return sortData(returnData);
+ },
+ async getUserRoles(this: ILoadOptionsFunctions): Promise {
+ const returnData: INodePropertyOptions[] = [];
+ const qs = {
+ sysparm_fields: 'sys_id,name',
+ };
+ const response = await serviceNowRequestAllItems.call(this, 'GET', '/now/table/sys_user_role', {}, qs);
+
+ for (const column of response) {
+ if (column.name) {
+ returnData.push({
+ name: column.name,
+ value: column.sys_id,
+ });
+ }
+ }
+ return sortData(returnData);
+ },
+ async getConfigurationItems(this: ILoadOptionsFunctions): Promise {
+ const returnData: INodePropertyOptions[] = [];
+ const qs = {
+ sysparm_fields: 'sys_id,name,sys_class_name',
+ };
+ const response = await serviceNowRequestAllItems.call(this, 'GET', '/now/table/cmdb_ci', {}, qs);
+
+ for (const column of response) {
+ if (column.name) {
+ returnData.push({
+ name: column.name,
+ value: column.sys_id,
+ description: column.sys_class_name,
+ });
+ }
+ }
+ return sortData(returnData);
+ },
+ async getIncidentCategories(this: ILoadOptionsFunctions): Promise {
+ const returnData: INodePropertyOptions[] = [];
+ const qs = {
+ sysparm_fields: 'label,value',
+ sysparm_query: 'element=category^name=incident',
+ };
+ const response = await serviceNowRequestAllItems.call(this, 'GET', '/now/table/sys_choice', {}, qs);
+
+ for (const column of response) {
+ returnData.push({
+ name: column.label,
+ value: column.value,
+ });
+
+ }
+ return sortData(returnData);
+ },
+ async getIncidentSubcategories(this: ILoadOptionsFunctions): Promise {
+ const returnData: INodePropertyOptions[] = [];
+ const operation = this.getNodeParameter('operation');
+ let category;
+ if (operation === 'update') {
+ const updateFields = this.getNodeParameter('updateFields') as IDataObject;
+ category = updateFields.category;
+ } else {
+ const additionalFields = this.getNodeParameter('additionalFields') as IDataObject;
+ category = additionalFields.category;
+ }
+ const qs = {
+ sysparm_fields: 'label,value',
+ sysparm_query: `name=incident^element=subcategory^dependent_value=${category}`,
+ };
+ const response = await serviceNowRequestAllItems.call(this, 'GET', '/now/table/sys_choice', {}, qs);
+
+ for (const column of response) {
+ returnData.push({
+ name: column.label,
+ value: column.value,
+ });
+ }
+
+ return sortData(returnData);
+ },
+ async getIncidentStates(this: ILoadOptionsFunctions): Promise {
+ const returnData: INodePropertyOptions[] = [];
+ const qs = {
+ sysparm_fields: 'label,value',
+ sysparm_query: 'element=state^name=incident',
+ };
+ const response = await serviceNowRequestAllItems.call(this, 'GET', '/now/table/sys_choice', {}, qs);
+
+ for (const column of response) {
+ returnData.push({
+ name: column.label,
+ value: column.value,
+ });
+
+ }
+ return sortData(returnData);
+ },
+ async getIncidentResolutionCodes(this: ILoadOptionsFunctions): Promise {
+ const returnData: INodePropertyOptions[] = [];
+ const qs = {
+ sysparm_fields: 'label,value',
+ sysparm_query: 'element=close_code^name=incident',
+ };
+ const response = await serviceNowRequestAllItems.call(this, 'GET', '/now/table/sys_choice', {}, qs);
+
+ for (const column of response) {
+ returnData.push({
+ name: column.label,
+ value: column.value,
+ });
+
+ }
+ return sortData(returnData);
+ },
+ async getIncidentHoldReasons(this: ILoadOptionsFunctions): Promise {
+ const returnData: INodePropertyOptions[] = [];
+ const qs = {
+ sysparm_fields: 'label,value',
+ sysparm_query: 'element=hold_reason^name=incident',
+ };
+ const response = await serviceNowRequestAllItems.call(this, 'GET', '/now/table/sys_choice', {}, qs);
+
+ for (const column of response) {
+ returnData.push({
+ name: column.label,
+ value: column.value,
+ });
+
+ }
+ return sortData(returnData);
+ },
+ },
+ };
+
+ async execute(this: IExecuteFunctions): Promise {
+ const items = this.getInputData();
+ const returnData: IDataObject[] = [];
+ const length = items.length;
+ let responseData = {};
+ let qs: IDataObject;
+ const resource = this.getNodeParameter('resource', 0) as string;
+ const operation = this.getNodeParameter('operation', 0) as string;
+
+ for (let i = 0; i < length; i++) {
+ try {
+ if (resource === 'businessService') {
+
+ if (operation === 'getAll') {
+
+ const returnAll = this.getNodeParameter('returnAll', i) as boolean;
+ qs = this.getNodeParameter('options', i) as IDataObject;
+
+ if (qs.sysparm_fields) {
+ qs.sysparm_fields = (qs.sysparm_fields as string[]).join(',');
+ }
+
+ if (!returnAll) {
+ const limit = this.getNodeParameter('limit', i) as number;
+ qs.sysparm_limit = limit;
+ const response = await serviceNowApiRequest.call(this, 'GET', '/now/table/cmdb_ci_service', {}, qs);
+ responseData = response.result;
+ } else {
+ responseData = await serviceNowRequestAllItems.call(this, 'GET', '/now/table/cmdb_ci_service', {}, qs);
+ }
+
+ }
+ } else if (resource === 'configurationItems') {
+
+ if (operation === 'getAll') {
+
+ const returnAll = this.getNodeParameter('returnAll', i) as boolean;
+ qs = this.getNodeParameter('options', i) as IDataObject;
+
+ if (qs.sysparm_fields) {
+ qs.sysparm_fields = (qs.sysparm_fields as string[]).join(',');
+ }
+
+ if (!returnAll) {
+ const limit = this.getNodeParameter('limit', i) as number;
+ qs.sysparm_limit = limit;
+ const response = await serviceNowApiRequest.call(this, 'GET', '/now/table/cmdb_ci', {}, qs);
+ responseData = response.result;
+ } else {
+ responseData = await serviceNowRequestAllItems.call(this, 'GET', '/now/table/cmdb_ci', {}, qs);
+ }
+
+ }
+ } else if (resource === 'department') {
+
+ if (operation === 'getAll') {
+
+ const returnAll = this.getNodeParameter('returnAll', i) as boolean;
+ qs = this.getNodeParameter('options', i) as IDataObject;
+
+ if (qs.sysparm_fields) {
+ qs.sysparm_fields = (qs.sysparm_fields as string[]).join(',');
+ }
+
+ if (!returnAll) {
+ const limit = this.getNodeParameter('limit', i) as number;
+ qs.sysparm_limit = limit;
+ const response = await serviceNowApiRequest.call(this, 'GET', '/now/table/cmn_department', {}, qs);
+ responseData = response.result;
+ } else {
+ responseData = await serviceNowRequestAllItems.call(this, 'GET', '/now/table/cmn_department', {}, qs);
+ }
+
+ }
+ } else if (resource === 'dictionary') {
+
+ if (operation === 'getAll') {
+
+ const returnAll = this.getNodeParameter('returnAll', i) as boolean;
+ qs = this.getNodeParameter('options', i) as IDataObject;
+
+ if (qs.sysparm_fields) {
+ qs.sysparm_fields = (qs.sysparm_fields as string[]).join(',');
+ }
+
+ if (!returnAll) {
+ const limit = this.getNodeParameter('limit', i) as number;
+ qs.sysparm_limit = limit;
+ const response = await serviceNowApiRequest.call(this, 'GET', '/now/table/sys_dictionary', {}, qs);
+ responseData = response.result;
+ } else {
+ responseData = await serviceNowRequestAllItems.call(this, 'GET', '/now/table/sys_dictionary', {}, qs);
+ }
+
+ }
+ } else if (resource === 'incident') {
+
+ if (operation === 'create') {
+
+ const shortDescription = this.getNodeParameter('short_description', i) as string;
+ const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
+ const body = {
+ short_description: shortDescription,
+ ...additionalFields,
+ };
+
+ const response = await serviceNowApiRequest.call(this, 'POST', `/now/table/incident`, body);
+ responseData = response.result;
+
+ } else if (operation === 'delete') {
+
+ const id = this.getNodeParameter('id', i) as string;
+ responseData = await serviceNowApiRequest.call(this, 'DELETE', `/now/table/incident/${id}`);
+ responseData = { success: true };
+
+ } else if (operation === 'get') {
+
+ const id = this.getNodeParameter('id', i) as string;
+ qs = this.getNodeParameter('options', i) as IDataObject;
+
+ if (qs.sysparm_fields) {
+ qs.sysparm_fields = (qs.sysparm_fields as string[]).join(',');
+ }
+
+ const response = await serviceNowApiRequest.call(this, 'GET', `/now/table/incident/${id}`, {}, qs);
+ responseData = response.result;
+
+ } else if (operation === 'getAll') {
+
+ const returnAll = this.getNodeParameter('returnAll', i) as boolean;
+ qs = this.getNodeParameter('options', i) as IDataObject;
+
+ if (qs.sysparm_fields) {
+ qs.sysparm_fields = (qs.sysparm_fields as string[]).join(',');
+ }
+
+ if (!returnAll) {
+ const limit = this.getNodeParameter('limit', i) as number;
+ qs.sysparm_limit = limit;
+ const response = await serviceNowApiRequest.call(this, 'GET', `/now/table/incident`, {}, qs);
+ responseData = response.result;
+ } else {
+ responseData = await serviceNowRequestAllItems.call(this, 'GET', `/now/table/incident`, {}, qs);
+ }
+
+ } else if (operation === 'update') {
+
+ const id = this.getNodeParameter('id', i) as string;
+ const body = this.getNodeParameter('updateFields', i) as IDataObject;
+
+ const response = await serviceNowApiRequest.call(this, 'PATCH', `/now/table/incident/${id}`, body);
+ responseData = response.result;
+
+ } else {
+ throw new NodeOperationError(this.getNode(), `The operation "${operation}" is not known!`);
+ }
+ } else if (resource === 'tableRecord') {
+
+ if (operation === 'create') {
+
+ const tableName = this.getNodeParameter('tableName', i) as string;
+ const dataToSend = this.getNodeParameter('dataToSend', i) as string;
+ let body = {};
+
+ if (dataToSend === 'mapInput') {
+ const inputsToIgnore = (this.getNodeParameter('inputsToIgnore', i) as string).split(',').map(field => field.trim());
+ body = Object.entries(items[i].json)
+ .filter(([key]) => !inputsToIgnore.includes(key))
+ .reduce((obj, [key, val]) => Object.assign(obj, { [key]: val }), {});
+ } else if (dataToSend === 'columns') {
+ const fieldsToSend = this.getNodeParameter('fieldsToSend', i) as {
+ field: IDataObject[]
+ };
+ body = fieldsToSend.field.reduce((obj, field) => {
+ obj[field.column as string] = field.value;
+ return obj;
+ }, {});
+ }
+
+ const response = await serviceNowApiRequest.call(this, 'POST', `/now/table/${tableName}`, body);
+ responseData = response.result;
+
+ } else if (operation === 'delete') {
+
+ const tableName = this.getNodeParameter('tableName', i) as string;
+ const id = this.getNodeParameter('id', i) as string;
+ responseData = await serviceNowApiRequest.call(this, 'DELETE', `/now/table/${tableName}/${id}`);
+ responseData = { success: true };
+
+ } else if (operation === 'get') {
+
+ const tableName = this.getNodeParameter('tableName', i) as string;
+ const id = this.getNodeParameter('id', i) as string;
+ qs = this.getNodeParameter('options', i) as IDataObject;
+
+ if (qs.sysparm_fields) {
+ qs.sysparm_fields = (qs.sysparm_fields as string[]).join(',');
+ }
+
+ const response = await serviceNowApiRequest.call(this, 'GET', `/now/table/${tableName}/${id}`, {}, qs);
+ responseData = response.result;
+
+ } else if (operation === 'getAll') {
+
+ const tableName = this.getNodeParameter('tableName', i) as string;
+ const returnAll = this.getNodeParameter('returnAll', i) as boolean;
+ qs = this.getNodeParameter('options', i) as IDataObject;
+
+ if (qs.sysparm_fields) {
+ qs.sysparm_fields = (qs.sysparm_fields as string[]).join(',');
+ }
+
+ if (!returnAll) {
+ const limit = this.getNodeParameter('limit', i) as number;
+ qs.sysparm_limit = limit;
+ const response = await serviceNowApiRequest.call(this, 'GET', `/now/table/${tableName}`, {}, qs);
+ responseData = response.result;
+ } else {
+ responseData = await serviceNowRequestAllItems.call(this, 'GET', `/now/table/${tableName}`, {}, qs);
+ }
+
+
+ } else if (operation === 'update') {
+
+ const tableName = this.getNodeParameter('tableName', i) as string;
+ const id = this.getNodeParameter('id', i) as string;
+ const dataToSend = this.getNodeParameter('dataToSend', i) as string;
+ let body = {};
+
+ if (dataToSend === 'mapInput') {
+ const inputsToIgnore = (this.getNodeParameter('inputsToIgnore', i) as string).split(',').map(field => field.trim());
+ body = Object.entries(items[i].json)
+ .filter(([key]) => !inputsToIgnore.includes(key))
+ .reduce((obj, [key, val]) => Object.assign(obj, { [key]: val }), {});
+ } else if (dataToSend === 'columns') {
+ const fieldsToSend = this.getNodeParameter('fieldsToSend', i) as {
+ field: IDataObject[]
+ };
+ body = fieldsToSend.field.reduce((obj, field) => {
+ obj[field.column as string] = field.value;
+ return obj;
+ }, {});
+ }
+
+ const response = await serviceNowApiRequest.call(this, 'PATCH', `/now/table/${tableName}/${id}`, body);
+ responseData = response.result;
+
+ } else {
+ throw new NodeOperationError(this.getNode(), `The operation "${operation}" is not known!`);
+ }
+ } else if (resource === 'user') {
+
+ if (operation === 'create') {
+
+ const body = this.getNodeParameter('additionalFields', i) as IDataObject;
+
+ const response = await serviceNowApiRequest.call(this, 'POST', '/now/table/sys_user', body);
+ responseData = response.result;
+
+ } else if (operation === 'delete') {
+
+ const id = this.getNodeParameter('id', i) as string;
+ responseData = await serviceNowApiRequest.call(this, 'DELETE', `/now/table/sys_user/${id}`);
+ responseData = { success: true };
+
+ } else if (operation === 'get') {
+
+ const getOption = this.getNodeParameter('getOption', i) as string;
+ qs = this.getNodeParameter('options', i) as IDataObject;
+
+ if (qs.sysparm_fields) {
+ qs.sysparm_fields = (qs.sysparm_fields as string[]).join(',');
+ }
+
+ if (getOption === 'id') {
+ const id = this.getNodeParameter('id', i) as string;
+ const response = await serviceNowApiRequest.call(this, 'GET', `/now/table/sys_user/${id}`, {}, qs);
+ responseData = response.result;
+ } else {
+ const userName = this.getNodeParameter('user_name', i) as string;
+ qs.sysparm_query = `user_name=${userName}`;
+ qs.sysparm_limit = 1;
+ const response = await serviceNowApiRequest.call(this, 'GET', '/now/table/sys_user', {}, qs);
+ responseData = response.result;
+ }
+
+ } else if (operation === 'getAll') {
+
+ const returnAll = this.getNodeParameter('returnAll', i) as boolean;
+ qs = this.getNodeParameter('options', i) as IDataObject;
+
+ if (qs.sysparm_fields) {
+ qs.sysparm_fields = (qs.sysparm_fields as string[]).join(',');
+ }
+
+ if (!returnAll) {
+ const limit = this.getNodeParameter('limit', i) as number;
+ qs.sysparm_limit = limit;
+ const response = await serviceNowApiRequest.call(this, 'GET', '/now/table/sys_user', {}, qs);
+ responseData = response.result;
+ } else {
+ responseData = await serviceNowRequestAllItems.call(this, 'GET', '/now/table/sys_user', {}, qs);
+ }
+
+ } else if (operation === 'update') {
+
+ const id = this.getNodeParameter('id', i) as string;
+ const body = this.getNodeParameter('updateFields', i) as IDataObject;
+
+ const response = await serviceNowApiRequest.call(this, 'PATCH', `/now/table/sys_user/${id}`, body);
+ responseData = response.result;
+
+ } else {
+ throw new NodeOperationError(this.getNode(), `The operation "${operation}" is not known!`);
+ }
+ } else if (resource === 'userGroup') {
+ if (operation === 'getAll') {
+
+ const returnAll = this.getNodeParameter('returnAll', i) as boolean;
+ qs = this.getNodeParameter('options', i) as IDataObject;
+
+ if (qs.sysparm_fields) {
+ qs.sysparm_fields = (qs.sysparm_fields as string[]).join(',');
+ }
+
+ if (!returnAll) {
+ const limit = this.getNodeParameter('limit', i) as number;
+ qs.sysparm_limit = limit;
+ const response = await serviceNowApiRequest.call(this, 'GET', '/now/table/sys_user_group', {}, qs);
+ responseData = response.result;
+ } else {
+ responseData = await serviceNowRequestAllItems.call(this, 'GET', '/now/table/sys_user_group', {}, qs);
+ }
+ } else {
+ throw new NodeOperationError(this.getNode(), `The operation "${operation}" is not known!`);
+ }
+ } else if (resource === 'userRole') {
+ if (operation === 'getAll') {
+
+ const returnAll = this.getNodeParameter('returnAll', i) as boolean;
+ qs = this.getNodeParameter('options', i) as IDataObject;
+
+ if (qs.sysparm_fields) {
+ qs.sysparm_fields = (qs.sysparm_fields as string[]).join(',');
+ }
+
+ if (!returnAll) {
+ const limit = this.getNodeParameter('limit', i) as number;
+ qs.sysparm_limit = limit;
+ const response = await serviceNowApiRequest.call(this, 'GET', '/now/table/sys_user_role', {}, qs);
+ responseData = response.result;
+ } else {
+ responseData = await serviceNowRequestAllItems.call(this, 'GET', '/now/table/sys_user_role', {}, qs);
+ }
+ } else {
+ throw new NodeOperationError(this.getNode(), `The operation "${operation}" is not known!`);
+ }
+ } else {
+ throw new NodeOperationError(this.getNode(), `The resource "${resource}" is not known!`);
+ }
+ } catch (error) {
+ if (this.continueOnFail()) {
+ returnData.push({ error: error.message });
+ continue;
+ }
+
+ throw error;
+ }
+
+ Array.isArray(responseData)
+ ? returnData.push(...responseData)
+ : returnData.push(responseData);
+ }
+ return [this.helpers.returnJsonArray(returnData)];
+ }
+}
diff --git a/packages/nodes-base/nodes/ServiceNow/TableRecordDescription.ts b/packages/nodes-base/nodes/ServiceNow/TableRecordDescription.ts
new file mode 100644
index 0000000000..51c9845a44
--- /dev/null
+++ b/packages/nodes-base/nodes/ServiceNow/TableRecordDescription.ts
@@ -0,0 +1,555 @@
+import {
+ INodeProperties,
+} from 'n8n-workflow';
+
+export const tableRecordOperations = [
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ displayOptions: {
+ show: {
+ resource: [
+ 'tableRecord',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Create',
+ value: 'create',
+ },
+ {
+ name: 'Delete',
+ value: 'delete',
+ },
+ {
+ name: 'Get',
+ value: 'get',
+ },
+ {
+ name: 'Get All',
+ value: 'getAll',
+ },
+ {
+ name: 'Update',
+ value: 'update',
+ },
+ ],
+ default: 'get',
+ },
+] as INodeProperties[];
+
+export const tableRecordFields = [
+ /* -------------------------------------------------------------------------- */
+ /* tableRecord:create */
+ /* -------------------------------------------------------------------------- */
+ {
+ displayName: 'Table Name',
+ name: 'tableName',
+ type: 'options',
+ typeOptions: {
+ loadOptionsMethod: 'getTables',
+ },
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'tableRecord',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ required: true,
+ description: 'The table name',
+ },
+ {
+ displayName: 'Data to Send',
+ name: 'dataToSend',
+ type: 'options',
+ options: [
+ {
+ name: 'Auto-map Input Data to Columns',
+ value: 'mapInput',
+ description: 'Use when node input names match destination field names',
+ },
+ {
+ name: 'Define Below for Each Column',
+ value: 'columns',
+ description: 'Set the value for each destination column',
+ },
+ {
+ name: 'Nothing',
+ value: 'nothing',
+ description: `Don't send any column data`,
+ },
+ ],
+ displayOptions: {
+ show: {
+ resource: [
+ 'tableRecord',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ default: 'columns',
+ },
+ {
+ displayName: 'Inputs to Ignore',
+ name: 'inputsToIgnore',
+ type: 'string',
+ displayOptions: {
+ show: {
+ resource: [
+ 'tableRecord',
+ ],
+ operation: [
+ 'create',
+ ],
+ dataToSend: [
+ 'mapInput',
+ ],
+ },
+ },
+ default: '',
+ required: false,
+ description: 'List of input properties to avoid sending, separated by commas. Leave empty to send all inputs',
+ },
+ {
+ displayName: 'Fields to Send',
+ name: 'fieldsToSend',
+ type: 'fixedCollection',
+ placeholder: 'Add field to send',
+ typeOptions: {
+ multipleValues: true,
+ },
+ displayOptions: {
+ show: {
+ resource: [
+ 'tableRecord',
+ ],
+ operation: [
+ 'create',
+ ],
+ dataToSend: [
+ 'columns',
+ ],
+ },
+ },
+ default: {},
+ options: [
+ {
+ displayName: 'Field',
+ name: 'field',
+ values: [
+ {
+ displayName: 'Field Name',
+ name: 'column',
+ type: 'options',
+ typeOptions: {
+ loadOptionsMethod: 'getColumns',
+ loadOptionsDependsOn: [
+ 'tableName',
+ ],
+ },
+ default: '',
+ },
+ {
+ displayName: 'Field Value',
+ name: 'value',
+ type: 'string',
+ default: '',
+ },
+ ],
+ },
+ ],
+ },
+
+ /* -------------------------------------------------------------------------- */
+ /* tableRecord:getAll */
+ /* -------------------------------------------------------------------------- */
+ {
+ displayName: 'Table Name',
+ name: 'tableName',
+ type: 'options',
+ typeOptions: {
+ loadOptionsMethod: 'getTables',
+ },
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'tableRecord',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ },
+ },
+ required: true,
+ description: 'The table name',
+ },
+ {
+ displayName: 'Return All',
+ name: 'returnAll',
+ type: 'boolean',
+ displayOptions: {
+ show: {
+ operation: [
+ 'getAll',
+ ],
+ resource: [
+ 'tableRecord',
+ ],
+ },
+ },
+ default: false,
+ description: 'If all results should be returned or only up to a given limit',
+ },
+ {
+ displayName: 'Limit',
+ name: 'limit',
+ type: 'number',
+ displayOptions: {
+ show: {
+ operation: [
+ 'getAll',
+ ],
+ resource: [
+ 'tableRecord',
+ ],
+ returnAll: [
+ false,
+ ],
+ },
+ },
+ typeOptions: {
+ minValue: 1,
+ maxValue: 500,
+ },
+ default: 50,
+ description: 'The max number of results to return',
+ },
+ {
+ displayName: 'Options',
+ name: 'options',
+ type: 'collection',
+ placeholder: 'Add Field',
+ displayOptions: {
+ show: {
+ resource: [
+ 'tableRecord',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ },
+ },
+ default: {},
+ options: [
+ {
+ displayName: 'Exclude Reference Link',
+ name: 'sysparm_exclude_reference_link',
+ type: 'boolean',
+ default: false,
+ description: 'Whether to exclude Table API links for reference fields',
+ },
+ {
+ displayName: 'Fields',
+ name: 'sysparm_fields',
+ type: 'multiOptions',
+ typeOptions: {
+ loadOptionsMethod: 'getColumns',
+ loadOptionsDependsOn: [
+ 'tableName',
+ ],
+ },
+ default: '',
+ description: 'A list of fields to return',
+ },
+ {
+ displayName: 'Filter',
+ name: 'sysparm_query',
+ type: 'string',
+ default: '',
+ description: 'An encoded query string used to filter the results. More info',
+ },
+ {
+ displayName: 'Return Values',
+ name: 'sysparm_display_value',
+ type: 'options',
+ options: [
+ {
+ name: 'Actual Values',
+ value: 'false',
+ },
+ {
+ name: 'Both',
+ value: 'all',
+ },
+ {
+ name: 'Display Values',
+ value: 'true',
+ },
+ ],
+ default: 'false',
+ description: 'Choose which values to return',
+ },
+ ],
+ },
+
+ /* -------------------------------------------------------------------------- */
+ /* tableRecord:get/delete */
+ /* -------------------------------------------------------------------------- */
+ {
+ displayName: 'Table Name',
+ name: 'tableName',
+ type: 'options',
+ typeOptions: {
+ loadOptionsMethod: 'getTables',
+ },
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'tableRecord',
+ ],
+ operation: [
+ 'delete',
+ 'get',
+ ],
+ },
+ },
+ required: true,
+ description: 'Name of the table in which the record exists',
+ },
+ {
+ displayName: 'Table Record ID',
+ name: 'id',
+ type: 'string',
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'tableRecord',
+ ],
+ operation: [
+ 'delete',
+ 'get',
+ ],
+ },
+ },
+ required: true,
+ description: 'Unique identifier of the record',
+ },
+ {
+ displayName: 'Options',
+ name: 'options',
+ type: 'collection',
+ placeholder: 'Add Field',
+ displayOptions: {
+ show: {
+ resource: [
+ 'tableRecord',
+ ],
+ operation: [
+ 'get',
+ ],
+ },
+ },
+ default: {},
+ options: [
+ {
+ displayName: 'Exclude Reference Link',
+ name: 'sysparm_exclude_reference_link',
+ type: 'boolean',
+ default: false,
+ description: 'Whether to exclude Table API links for reference fields',
+ },
+ {
+ displayName: 'Fields',
+ name: 'sysparm_fields',
+ type: 'multiOptions',
+ typeOptions: {
+ loadOptionsMethod: 'getColumns',
+ loadOptionsDependsOn: [
+ 'tableName',
+ ],
+ },
+ default: '',
+ description: 'A list of fields to return',
+ },
+ {
+ displayName: 'Return Values',
+ name: 'sysparm_display_value',
+ type: 'options',
+ options: [
+ {
+ name: 'Actual Values',
+ value: 'false',
+ },
+ {
+ name: 'Both',
+ value: 'all',
+ },
+ {
+ name: 'Display Values',
+ value: 'true',
+ },
+ ],
+ default: 'false',
+ description: 'Choose which values to return',
+ },
+ ],
+ },
+
+ /* -------------------------------------------------------------------------- */
+ /* tableRecord:update */
+ /* -------------------------------------------------------------------------- */
+ {
+ displayName: 'Table Name',
+ name: 'tableName',
+ type: 'options',
+ typeOptions: {
+ loadOptionsMethod: 'getTables',
+ },
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'tableRecord',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ required: true,
+ description: 'The table name',
+ },
+ {
+ displayName: 'Table Record ID',
+ name: 'id',
+ type: 'string',
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'tableRecord',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ required: true,
+ description: 'Unique identifier of the record',
+ },
+ {
+ displayName: 'Data to Send',
+ name: 'dataToSend',
+ type: 'options',
+ options: [
+ {
+ name: 'Auto-map Input Data to Columns',
+ value: 'mapInput',
+ description: 'Use when node input names match destination field names',
+ },
+ {
+ name: 'Define Below for Each Column',
+ value: 'columns',
+ description: 'Set the value for each destination column',
+ },
+ {
+ name: 'Nothing',
+ value: 'nothing',
+ description: `Don't send any column data`,
+ },
+ ],
+ displayOptions: {
+ show: {
+ resource: [
+ 'tableRecord',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ default: 'columns',
+ },
+ {
+ displayName: 'Inputs to Ignore',
+ name: 'inputsToIgnore',
+ type: 'string',
+ displayOptions: {
+ show: {
+ resource: [
+ 'tableRecord',
+ ],
+ operation: [
+ 'update',
+ ],
+ dataToSend: [
+ 'mapInput',
+ ],
+ },
+ },
+ default: '',
+ required: false,
+ description: 'List of input properties to avoid sending, separated by commas. Leave empty to send all inputs',
+ },
+ {
+ displayName: 'Fields to Send',
+ name: 'fieldsToSend',
+ type: 'fixedCollection',
+ placeholder: 'Add field to send',
+ typeOptions: {
+ multipleValues: true,
+ },
+ displayOptions: {
+ show: {
+ resource: [
+ 'tableRecord',
+ ],
+ operation: [
+ 'update',
+ ],
+ dataToSend: [
+ 'columns',
+ ],
+ },
+ },
+ default: {},
+ options: [
+ {
+ displayName: 'Field',
+ name: 'field',
+ values: [
+ {
+ displayName: 'Field Name',
+ name: 'column',
+ type: 'options',
+ typeOptions: {
+ loadOptionsMethod: 'getColumns',
+ loadOptionsDependsOn: [
+ 'tableName',
+ ],
+ },
+ default: '',
+ },
+ {
+ displayName: 'Field Value',
+ name: 'value',
+ type: 'string',
+ default: '',
+ },
+ ],
+ },
+ ],
+ },
+] as INodeProperties[];
diff --git a/packages/nodes-base/nodes/ServiceNow/UserDescription.ts b/packages/nodes-base/nodes/ServiceNow/UserDescription.ts
new file mode 100644
index 0000000000..645d7efa75
--- /dev/null
+++ b/packages/nodes-base/nodes/ServiceNow/UserDescription.ts
@@ -0,0 +1,732 @@
+import {
+ INodeProperties,
+} from 'n8n-workflow';
+
+export const userOperations = [
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ displayOptions: {
+ show: {
+ resource: [
+ 'user',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Create',
+ value: 'create',
+ },
+ {
+ name: 'Delete',
+ value: 'delete',
+ },
+ {
+ name: 'Get',
+ value: 'get',
+ },
+ {
+ name: 'Get All',
+ value: 'getAll',
+ },
+ {
+ name: 'Update',
+ value: 'update',
+ },
+ ],
+ default: 'get',
+ },
+] as INodeProperties[];
+
+export const userFields = [
+ /* -------------------------------------------------------------------------- */
+ /* user:create */
+ /* -------------------------------------------------------------------------- */
+
+ {
+ displayName: 'Short Description',
+ name: 'short_description',
+ type: 'string',
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'user',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ required: true,
+ description: 'Short description of the user',
+ },
+ {
+ displayName: 'Additional Fields',
+ name: 'additionalFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ displayOptions: {
+ show: {
+ resource: [
+ 'user',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ default: {},
+ options: [
+ {
+ displayName: 'Active',
+ name: 'active',
+ type: 'boolean',
+ default: '',
+ description: 'Whether to activate the user',
+ },
+ {
+ displayName: 'Building',
+ name: 'building',
+ type: 'string',
+ default: '',
+ description: 'The Building address',
+ },
+ {
+ displayName: 'City',
+ name: 'city',
+ type: 'string',
+ default: '',
+ description: 'City of the user',
+ },
+ {
+ displayName: 'Company',
+ name: 'company',
+ type: 'string',
+ default: '',
+ description: 'The name of the company for the user',
+ },
+ {
+ displayName: 'Country',
+ name: 'country',
+ type: 'string',
+ default: '',
+ description: 'Country of the user',
+ },
+ {
+ displayName: 'Department',
+ name: 'department',
+ type: 'string',
+ default: '',
+ description: 'Department of the user',
+ },
+ {
+ displayName: 'Email',
+ name: 'email',
+ type: 'string',
+ default: '',
+ description: 'The email address associated with the user',
+ },
+ {
+ displayName: 'First Name',
+ name: 'first_name',
+ type: 'string',
+ default: '',
+ description: 'The first name of the user',
+ },
+ {
+ displayName: 'Gender',
+ name: 'gender',
+ type: 'string',
+ default: '',
+ description: 'The gender of the user',
+ },
+ {
+ displayName: 'Home Phone',
+ name: 'home_phone',
+ type: 'string',
+ default: '',
+ description: 'Home phone of the user',
+ },
+ {
+ displayName: 'Last Name',
+ name: 'last_name',
+ type: 'string',
+ default: '',
+ description: 'The last name of the user',
+ },
+ {
+ displayName: 'Location',
+ name: 'location',
+ type: 'string',
+ default: '',
+ description: 'Location of the user',
+ },
+ {
+ displayName: 'Manager',
+ name: 'manager',
+ type: 'string',
+ default: '',
+ description: 'Manager of the user',
+ },
+ {
+ displayName: 'Middle Name',
+ name: 'middle_name',
+ type: 'string',
+ default: '',
+ description: 'The middle name of the user',
+ },
+ {
+ displayName: 'Mobile Phone',
+ name: 'mobile_phone',
+ type: 'string',
+ default: '',
+ description: 'Mobile phone number of the user',
+ },
+ {
+ displayName: 'Password',
+ name: 'user_password',
+ type: 'string',
+ default: '',
+ description: 'The user\'s password',
+ },
+ {
+ displayName: 'Password Needs Reset',
+ name: 'password_needs_reset',
+ type: 'boolean',
+ default: '',
+ description: 'Whether to require a password reset when the user logs in',
+ },
+ {
+ displayName: 'Phone',
+ name: 'phone',
+ type: 'string',
+ default: '',
+ description: 'The main phone number of the user',
+ },
+ {
+ displayName: 'Roles',
+ name: 'roles',
+ type: 'multiOptions',
+ typeOptions: {
+ loadOptionsMethod: 'getUserRoles',
+ },
+ default: '',
+ description: 'Roles of the user',
+ },
+ {
+ displayName: 'Source',
+ name: 'source',
+ type: 'string',
+ default: '',
+ description: 'The source',
+ },
+ {
+ displayName: 'State',
+ name: 'state',
+ type: 'string',
+ default: '',
+ description: 'State for the user',
+ },
+ {
+ displayName: 'Street',
+ name: 'street',
+ type: 'string',
+ default: '',
+ description: 'Street information for the user separated by comma',
+ },
+ {
+ displayName: 'Username',
+ name: 'user_name',
+ type: 'string',
+ default: '',
+ description: 'A username associated with the user (e.g. user_name.123)',
+ },
+ {
+ displayName: 'Zip Code',
+ name: 'zip',
+ type: 'string',
+ default: '',
+ description: 'Zip code for the user',
+ },
+ ],
+ },
+
+ /* -------------------------------------------------------------------------- */
+ /* user:getAll */
+ /* -------------------------------------------------------------------------- */
+ {
+ displayName: 'Return All',
+ name: 'returnAll',
+ type: 'boolean',
+ displayOptions: {
+ show: {
+ operation: [
+ 'getAll',
+ ],
+ resource: [
+ 'user',
+ ],
+ },
+ },
+ default: false,
+ description: 'If all results should be returned or only up to a given limit',
+ },
+ {
+ displayName: 'Limit',
+ name: 'limit',
+ type: 'number',
+ displayOptions: {
+ show: {
+ operation: [
+ 'getAll',
+ ],
+ resource: [
+ 'user',
+ ],
+ returnAll: [
+ false,
+ ],
+ },
+ },
+ typeOptions: {
+ minValue: 1,
+ maxValue: 500,
+ },
+ default: 50,
+ description: 'The max number of results to return',
+ },
+ {
+ displayName: 'Options',
+ name: 'options',
+ type: 'collection',
+ placeholder: 'Add Field',
+ displayOptions: {
+ show: {
+ resource: [
+ 'user',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ },
+ },
+ default: {},
+ options: [
+ {
+ displayName: 'Exclude Reference Link',
+ name: 'sysparm_exclude_reference_link',
+ type: 'boolean',
+ default: false,
+ description: 'Whether to exclude Table API links for reference fields',
+ },
+ {
+ displayName: 'Fields',
+ name: 'sysparm_fields',
+ type: 'multiOptions',
+ typeOptions: {
+ loadOptionsMethod: 'getColumns',
+ loadOptionsDependsOn: [
+ 'operation',
+ ],
+ },
+ default: '',
+ description: 'A list of fields to return',
+ },
+ {
+ displayName: 'Filter',
+ name: 'sysparm_query',
+ type: 'string',
+ default: '',
+ description: 'An encoded query string used to filter the results. More info',
+ },
+ {
+ displayName: 'Return Values',
+ name: 'sysparm_display_value',
+ type: 'options',
+ options: [
+ {
+ name: 'Actual Values',
+ value: 'false',
+ },
+ {
+ name: 'Both',
+ value: 'all',
+ },
+ {
+ name: 'Display Values',
+ value: 'true',
+ },
+ ],
+ default: 'false',
+ description: 'Choose which values to return',
+ },
+ ],
+ },
+
+ /* -------------------------------------------------------------------------- */
+ /* user:get/delete */
+ /* -------------------------------------------------------------------------- */
+ {
+ displayName: 'Retrieve Identifier',
+ name: 'getOption',
+ type: 'options',
+ default: 'id',
+ options: [
+ {
+ name: 'ID',
+ value: 'id',
+ },
+ {
+ name: 'Username',
+ value: 'user_name',
+ },
+ ],
+ displayOptions: {
+ show: {
+ resource: [
+ 'user',
+ ],
+ operation: [
+ 'get',
+ ],
+ },
+ },
+ required: true,
+ description: 'Unique identifier of the user',
+ },
+ {
+ displayName: 'Username',
+ name: 'user_name',
+ type: 'string',
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'user',
+ ],
+ operation: [
+ 'get',
+ ],
+ getOption: [
+ 'user_name',
+ ],
+ },
+ },
+ required: true,
+ description: 'Unique identifier of the user',
+ },
+ {
+ displayName: 'User ID',
+ name: 'id',
+ type: 'string',
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'user',
+ ],
+ operation: [
+ 'get',
+ ],
+ getOption: [
+ 'id',
+ ],
+ },
+ },
+ required: true,
+ description: 'Unique identifier of the user',
+ },
+ {
+ displayName: 'User ID',
+ name: 'id',
+ type: 'string',
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'user',
+ ],
+ operation: [
+ 'delete',
+ ],
+ },
+ },
+ required: true,
+ description: 'Unique identifier of the user',
+ },
+ {
+ displayName: 'Options',
+ name: 'options',
+ type: 'collection',
+ placeholder: 'Add Field',
+ displayOptions: {
+ show: {
+ resource: [
+ 'user',
+ ],
+ operation: [
+ 'get',
+ ],
+ },
+ },
+ default: {},
+ options: [
+ {
+ displayName: 'Exclude Reference Link',
+ name: 'sysparm_exclude_reference_link',
+ type: 'boolean',
+ default: false,
+ description: 'Whether to exclude Table API links for reference fields',
+ },
+ {
+ displayName: 'Fields',
+ name: 'sysparm_fields',
+ type: 'multiOptions',
+ typeOptions: {
+ loadOptionsMethod: 'getColumns',
+ loadOptionsDependsOn: [
+ 'operation',
+ ],
+ },
+ default: '',
+ description: 'A list of fields to return',
+ },
+ {
+ displayName: 'Return Values',
+ name: 'sysparm_display_value',
+ type: 'options',
+ options: [
+ {
+ name: 'Actual Values',
+ value: 'false',
+ },
+ {
+ name: 'Both',
+ value: 'all',
+ },
+ {
+ name: 'Display Values',
+ value: 'true',
+ },
+ ],
+ default: 'false',
+ description: 'Choose which values to return',
+ },
+ ],
+ },
+
+ /* -------------------------------------------------------------------------- */
+ /* user:update */
+ /* -------------------------------------------------------------------------- */
+ {
+ displayName: 'User ID',
+ name: 'id',
+ type: 'string',
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'user',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ required: true,
+ description: 'Unique identifier of the user',
+ },
+ {
+ displayName: 'Update Fields',
+ name: 'updateFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ displayOptions: {
+ show: {
+ resource: [
+ 'user',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ default: {},
+ options: [
+ {
+ displayName: 'Active',
+ name: 'active',
+ type: 'boolean',
+ default: '',
+ description: 'Whether to activate the user',
+ },
+ {
+ displayName: 'Building',
+ name: 'building',
+ type: 'string',
+ default: '',
+ description: 'The Building address',
+ },
+ {
+ displayName: 'City',
+ name: 'city',
+ type: 'string',
+ default: '',
+ description: 'City of the user',
+ },
+ {
+ displayName: 'Company',
+ name: 'company',
+ type: 'string',
+ default: '',
+ description: 'The name of the company for the user',
+ },
+ {
+ displayName: 'Country',
+ name: 'country',
+ type: 'string',
+ default: '',
+ description: 'Country of the user',
+ },
+ {
+ displayName: 'Department',
+ name: 'department',
+ type: 'string',
+ default: '',
+ description: 'Department of the user',
+ },
+ {
+ displayName: 'Email',
+ name: 'email',
+ type: 'string',
+ default: '',
+ description: 'The email address associated with the user',
+ },
+ {
+ displayName: 'First Name',
+ name: 'first_name',
+ type: 'string',
+ default: '',
+ description: 'The first name of the user',
+ },
+ {
+ displayName: 'Gender',
+ name: 'gender',
+ type: 'string',
+ default: '',
+ description: 'The gender of the user',
+ },
+ {
+ displayName: 'Home Phone',
+ name: 'home_phone',
+ type: 'string',
+ default: '',
+ description: 'Home phone of the user',
+ },
+ {
+ displayName: 'Last Name',
+ name: 'last_name',
+ type: 'string',
+ default: '',
+ description: 'The last name of the user',
+ },
+ {
+ displayName: 'Location',
+ name: 'location',
+ type: 'string',
+ default: '',
+ description: 'Location of the user',
+ },
+ {
+ displayName: 'Manager',
+ name: 'manager',
+ type: 'string',
+ default: '',
+ description: 'Manager of the user',
+ },
+ {
+ displayName: 'Middle Name',
+ name: 'middle_name',
+ type: 'string',
+ default: '',
+ description: 'The middle name of the user',
+ },
+ {
+ displayName: 'Mobile Phone',
+ name: 'mobile_phone',
+ type: 'string',
+ default: '',
+ description: 'Mobile phone number of the user',
+ },
+ {
+ displayName: 'Password',
+ name: 'user_password',
+ type: 'string',
+ default: '',
+ description: 'The user\'s password',
+ },
+ {
+ displayName: 'Password Needs Reset',
+ name: 'password_needs_reset',
+ type: 'boolean',
+ default: '',
+ description: 'Whether to require a password reset when the user logs in',
+ },
+ {
+ displayName: 'Phone',
+ name: 'phone',
+ type: 'string',
+ default: '',
+ description: 'The main phone number of the user',
+ },
+ {
+ displayName: 'Roles',
+ name: 'roles',
+ type: 'multiOptions',
+ typeOptions: {
+ loadOptionsMethod: 'getUserRoles',
+ },
+ default: '',
+ description: 'Roles of the user',
+ },
+ {
+ displayName: 'Source',
+ name: 'source',
+ type: 'string',
+ default: '',
+ description: 'The source',
+ },
+ {
+ displayName: 'State',
+ name: 'state',
+ type: 'string',
+ default: '',
+ description: 'State for the user',
+ },
+ {
+ displayName: 'Street',
+ name: 'street',
+ type: 'string',
+ default: '',
+ description: 'Street information for the user separated by comma',
+ },
+ {
+ displayName: 'Username',
+ name: 'user_name',
+ type: 'string',
+ default: '',
+ description: 'A username associated with the user (e.g. user_name.123)',
+ },
+ {
+ displayName: 'Zip Code',
+ name: 'zip',
+ type: 'string',
+ default: '',
+ description: 'Zip code for the user',
+ },
+ ],
+ },
+] as INodeProperties[];
diff --git a/packages/nodes-base/nodes/ServiceNow/servicenow.svg b/packages/nodes-base/nodes/ServiceNow/servicenow.svg
new file mode 100644
index 0000000000..e5567e562c
--- /dev/null
+++ b/packages/nodes-base/nodes/ServiceNow/servicenow.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json
index 1c6125e26e..7bbd5fc8f0 100644
--- a/packages/nodes-base/package.json
+++ b/packages/nodes-base/package.json
@@ -220,6 +220,7 @@
"dist/credentials/SentryIoApi.credentials.js",
"dist/credentials/SentryIoServerApi.credentials.js",
"dist/credentials/SentryIoOAuth2Api.credentials.js",
+ "dist/credentials/ServiceNowOAuth2Api.credentials.js",
"dist/credentials/ShopifyApi.credentials.js",
"dist/credentials/Signl4Api.credentials.js",
"dist/credentials/SlackApi.credentials.js",
@@ -515,6 +516,7 @@
"dist/nodes/Set.node.js",
"dist/nodes/SentryIo/SentryIo.node.js",
"dist/nodes/SendGrid/SendGrid.node.js",
+ "dist/nodes/ServiceNow/ServiceNow.node.js",
"dist/nodes/Shopify/Shopify.node.js",
"dist/nodes/Shopify/ShopifyTrigger.node.js",
"dist/nodes/Signl4/Signl4.node.js",