Add ServiceNow node (#1932)

*  ServiceNow node

* Add Table Record resource & operations

* Add Incident resource & operations

* Add User resource & operations

* Add Business Service getAll operation

* Add Business Service, Department, Dictionary and Configuration Items resources

* Improvements

* Add continueOnFail

* Fix node display name

* Fix node credentials name

* Minor improvements

* Improvements

* Add load function for fields parameter

* Add load function for incident, user and table record fields

* Fix sending input function

* Enhance ServiceNow credentials

* Apply review changes & improvements

* Minor improvements

* Minor code enhancement

* Apply review changes

*  Small improvement

* Fix TableRecord update operation & add BusinessServices loading

*  Small improvement

*  Improve ServiceNow node

*  Fix one more description

Co-authored-by: ricardo <ricardoespinoza105@gmail.com>
Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com>
This commit is contained in:
MedAliMarz 2021-07-15 19:30:59 +02:00 committed by GitHub
parent a376ff85b5
commit de2119564c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 3459 additions and 0 deletions

View file

@ -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',
},
];
}

View file

@ -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. <a href="https://developer.servicenow.com/dev.do#!/learn/learning-plans/quebec/servicenow_application_developer/app_store_learnv2_rest_quebec_more_about_query_parameters" target="_blank">More info</a>',
},
{
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[];

View file

@ -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. <a href="https://developer.servicenow.com/dev.do#!/learn/learning-plans/quebec/servicenow_application_developer/app_store_learnv2_rest_quebec_more_about_query_parameters" target="_blank">More info</a>',
},
{
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[];

View file

@ -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. <a href="https://developer.servicenow.com/dev.do#!/learn/learning-plans/quebec/servicenow_application_developer/app_store_learnv2_rest_quebec_more_about_query_parameters" target="_blank">More info</a>',
},
{
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[];

View file

@ -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. <a href="https://developer.servicenow.com/dev.do#!/learn/learning-plans/quebec/servicenow_application_developer/app_store_learnv2_rest_quebec_more_about_query_parameters" target="_blank">More info</a>',
},
{
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[];

View file

@ -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<any> { // 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<any> { // 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;
};

View file

@ -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. <a href="https://developer.servicenow.com/dev.do#!/learn/learning-plans/quebec/servicenow_application_developer/app_store_learnv2_rest_quebec_more_about_query_parameters" target="_blank">More info</a>',
},
{
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 <code>On Hold</code>',
},
{
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[];

View file

@ -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<INodePropertyOptions[]> {
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<INodePropertyOptions[]> {
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<INodePropertyOptions[]> {
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<INodePropertyOptions[]> {
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<INodePropertyOptions[]> {
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<INodePropertyOptions[]> {
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<INodePropertyOptions[]> {
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<INodePropertyOptions[]> {
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<INodePropertyOptions[]> {
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<INodePropertyOptions[]> {
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<INodePropertyOptions[]> {
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<INodePropertyOptions[]> {
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<INodeExecutionData[][]> {
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)];
}
}

View file

@ -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. <a href="https://developer.servicenow.com/dev.do#!/learn/learning-plans/quebec/servicenow_application_developer/app_store_learnv2_rest_quebec_more_about_query_parameters" target="_blank">More info</a>',
},
{
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[];

View file

@ -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. <a href="https://developer.servicenow.com/dev.do#!/learn/learning-plans/quebec/servicenow_application_developer/app_store_learnv2_rest_quebec_more_about_query_parameters" target="_blank">More info</a>',
},
{
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[];

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 66 59" fill="#fff" fill-rule="evenodd" stroke="#000" stroke-linecap="round" stroke-linejoin="round"><use xlink:href="#A" x=".5" y=".5"/><symbol id="A" overflow="visible"><path d="M32.196 0A32.27 32.27 0 0 0 2.312 20.275 32.27 32.27 0 0 0 9.95 55.571c2.27 2.167 5.782 2.349 8.264.43 8.15-6.045 19.295-6.045 27.445 0 2.507 1.985 6.098 1.8 8.389-.43A32.27 32.27 0 0 0 32.196 0m-.18 48.275a15.63 15.63 0 0 1-16.133-16.026c-.196-5.857 2.816-11.355 7.858-14.341s11.311-2.987 16.353 0 8.055 8.485 7.858 14.341c.115 4.284-1.537 8.428-4.568 11.458s-7.174 4.682-11.458 4.568" stroke="none" fill="#81b5a1"/></symbol></svg>

After

Width:  |  Height:  |  Size: 707 B

View file

@ -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",