diff --git a/packages/nodes-base/credentials/FreshserviceApi.credentials.ts b/packages/nodes-base/credentials/FreshserviceApi.credentials.ts
new file mode 100644
index 0000000000..cc5f716097
--- /dev/null
+++ b/packages/nodes-base/credentials/FreshserviceApi.credentials.ts
@@ -0,0 +1,27 @@
+import {
+ ICredentialType,
+ INodeProperties,
+} from 'n8n-workflow';
+
+export class FreshserviceApi implements ICredentialType {
+ name = 'freshserviceApi';
+ displayName = 'Freshservice API';
+ documentationUrl = 'freshservice';
+ properties: INodeProperties[] = [
+ {
+ displayName: 'API Key',
+ name: 'apiKey',
+ type: 'string',
+ default: '',
+ placeholder: 'atuH3AbeH9HsKvgHuxg',
+ },
+ {
+ displayName: 'Domain',
+ name: 'domain',
+ type: 'string',
+ default: '',
+ placeholder: 'n8n',
+ description: 'Domain in the Freshservice org URL. For example, in https://n8n.freshservice.com
, the domain is n8n
',
+ },
+ ];
+}
diff --git a/packages/nodes-base/nodes/Freshservice/Freshservice.node.ts b/packages/nodes-base/nodes/Freshservice/Freshservice.node.ts
new file mode 100644
index 0000000000..1b25eae228
--- /dev/null
+++ b/packages/nodes-base/nodes/Freshservice/Freshservice.node.ts
@@ -0,0 +1,1525 @@
+import {
+ IExecuteFunctions,
+} from 'n8n-core';
+
+import {
+ IDataObject,
+ ILoadOptionsFunctions,
+ INodeExecutionData,
+ INodePropertyOptions,
+ INodeType,
+ INodeTypeDescription,
+} from 'n8n-workflow';
+
+import {
+ adjustAddress,
+ adjustAgentRoles,
+ formatFilters,
+ freshserviceApiRequest,
+ handleListing,
+ sanitizeAssignmentScopeGroup,
+ toArray,
+ toOptions,
+ toUserOptions,
+ validateAssignmentScopeGroup,
+ validateUpdateFields,
+} from './GenericFunctions';
+
+import {
+ agentFields,
+ agentGroupFields,
+ agentGroupOperations,
+ agentOperations,
+ agentRoleFields,
+ agentRoleOperations,
+ announcementFields,
+ announcementOperations,
+ assetFields,
+ assetOperations,
+ assetTypeFields,
+ assetTypeOperations,
+ changeFields,
+ changeOperations,
+ departmentFields,
+ departmentOperations,
+ locationFields,
+ locationOperations,
+ problemFields,
+ problemOperations,
+ productFields,
+ productOperations,
+ releaseFields,
+ releaseOperations,
+ requesterFields,
+ requesterGroupFields,
+ requesterGroupOperations,
+ requesterOperations,
+ softwareFields,
+ softwareOperations,
+ ticketFields,
+ ticketOperations,
+} from './descriptions';
+
+import {
+ AddressFixedCollection,
+ LoadedResource,
+ LoadedUser,
+ RolesParameter,
+} from './types';
+
+import {
+ tz,
+} from 'moment-timezone';
+
+export class Freshservice implements INodeType {
+ description: INodeTypeDescription = {
+ displayName: 'Freshservice',
+ name: 'freshservice',
+ icon: 'file:freshservice.svg',
+ group: ['transform'],
+ version: 1,
+ subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
+ description: 'Consume the Freshservice API',
+ defaults: {
+ name: 'Freshservice',
+ color: '#08c7fb',
+ },
+ inputs: ['main'],
+ outputs: ['main'],
+ credentials: [
+ {
+ name: 'freshserviceApi',
+ required: true,
+ },
+ ],
+ properties: [
+ {
+ displayName: 'Resource',
+ name: 'resource',
+ type: 'options',
+ options: [
+ {
+ name: 'Agent',
+ value: 'agent',
+ },
+ {
+ name: 'Agent Group',
+ value: 'agentGroup',
+ },
+ {
+ name: 'Agent Role',
+ value: 'agentRole',
+ },
+ {
+ name: 'Announcement',
+ value: 'announcement',
+ },
+ // {
+ // name: 'Asset',
+ // value: 'asset',
+ // },
+ {
+ name: 'Asset Type',
+ value: 'assetType',
+ },
+ {
+ name: 'Change',
+ value: 'change',
+ },
+ {
+ name: 'Department',
+ value: 'department',
+ },
+ {
+ name: 'Location',
+ value: 'location',
+ },
+ {
+ name: 'Problem',
+ value: 'problem',
+ },
+ {
+ name: 'Product',
+ value: 'product',
+ },
+ {
+ name: 'Release',
+ value: 'release',
+ },
+ {
+ name: 'Requester',
+ value: 'requester',
+ },
+ {
+ name: 'Requester Group',
+ value: 'requesterGroup',
+ },
+ {
+ name: 'Software',
+ value: 'software',
+ },
+ {
+ name: 'Ticket',
+ value: 'ticket',
+ },
+ ],
+ default: 'agent',
+ },
+ ...agentOperations,
+ ...agentFields,
+ ...agentGroupOperations,
+ ...agentGroupFields,
+ ...agentRoleOperations,
+ ...agentRoleFields,
+ ...announcementOperations,
+ ...announcementFields,
+ // ...assetOperations,
+ // ...assetFields,
+ ...assetTypeOperations,
+ ...assetTypeFields,
+ ...changeOperations,
+ ...changeFields,
+ ...departmentOperations,
+ ...departmentFields,
+ ...locationOperations,
+ ...locationFields,
+ ...problemOperations,
+ ...problemFields,
+ ...productOperations,
+ ...productFields,
+ ...releaseOperations,
+ ...releaseFields,
+ ...requesterOperations,
+ ...requesterFields,
+ ...requesterGroupOperations,
+ ...requesterGroupFields,
+ ...softwareOperations,
+ ...softwareFields,
+ ...ticketOperations,
+ ...ticketFields,
+ ],
+ };
+
+ methods = {
+ loadOptions: {
+ async getAgents(this: ILoadOptionsFunctions): Promise {
+ const { agents } = await freshserviceApiRequest.call(this, 'GET', '/agents') as {
+ agents: LoadedUser[];
+ };
+ return toUserOptions(agents.filter((agent) => agent.active));
+ },
+
+ async getAgentGroups(this: ILoadOptionsFunctions): Promise {
+ const { groups } = await freshserviceApiRequest.call(this, 'GET', '/groups') as {
+ groups: LoadedResource[];
+ };
+ return toOptions(groups);
+ },
+
+ async getAgentRoles(this: ILoadOptionsFunctions): Promise {
+ const { roles } = await freshserviceApiRequest.call(this, 'GET', '/roles') as {
+ roles: LoadedResource[];
+ };
+ return toOptions(roles);
+ },
+
+ async getAssetTypes(this: ILoadOptionsFunctions): Promise {
+ const { asset_types } = await freshserviceApiRequest.call(this, 'GET', '/asset_types') as {
+ asset_types: LoadedResource[];
+ };
+ return toOptions(asset_types);
+ },
+
+ async getAssetTypeFields(this: ILoadOptionsFunctions): Promise {
+ const assetType = this.getCurrentNodeParameter('assetTypeId');
+ const { asset_type_fields } = await freshserviceApiRequest.call(this, 'GET', `/asset_types/${assetType}/fields`) as {
+ asset_type_fields: [{ fields: LoadedResource[] }];
+ };
+ // tslint:disable-next-line: no-any
+ let fields: any[] = [];
+ fields = fields.concat(...asset_type_fields.map((data) => data.fields)).map(data => ({ name: data.label, id: data.name }));
+ return toOptions(fields);
+ },
+
+ async getDepartments(this: ILoadOptionsFunctions): Promise {
+ const { departments } = await freshserviceApiRequest.call(this, 'GET', '/departments') as {
+ departments: LoadedResource[];
+ };
+ return toOptions(departments);
+ },
+
+ async getLocations(this: ILoadOptionsFunctions): Promise {
+ const { locations } = await freshserviceApiRequest.call(this, 'GET', '/locations') as {
+ locations: LoadedResource[];
+ };
+ return toOptions(locations);
+ },
+
+ async getRequesters(this: ILoadOptionsFunctions): Promise {
+ const { requesters } = await freshserviceApiRequest.call(this, 'GET', '/requesters') as {
+ requesters: LoadedUser[];
+ };
+ return toUserOptions(requesters);
+ },
+ },
+ };
+
+ async execute(this: IExecuteFunctions): Promise {
+ const items = this.getInputData();
+ const returnData: IDataObject[] = [];
+
+ const resource = this.getNodeParameter('resource', 0) as string;
+ const operation = this.getNodeParameter('operation', 0) as string;
+
+ const defaultTimezone = this.getTimezone();
+
+ let responseData;
+
+ for (let i = 0; i < items.length; i++) {
+
+ try {
+
+ if (resource === 'agent') {
+
+ // **********************************************************************
+ // agent
+ // **********************************************************************
+
+ if (operation === 'create') {
+
+ // ----------------------------------------
+ // agent: create
+ // ----------------------------------------
+
+ const body = {
+ email: this.getNodeParameter('email', i),
+ first_name: this.getNodeParameter('firstName', i),
+ } as IDataObject;
+
+ const roles = this.getNodeParameter('roles', i) as RolesParameter;
+
+ validateAssignmentScopeGroup.call(this, roles);
+ sanitizeAssignmentScopeGroup.call(this, roles);
+
+ Object.assign(body, adjustAgentRoles(roles));
+
+ const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
+
+ if (Object.keys(additionalFields).length) {
+ Object.assign(body, additionalFields);
+ }
+
+ responseData = await freshserviceApiRequest.call(this, 'POST', '/agents', body);
+
+ } else if (operation === 'delete') {
+
+ // ----------------------------------------
+ // agent: delete
+ // ----------------------------------------
+
+ const agentId = this.getNodeParameter('agentId', i);
+ responseData = await freshserviceApiRequest.call(this, 'DELETE', `/agents/${agentId}`);
+
+ } else if (operation === 'get') {
+
+ // ----------------------------------------
+ // agent: get
+ // ----------------------------------------
+
+ const agentId = this.getNodeParameter('agentId', i);
+ responseData = await freshserviceApiRequest.call(this, 'GET', `/agents/${agentId}`);
+
+ } else if (operation === 'getAll') {
+
+ // ----------------------------------------
+ // agent: getAll
+ // ----------------------------------------
+
+ const qs = {} as IDataObject;
+ const filters = this.getNodeParameter('filters', i) as IDataObject;
+
+ if (Object.keys(filters).length) {
+ Object.assign(qs, formatFilters(filters));
+ }
+
+ responseData = await handleListing.call(this, 'GET', '/agents', {}, qs);
+
+ } else if (operation === 'update') {
+
+ // ----------------------------------------
+ // agent: update
+ // ----------------------------------------
+
+ const body = {} as IDataObject;
+ const updateFields = this.getNodeParameter('updateFields', i) as IDataObject;
+
+ validateUpdateFields.call(this, updateFields, resource);
+
+ Object.assign(body, updateFields);
+
+ const agentId = this.getNodeParameter('agentId', i);
+ responseData = await freshserviceApiRequest.call(this, 'PUT', `/agents/${agentId}`, body);
+
+ }
+
+ } else if (resource === 'agentGroup') {
+
+ // **********************************************************************
+ // agentGroup
+ // **********************************************************************
+
+ if (operation === 'create') {
+
+ // ----------------------------------------
+ // agentGroup: create
+ // ----------------------------------------
+
+ const body = {
+ name: this.getNodeParameter('name', i),
+ } as IDataObject;
+
+ const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
+
+ if (Object.keys(additionalFields).length) {
+ Object.assign(body, additionalFields);
+ }
+
+ responseData = await freshserviceApiRequest.call(this, 'POST', '/groups', body);
+
+ } else if (operation === 'delete') {
+
+ // ----------------------------------------
+ // agentGroup: delete
+ // ----------------------------------------
+
+ const agentGroupId = this.getNodeParameter('agentGroupId', i);
+ const endpoint = `/groups/${agentGroupId}`;
+ responseData = await freshserviceApiRequest.call(this, 'DELETE', endpoint);
+
+ } else if (operation === 'get') {
+
+ // ----------------------------------------
+ // agentGroup: get
+ // ----------------------------------------
+
+ const agentGroupId = this.getNodeParameter('agentGroupId', i);
+ const endpoint = `/groups/${agentGroupId}`;
+ responseData = await freshserviceApiRequest.call(this, 'GET', endpoint);
+
+ } else if (operation === 'getAll') {
+
+ // ----------------------------------------
+ // agentGroup: getAll
+ // ----------------------------------------
+
+ responseData = await handleListing.call(this, 'GET', '/groups');
+
+ } else if (operation === 'update') {
+
+ // ----------------------------------------
+ // agentGroup: update
+ // ----------------------------------------
+
+ const body = {} as IDataObject;
+ const updateFields = this.getNodeParameter('updateFields', i) as IDataObject;
+
+ validateUpdateFields.call(this, updateFields, resource);
+
+ Object.assign(body, updateFields);
+
+ const agentGroupId = this.getNodeParameter('agentGroupId', i);
+ const endpoint = `/groups/${agentGroupId}`;
+ responseData = await freshserviceApiRequest.call(this, 'PUT', endpoint, body);
+
+ }
+
+ } else if (resource === 'agentRole') {
+
+ // **********************************************************************
+ // agentRole
+ // **********************************************************************
+
+ if (operation === 'get') {
+
+ // ----------------------------------------
+ // agentRole: get
+ // ----------------------------------------
+
+ const agentRoleId = this.getNodeParameter('agentRoleId', i);
+ responseData = await freshserviceApiRequest.call(this, 'GET', `/roles/${agentRoleId}`);
+
+ } else if (operation === 'getAll') {
+
+ // ----------------------------------------
+ // agentRole: getAll
+ // ----------------------------------------
+
+ responseData = await handleListing.call(this, 'GET', '/roles');
+
+ }
+
+ } else if (resource === 'announcement') {
+
+ // **********************************************************************
+ // announcement
+ // **********************************************************************
+
+ if (operation === 'create') {
+
+ // ----------------------------------------
+ // announcement: create
+ // ----------------------------------------
+
+ const visibleFrom = this.getNodeParameter('visibleFrom', i) as string;
+
+ const body = {
+ title: this.getNodeParameter('title', i),
+ body_html: this.getNodeParameter('bodyHtml', i),
+ visibility: this.getNodeParameter('visibility', i),
+ visible_from: tz(visibleFrom, defaultTimezone),
+ } as IDataObject;
+
+ const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject & {
+ additional_emails?: string;
+ visible_till?: string;
+ };
+
+ if (Object.keys(additionalFields).length) {
+ const { visible_till, additional_emails, ...rest } = additionalFields;
+
+ Object.assign(body, {
+ ...(additional_emails && { additional_emails: toArray(additional_emails) }),
+ ...(visible_till && { visible_till: tz(visible_till, defaultTimezone) }),
+ ...rest,
+ });
+ }
+
+ responseData = await freshserviceApiRequest.call(this, 'POST', '/announcements', body);
+
+ } else if (operation === 'delete') {
+
+ // ----------------------------------------
+ // announcement: delete
+ // ----------------------------------------
+
+ const announcementId = this.getNodeParameter('announcementId', i);
+ const endpoint = `/announcements/${announcementId}`;
+ responseData = await freshserviceApiRequest.call(this, 'DELETE', endpoint);
+
+ } else if (operation === 'get') {
+
+ // ----------------------------------------
+ // announcement: get
+ // ----------------------------------------
+
+ const announcementId = this.getNodeParameter('announcementId', i);
+
+ const endpoint = `/announcements/${announcementId}`;
+ responseData = await freshserviceApiRequest.call(this, 'GET', endpoint);
+
+ } else if (operation === 'getAll') {
+
+ // ----------------------------------------
+ // announcement: getAll
+ // ----------------------------------------
+
+ responseData = await handleListing.call(this, 'GET', '/announcements');
+
+ } else if (operation === 'update') {
+
+ // ----------------------------------------
+ // announcement: update
+ // ----------------------------------------
+
+ const body = {} as IDataObject;
+ const updateFields = this.getNodeParameter('updateFields', i) as IDataObject & {
+ additional_emails?: string;
+ visible_till?: string;
+ };
+
+ validateUpdateFields.call(this, updateFields, resource);
+
+ const { visible_till, additional_emails, ...rest } = updateFields;
+
+ Object.assign(body, {
+ ...(additional_emails && { additional_emails: toArray(additional_emails) }),
+ ...(visible_till && { visible_till: tz(visible_till, defaultTimezone) }),
+ ...rest,
+ });
+
+ const announcementId = this.getNodeParameter('announcementId', i);
+ const endpoint = `/announcements/${announcementId}`;
+ responseData = await freshserviceApiRequest.call(this, 'PUT', endpoint, body);
+
+ }
+
+ } else if (resource === 'asset') {
+
+ // **********************************************************************
+ // asset
+ // **********************************************************************
+
+ if (operation === 'create') {
+
+ // ----------------------------------------
+ // asset: create
+ // ----------------------------------------
+
+ const body = {
+ asset_type_id: this.getNodeParameter('assetTypeId', i),
+ name: this.getNodeParameter('name', i),
+ } as IDataObject;
+
+ const assetFields = this.getNodeParameter('assetFieldsUi.assetFieldValue', i, []) as IDataObject[];
+
+ Object.assign(body, { type_fields: assetFields.reduce((obj, value) => Object.assign(obj, { [`${value.name}`]: value.value }), {}) });
+ console.log(body);
+ responseData = await freshserviceApiRequest.call(this, 'POST', '/assets', body);
+
+ } else if (operation === 'delete') {
+
+ // ----------------------------------------
+ // asset: delete
+ // ----------------------------------------
+
+ const assetDisplayId = this.getNodeParameter('assetDisplayId', i);
+ responseData = await freshserviceApiRequest.call(this, 'DELETE', `/assets/${assetDisplayId}`);
+
+ } else if (operation === 'get') {
+
+ // ----------------------------------------
+ // asset: get
+ // ----------------------------------------
+
+ const assetDisplayId = this.getNodeParameter('assetDisplayId', i);
+ responseData = await freshserviceApiRequest.call(this, 'GET', `/assets/${assetDisplayId}`);
+
+ } else if (operation === 'getAll') {
+
+ // ----------------------------------------
+ // asset: getAll
+ // ----------------------------------------
+
+ const qs = {} as IDataObject;
+ const filters = this.getNodeParameter('filters', i) as IDataObject;
+
+ if (Object.keys(filters).length) {
+ Object.assign(qs, formatFilters(filters));
+ }
+
+ responseData = await handleListing.call(this, 'GET', '/assets', {}, qs);
+
+ } else if (operation === 'update') {
+
+ // ----------------------------------------
+ // asset: update
+ // ----------------------------------------
+
+ const body = {} as IDataObject;
+ const updateFields = this.getNodeParameter('updateFields', i) as IDataObject;
+
+ validateUpdateFields.call(this, updateFields, resource);
+
+ Object.assign(body, updateFields);
+
+ const assetDisplayId = this.getNodeParameter('assetDisplayId', i);
+ responseData = await freshserviceApiRequest.call(this, 'PUT', `/assets/${assetDisplayId}`, body);
+
+ }
+
+ } else if (resource === 'assetType') {
+
+ // **********************************************************************
+ // assetType
+ // **********************************************************************
+
+ if (operation === 'create') {
+
+ // ----------------------------------------
+ // assetType: create
+ // ----------------------------------------
+
+ const body = {
+ name: this.getNodeParameter('name', i),
+ } as IDataObject;
+
+ const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
+
+ if (Object.keys(additionalFields).length) {
+ Object.assign(body, additionalFields);
+ }
+
+ responseData = await freshserviceApiRequest.call(this, 'POST', '/asset_types', body);
+
+ } else if (operation === 'delete') {
+
+ // ----------------------------------------
+ // assetType: delete
+ // ----------------------------------------
+
+ const assetTypeId = this.getNodeParameter('assetTypeId', i);
+
+ const endpoint = `/asset_types/${assetTypeId}`;
+ responseData = await freshserviceApiRequest.call(this, 'DELETE', endpoint);
+
+ } else if (operation === 'get') {
+
+ // ----------------------------------------
+ // assetType: get
+ // ----------------------------------------
+
+ const assetTypeId = this.getNodeParameter('assetTypeId', i);
+ const endpoint = `/asset_types/${assetTypeId}`;
+ responseData = await freshserviceApiRequest.call(this, 'GET', endpoint);
+
+ } else if (operation === 'getAll') {
+
+ // ----------------------------------------
+ // assetType: getAll
+ // ----------------------------------------
+
+ responseData = await handleListing.call(this, 'GET', '/asset_types');
+
+ } else if (operation === 'update') {
+
+ // ----------------------------------------
+ // assetType: update
+ // ----------------------------------------
+
+ const body = {} as IDataObject;
+ const updateFields = this.getNodeParameter('updateFields', i) as IDataObject;
+
+ validateUpdateFields.call(this, updateFields, resource);
+
+ Object.assign(body, updateFields);
+
+ const assetTypeId = this.getNodeParameter('assetTypeId', i);
+ const endpoint = `/asset_types/${assetTypeId}`;
+ responseData = await freshserviceApiRequest.call(this, 'PUT', endpoint, body);
+
+ }
+
+ } else if (resource === 'change') {
+
+ // **********************************************************************
+ // change
+ // **********************************************************************
+
+ if (operation === 'create') {
+
+ // ----------------------------------------
+ // change: create
+ // ----------------------------------------
+
+ const body = {
+ requester_id: this.getNodeParameter('requesterId', i),
+ subject: this.getNodeParameter('subject', i),
+ planned_start_date: this.getNodeParameter('plannedStartDate', i),
+ planned_end_date: this.getNodeParameter('plannedEndDate', i),
+ status: 1,
+ priority: 1,
+ impact: 1,
+ risk: 1,
+ change_type: 1,
+ } as IDataObject;
+
+ const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
+
+ if (Object.keys(additionalFields).length) {
+ Object.assign(body, additionalFields);
+ }
+
+ responseData = await freshserviceApiRequest.call(this, 'POST', '/changes', body);
+
+ } else if (operation === 'delete') {
+
+ // ----------------------------------------
+ // change: delete
+ // ----------------------------------------
+
+ const changeId = this.getNodeParameter('changeId', i);
+ responseData = await freshserviceApiRequest.call(this, 'DELETE', `/changes/${changeId}`);
+
+ } else if (operation === 'get') {
+
+ // ----------------------------------------
+ // change: get
+ // ----------------------------------------
+
+ const changeId = this.getNodeParameter('changeId', i);
+ responseData = await freshserviceApiRequest.call(this, 'GET', `/changes/${changeId}`);
+
+ } else if (operation === 'getAll') {
+
+ // ----------------------------------------
+ // change: getAll
+ // ----------------------------------------
+
+ const qs = {} as IDataObject;
+ const filters = this.getNodeParameter('filters', i) as IDataObject;
+
+ if (Object.keys(filters).length) {
+ Object.assign(qs, formatFilters(filters));
+ }
+
+ responseData = await handleListing.call(this, 'GET', '/changes', {}, qs);
+
+ } else if (operation === 'update') {
+
+ // ----------------------------------------
+ // change: update
+ // ----------------------------------------
+
+ const body = {} as IDataObject;
+ const updateFields = this.getNodeParameter('updateFields', i) as IDataObject;
+
+ validateUpdateFields.call(this, updateFields, resource);
+
+ Object.assign(body, updateFields);
+
+ const changeId = this.getNodeParameter('changeId', i);
+ responseData = await freshserviceApiRequest.call(this, 'PUT', `/changes/${changeId}`, body);
+
+ }
+
+ } else if (resource === 'department') {
+
+ // **********************************************************************
+ // department
+ // **********************************************************************
+
+ if (operation === 'create') {
+
+ // ----------------------------------------
+ // department: create
+ // ----------------------------------------
+
+ const body = {
+ name: this.getNodeParameter('name', i),
+ } as IDataObject;
+
+ const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject & {
+ domains?: string;
+ };
+
+ if (Object.keys(additionalFields).length) {
+ const { domains, ...rest } = additionalFields;
+ Object.assign(body, {
+ ...(domains && { domains: toArray(domains) }),
+ ...rest,
+ });
+ }
+
+ responseData = await freshserviceApiRequest.call(this, 'POST', '/departments', body);
+
+ } else if (operation === 'delete') {
+
+ // ----------------------------------------
+ // department: delete
+ // ----------------------------------------
+
+ const departmentId = this.getNodeParameter('departmentId', i);
+ const endpoint = `/departments/${departmentId}`;
+ responseData = await freshserviceApiRequest.call(this, 'DELETE', endpoint);
+
+ } else if (operation === 'get') {
+
+ // ----------------------------------------
+ // department: get
+ // ----------------------------------------
+
+ const departmentId = this.getNodeParameter('departmentId', i);
+ const endpoint = `/departments/${departmentId}`;
+ responseData = await freshserviceApiRequest.call(this, 'GET', endpoint);
+
+ } else if (operation === 'getAll') {
+
+ // ----------------------------------------
+ // department: getAll
+ // ----------------------------------------
+
+ const qs = {} as IDataObject;
+ const filters = this.getNodeParameter('filters', i) as IDataObject;
+
+ if (Object.keys(filters).length) {
+ Object.assign(qs, formatFilters(filters));
+ }
+
+ responseData = await handleListing.call(this, 'GET', '/departments', {}, qs);
+
+ } else if (operation === 'update') {
+
+ // ----------------------------------------
+ // department: update
+ // ----------------------------------------
+
+ const body = {} as IDataObject;
+ const updateFields = this.getNodeParameter('updateFields', i) as IDataObject & {
+ domains?: string;
+ };
+
+ validateUpdateFields.call(this, updateFields, resource);
+
+ const { domains, ...rest } = updateFields;
+ Object.assign(body, {
+ ...(domains && { domains: toArray(domains) }),
+ ...rest,
+ });
+
+ const departmentId = this.getNodeParameter('departmentId', i);
+ const endpoint = `/departments/${departmentId}`;
+ responseData = await freshserviceApiRequest.call(this, 'PUT', endpoint, body);
+
+ }
+
+ } else if (resource === 'location') {
+
+ // **********************************************************************
+ // location
+ // **********************************************************************
+
+ if (operation === 'create') {
+
+ // ----------------------------------------
+ // location: create
+ // ----------------------------------------
+
+ const body = {
+ name: this.getNodeParameter('name', i),
+ } as IDataObject;
+
+ const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject & AddressFixedCollection;
+
+ if (Object.keys(additionalFields).length) {
+ Object.assign(body, adjustAddress(additionalFields));
+ }
+
+ responseData = await freshserviceApiRequest.call(this, 'POST', '/locations', body);
+
+ } else if (operation === 'delete') {
+
+ // ----------------------------------------
+ // location: delete
+ // ----------------------------------------
+
+ const locationId = this.getNodeParameter('locationId', i);
+ const endpoint = `/locations/${locationId}`;
+ responseData = await freshserviceApiRequest.call(this, 'DELETE', endpoint);
+
+ } else if (operation === 'get') {
+
+ // ----------------------------------------
+ // location: get
+ // ----------------------------------------
+
+ const locationId = this.getNodeParameter('locationId', i);
+ const endpoint = `/locations/${locationId}`;
+ responseData = await freshserviceApiRequest.call(this, 'GET', endpoint);
+
+ } else if (operation === 'getAll') {
+
+ // ----------------------------------------
+ // location: getAll
+ // ----------------------------------------
+
+ responseData = await handleListing.call(this, 'GET', '/locations');
+
+ } else if (operation === 'update') {
+
+ // ----------------------------------------
+ // location: update
+ // ----------------------------------------
+
+ const body = {} as IDataObject;
+ const updateFields = this.getNodeParameter('updateFields', i) as IDataObject;
+
+ validateUpdateFields.call(this, updateFields, resource);
+
+ Object.assign(body, adjustAddress(updateFields));
+
+ const locationId = this.getNodeParameter('locationId', i);
+ const endpoint = `/locations/${locationId}`;
+ responseData = await freshserviceApiRequest.call(this, 'PUT', endpoint, body);
+
+ }
+
+ } else if (resource === 'problem') {
+
+ // **********************************************************************
+ // problem
+ // **********************************************************************
+
+ if (operation === 'create') {
+
+ // ----------------------------------------
+ // problem: create
+ // ----------------------------------------
+
+ const body = {
+ subject: this.getNodeParameter('subject', i),
+ requester_id: this.getNodeParameter('requesterId', i),
+ due_by: this.getNodeParameter('dueBy', i),
+ status: 1,
+ priority: 1,
+ impact: 1,
+ } as IDataObject;
+
+ const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
+
+ if (Object.keys(additionalFields).length) {
+ Object.assign(body, additionalFields);
+ }
+
+ responseData = await freshserviceApiRequest.call(this, 'POST', '/problems', body);
+
+ } else if (operation === 'delete') {
+
+ // ----------------------------------------
+ // problem: delete
+ // ----------------------------------------
+
+ const problemId = this.getNodeParameter('problemId', i);
+ const endpoint = `/problems/${problemId}`;
+ responseData = await freshserviceApiRequest.call(this, 'DELETE', endpoint);
+
+ } else if (operation === 'get') {
+
+ // ----------------------------------------
+ // problem: get
+ // ----------------------------------------
+
+ const problemId = this.getNodeParameter('problemId', i);
+ const endpoint = `/problems/${problemId}`;
+ responseData = await freshserviceApiRequest.call(this, 'GET', endpoint);
+
+ } else if (operation === 'getAll') {
+
+ // ----------------------------------------
+ // problem: getAll
+ // ----------------------------------------
+
+ responseData = await handleListing.call(this, 'GET', '/problems');
+
+ } else if (operation === 'update') {
+
+ // ----------------------------------------
+ // problem: update
+ // ----------------------------------------
+
+ const body = {} as IDataObject;
+ const updateFields = this.getNodeParameter('updateFields', i) as IDataObject;
+
+ validateUpdateFields.call(this, updateFields, resource);
+
+ Object.assign(body, updateFields);
+
+ const problemId = this.getNodeParameter('problemId', i);
+ const endpoint = `/problems/${problemId}`;
+ responseData = await freshserviceApiRequest.call(this, 'PUT', endpoint, body);
+
+ }
+
+ } else if (resource === 'product') {
+
+ // **********************************************************************
+ // product
+ // **********************************************************************
+
+ if (operation === 'create') {
+
+ // ----------------------------------------
+ // product: create
+ // ----------------------------------------
+
+ const body = {
+ asset_type_id: this.getNodeParameter('assetTypeId', i),
+ name: this.getNodeParameter('name', i),
+ } as IDataObject;
+
+ const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
+
+ if (Object.keys(additionalFields).length) {
+ Object.assign(body, additionalFields);
+ }
+
+ responseData = await freshserviceApiRequest.call(this, 'POST', '/products', body);
+
+ } else if (operation === 'delete') {
+
+ // ----------------------------------------
+ // product: delete
+ // ----------------------------------------
+
+ const productId = this.getNodeParameter('productId', i);
+
+ const endpoint = `/products/${productId}`;
+ responseData = await freshserviceApiRequest.call(this, 'DELETE', endpoint);
+
+ } else if (operation === 'get') {
+
+ // ----------------------------------------
+ // product: get
+ // ----------------------------------------
+
+ const productId = this.getNodeParameter('productId', i);
+ const endpoint = `/products/${productId}`;
+ responseData = await freshserviceApiRequest.call(this, 'GET', endpoint);
+
+ } else if (operation === 'getAll') {
+
+ // ----------------------------------------
+ // product: getAll
+ // ----------------------------------------
+
+ responseData = await handleListing.call(this, 'GET', '/products');
+
+ } else if (operation === 'update') {
+
+ // ----------------------------------------
+ // product: update
+ // ----------------------------------------
+
+ const body = {} as IDataObject;
+ const updateFields = this.getNodeParameter('updateFields', i) as IDataObject;
+
+ Object.assign(body, updateFields);
+
+ const productId = this.getNodeParameter('productId', i);
+ const endpoint = `/products/${productId}`;
+ responseData = await freshserviceApiRequest.call(this, 'PUT', endpoint, body);
+
+ }
+
+ } else if (resource === 'release') {
+
+ // **********************************************************************
+ // release
+ // **********************************************************************
+
+ if (operation === 'create') {
+
+ // ----------------------------------------
+ // release: create
+ // ----------------------------------------
+
+ const body = {
+ subject: this.getNodeParameter('subject', i),
+ release_type: this.getNodeParameter('releaseType', i),
+ status: this.getNodeParameter('status', i),
+ priority: this.getNodeParameter('priority', i),
+ planned_start_date: this.getNodeParameter('plannedStartDate', i),
+ planned_end_date: this.getNodeParameter('plannedEndDate', i),
+ } as IDataObject;
+
+ const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
+
+ if (Object.keys(additionalFields).length) {
+ Object.assign(body, additionalFields);
+ }
+
+ responseData = await freshserviceApiRequest.call(this, 'POST', '/releases', body);
+
+ } else if (operation === 'delete') {
+
+ // ----------------------------------------
+ // release: delete
+ // ----------------------------------------
+
+ const releaseId = this.getNodeParameter('releaseId', i);
+ const endpoint = `/releases/${releaseId}`;
+ responseData = await freshserviceApiRequest.call(this, 'DELETE', endpoint);
+
+ } else if (operation === 'get') {
+
+ // ----------------------------------------
+ // release: get
+ // ----------------------------------------
+
+ const releaseId = this.getNodeParameter('releaseId', i);
+ const endpoint = `/releases/${releaseId}`;
+ responseData = await freshserviceApiRequest.call(this, 'GET', endpoint);
+
+ } else if (operation === 'getAll') {
+
+ // ----------------------------------------
+ // release: getAll
+ // ----------------------------------------
+
+ responseData = await handleListing.call(this, 'GET', '/releases');
+
+ } else if (operation === 'update') {
+
+ // ----------------------------------------
+ // release: update
+ // ----------------------------------------
+
+ const body = {} as IDataObject;
+ const updateFields = this.getNodeParameter('updateFields', i) as IDataObject;
+
+ Object.assign(body, updateFields);
+
+ const releaseId = this.getNodeParameter('releaseId', i);
+ const endpoint = `/releases/${releaseId}`;
+ responseData = await freshserviceApiRequest.call(this, 'PUT', endpoint, body);
+
+ }
+
+ } else if (resource === 'requester') {
+
+ // **********************************************************************
+ // requester
+ // **********************************************************************
+
+ if (operation === 'create') {
+
+ // ----------------------------------------
+ // requester: create
+ // ----------------------------------------
+
+ const body = {
+ first_name: this.getNodeParameter('firstName', i),
+ primary_email: this.getNodeParameter('primaryEmail', i),
+ } as IDataObject;
+
+ const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject & {
+ secondary_emails?: string;
+ };
+
+ if (Object.keys(additionalFields).length) {
+ const { secondary_emails, ...rest } = additionalFields;
+
+ Object.assign(body, {
+ ...(secondary_emails && { secondary_emails: toArray(secondary_emails) }),
+ ...rest,
+ });
+ }
+
+ responseData = await freshserviceApiRequest.call(this, 'POST', '/requesters', body);
+
+ } else if (operation === 'delete') {
+
+ // ----------------------------------------
+ // requester: delete
+ // ----------------------------------------
+
+ const requesterId = this.getNodeParameter('requesterId', i);
+ const endpoint = `/requesters/${requesterId}`;
+ responseData = await freshserviceApiRequest.call(this, 'DELETE', endpoint);
+
+ } else if (operation === 'get') {
+
+ // ----------------------------------------
+ // requester: get
+ // ----------------------------------------
+
+ const requesterId = this.getNodeParameter('requesterId', i);
+ const endpoint = `/requesters/${requesterId}`;
+ responseData = await freshserviceApiRequest.call(this, 'GET', endpoint);
+
+ } else if (operation === 'getAll') {
+
+ // ----------------------------------------
+ // requester: getAll
+ // ----------------------------------------
+
+ const qs = {} as IDataObject;
+ const filters = this.getNodeParameter('filters', i) as IDataObject;
+
+ if (Object.keys(filters).length) {
+ Object.assign(qs, formatFilters(filters));
+ }
+
+ responseData = await handleListing.call(this, 'GET', '/requesters', {}, qs);
+
+ } else if (operation === 'update') {
+
+ // ----------------------------------------
+ // requester: update
+ // ----------------------------------------
+
+ const body = {} as IDataObject;
+ const updateFields = this.getNodeParameter('updateFields', i) as IDataObject & {
+ secondary_emails?: string;
+ };
+
+ validateUpdateFields.call(this, updateFields, resource);
+
+ const { secondary_emails, ...rest } = updateFields;
+
+ Object.assign(body, {
+ ...(secondary_emails && { secondary_emails: toArray(secondary_emails) }),
+ ...rest,
+ });
+
+ const requesterId = this.getNodeParameter('requesterId', i);
+ const endpoint = `/requesters/${requesterId}`;
+ responseData = await freshserviceApiRequest.call(this, 'PUT', endpoint, body);
+
+ }
+
+ } else if (resource === 'requesterGroup') {
+
+ // **********************************************************************
+ // requesterGroup
+ // **********************************************************************
+
+ if (operation === 'create') {
+
+ // ----------------------------------------
+ // requesterGroup: create
+ // ----------------------------------------
+
+ const body = {
+ name: this.getNodeParameter('name', i),
+ } as IDataObject;
+
+ const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
+
+ if (Object.keys(additionalFields).length) {
+ Object.assign(body, additionalFields);
+ }
+
+ responseData = await freshserviceApiRequest.call(this, 'POST', '/requester_groups', body);
+
+ } else if (operation === 'delete') {
+
+ // ----------------------------------------
+ // requesterGroup: delete
+ // ----------------------------------------
+
+ const requesterGroupId = this.getNodeParameter('requesterGroupId', i);
+ const endpoint = `/requester_groups/${requesterGroupId}`;
+ responseData = await freshserviceApiRequest.call(this, 'DELETE', endpoint);
+
+ } else if (operation === 'get') {
+
+ // ----------------------------------------
+ // requesterGroup: get
+ // ----------------------------------------
+
+ const requesterGroupId = this.getNodeParameter('requesterGroupId', i);
+ const endpoint = `/requester_groups/${requesterGroupId}`;
+ responseData = await freshserviceApiRequest.call(this, 'GET', endpoint);
+
+ } else if (operation === 'getAll') {
+
+ // ----------------------------------------
+ // requesterGroup: getAll
+ // ----------------------------------------
+
+ responseData = await handleListing.call(this, 'GET', '/requester_groups');
+
+ } else if (operation === 'update') {
+
+ // ----------------------------------------
+ // requesterGroup: update
+ // ----------------------------------------
+
+ const body = {} as IDataObject;
+ const updateFields = this.getNodeParameter('updateFields', i) as IDataObject;
+
+ validateUpdateFields.call(this, updateFields, resource);
+
+ Object.assign(body, updateFields);
+
+ const requesterGroupId = this.getNodeParameter('requesterGroupId', i);
+ const endpoint = `/requester_groups/${requesterGroupId}`;
+ responseData = await freshserviceApiRequest.call(this, 'PUT', endpoint, body);
+
+ }
+
+ } else if (resource === 'software') {
+
+ // **********************************************************************
+ // software
+ // **********************************************************************
+
+ if (operation === 'create') {
+
+ // ----------------------------------------
+ // software: create
+ // ----------------------------------------
+
+ const body = {
+ application: {
+ application_type: this.getNodeParameter('applicationType', i),
+ name: this.getNodeParameter('name', i),
+ },
+ } as IDataObject;
+
+ const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
+
+ if (Object.keys(additionalFields).length) {
+ Object.assign(body.application, additionalFields);
+ }
+
+ responseData = await freshserviceApiRequest.call(this, 'POST', '/applications', body);
+
+ } else if (operation === 'delete') {
+
+ // ----------------------------------------
+ // software: delete
+ // ----------------------------------------
+
+ const softwareId = this.getNodeParameter('softwareId', i);
+ const endpoint = `/applications/${softwareId}`;
+ responseData = await freshserviceApiRequest.call(this, 'DELETE', endpoint);
+
+ } else if (operation === 'get') {
+
+ // ----------------------------------------
+ // software: get
+ // ----------------------------------------
+
+ const softwareId = this.getNodeParameter('softwareId', i);
+ const endpoint = `/applications/${softwareId}`;
+ responseData = await freshserviceApiRequest.call(this, 'GET', endpoint);
+
+ } else if (operation === 'getAll') {
+
+ // ----------------------------------------
+ // software: getAll
+ // ----------------------------------------
+
+ responseData = await handleListing.call(this, 'GET', '/applications');
+
+ } else if (operation === 'update') {
+
+ // ----------------------------------------
+ // software: update
+ // ----------------------------------------
+
+ const body = { application: {} } as IDataObject;
+ const updateFields = this.getNodeParameter('updateFields', i) as IDataObject;
+
+ validateUpdateFields.call(this, updateFields, resource);
+
+ Object.assign(body.application, updateFields);
+
+ const softwareId = this.getNodeParameter('softwareId', i);
+ const endpoint = `/applications/${softwareId}`;
+ responseData = await freshserviceApiRequest.call(this, 'PUT', endpoint, body);
+
+ }
+
+ } else if (resource === 'ticket') {
+
+ // **********************************************************************
+ // ticket
+ // **********************************************************************
+
+ if (operation === 'create') {
+
+ // ----------------------------------------
+ // ticket: create
+ // ----------------------------------------
+
+ const body = {
+ email: this.getNodeParameter('email', i),
+ subject: this.getNodeParameter('subject', i),
+ description: this.getNodeParameter('description', i),
+ priority: this.getNodeParameter('priority', i),
+ status: this.getNodeParameter('status', i),
+ } as IDataObject;
+
+ const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject & {
+ cc_emails?: string,
+ };
+
+ if (Object.keys(additionalFields).length) {
+ const { cc_emails, ...rest } = additionalFields;
+
+ Object.assign(body, {
+ ...(cc_emails && { cc_emails: toArray(cc_emails) }),
+ ...rest,
+ });
+ }
+
+ responseData = await freshserviceApiRequest.call(this, 'POST', '/tickets', body);
+
+ } else if (operation === 'delete') {
+
+ // ----------------------------------------
+ // ticket: delete
+ // ----------------------------------------
+
+ const ticketId = this.getNodeParameter('ticketId', i);
+ responseData = await freshserviceApiRequest.call(this, 'DELETE', `/tickets/${ticketId}`);
+
+ } else if (operation === 'get') {
+
+ // ----------------------------------------
+ // ticket: get
+ // ----------------------------------------
+
+ const ticketId = this.getNodeParameter('ticketId', i);
+ responseData = await freshserviceApiRequest.call(this, 'GET', `/tickets/${ticketId}`);
+
+ } else if (operation === 'getAll') {
+
+ // ----------------------------------------
+ // ticket: getAll
+ // ----------------------------------------
+
+ const qs = {} as IDataObject;
+ const filters = this.getNodeParameter('filters', i) as IDataObject;
+ let endpoint = '';
+
+ if (Object.keys(filters).length) {
+ Object.assign(qs, formatFilters(filters));
+ endpoint = '/tickets/filter';
+ } else {
+ endpoint = '/tickets';
+ }
+
+ responseData = await handleListing.call(this, 'GET', endpoint, {}, qs);
+
+ } else if (operation === 'update') {
+
+ // ----------------------------------------
+ // ticket: update
+ // ----------------------------------------
+
+ const body = {} as IDataObject;
+ const updateFields = this.getNodeParameter('updateFields', i) as IDataObject;
+
+ validateUpdateFields.call(this, updateFields, resource);
+
+ Object.assign(body, updateFields);
+
+ const ticketId = this.getNodeParameter('ticketId', i);
+ responseData = await freshserviceApiRequest.call(this, 'PUT', `/tickets/${ticketId}`, body);
+
+ }
+ }
+
+ if (operation === 'delete' && !responseData) {
+ responseData = { success: true };
+ } else if (operation !== 'getAll') {
+ const special: { [key: string]: string } = {
+ agentGroup: 'group',
+ agentRole: 'role',
+ assetType: 'asset_type',
+ requesterGroup: 'requester_group',
+ software: 'application',
+ };
+ responseData = responseData[special[resource]] ?? responseData[resource];
+ }
+
+ } 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/Freshservice/GenericFunctions.ts b/packages/nodes-base/nodes/Freshservice/GenericFunctions.ts
new file mode 100644
index 0000000000..96b94184ae
--- /dev/null
+++ b/packages/nodes-base/nodes/Freshservice/GenericFunctions.ts
@@ -0,0 +1,243 @@
+import {
+ IExecuteFunctions,
+ IHookFunctions,
+} from 'n8n-core';
+
+import {
+ IDataObject,
+ ILoadOptionsFunctions,
+ NodeApiError,
+ NodeOperationError,
+} from 'n8n-workflow';
+
+import {
+ AddressFixedCollection,
+ FreshserviceCredentials,
+ LoadedUser,
+ RolesParameter,
+} from './types';
+
+import {
+ OptionsWithUri,
+} from 'request';
+
+import {
+ omit,
+} from 'lodash';
+
+export async function freshserviceApiRequest(
+ this: IExecuteFunctions | IHookFunctions | ILoadOptionsFunctions,
+ method: string,
+ endpoint: string,
+ body: IDataObject = {},
+ qs: IDataObject = {},
+) {
+ const { apiKey, domain } = await this.getCredentials('freshserviceApi') as FreshserviceCredentials;
+ const encodedApiKey = Buffer.from(`${apiKey}:X`).toString('base64');
+
+ const options: OptionsWithUri = {
+ headers: {
+ Authorization: `Basic ${encodedApiKey}`,
+ },
+ method,
+ body,
+ qs,
+ uri: `https://${domain}.freshservice.com/api/v2${endpoint}`,
+ json: true,
+ };
+
+ if (!Object.keys(body).length) {
+ delete options.body;
+ }
+
+ if (!Object.keys(qs).length) {
+ delete options.qs;
+ }
+
+ try {
+ return await this.helpers.request!(options);
+ } catch (error) {
+ if (error.error.description === 'Validation failed') {
+
+ const numberOfErrors = error.error.errors.length;
+ const message = 'Please check your parameters';
+
+ if (numberOfErrors === 1) {
+ const [validationError] = error.error.errors;
+ throw new NodeApiError(this.getNode(), error, {
+ message,
+ description: `For ${validationError.field}: ${validationError.message}`,
+ });
+
+ } else if (numberOfErrors > 1) {
+ throw new NodeApiError(this.getNode(), error, {
+ message,
+ description: 'For more information, expand \'details\' below and look at \'cause\' section',
+ });
+ }
+ }
+
+ throw new NodeApiError(this.getNode(), error);
+ }
+}
+
+export async function freshserviceApiRequestAllItems(
+ this: IExecuteFunctions | IHookFunctions,
+ method: string,
+ endpoint: string,
+ body: IDataObject = {},
+ qs: IDataObject = {},
+) {
+ const returnData: IDataObject[] = [];
+ qs.page = 1;
+ let items;
+
+ do {
+ const responseData = await freshserviceApiRequest.call(this, method, endpoint, body, qs);
+ const key = Object.keys(responseData)[0];
+ items = responseData[key];
+ if (!items.length) return returnData;
+ returnData.push(...items);
+ qs.page++;
+ } while (
+ items.length >= 30
+ );
+
+ return returnData;
+}
+
+export async function handleListing(
+ this: IExecuteFunctions,
+ method: string,
+ endpoint: string,
+ body: IDataObject = {},
+ qs: IDataObject = {},
+) {
+ const returnAll = this.getNodeParameter('returnAll', 0) as boolean;
+
+ if (returnAll) {
+ return await freshserviceApiRequestAllItems.call(this, method, endpoint, body, qs);
+ }
+
+ const responseData = await freshserviceApiRequestAllItems.call(this, method, endpoint, body, qs);
+ const limit = this.getNodeParameter('limit', 0) as number;
+
+ return responseData.slice(0, limit);
+}
+
+export const toOptions = (loadedResources: LoadedResource[]) => {
+ return loadedResources
+ .map(({ id, name }) => ({ value: id, name }))
+ .sort((a, b) => a.name.localeCompare(b.name));
+};
+
+export const toUserOptions = (loadedUsers: LoadedUser[]) => {
+ return loadedUsers
+ .map(({ id, last_name, first_name }) => {
+ return {
+ value: id,
+ name: last_name ? `${last_name}, ${first_name}` : `${first_name}`,
+ };
+ })
+ .sort((a, b) => a.name.localeCompare(b.name));
+};
+
+/**
+ * Ensure at least one role has been specified.
+ */
+export function validateAssignmentScopeGroup(
+ this: IExecuteFunctions,
+ roles: RolesParameter,
+) {
+ if (!roles.roleProperties?.length) {
+ throw new NodeOperationError(
+ this.getNode(),
+ 'Please specify a role for the agent to create.',
+ );
+ }
+}
+
+export function sanitizeAssignmentScopeGroup(
+ this: IExecuteFunctions,
+ roles: RolesParameter,
+) {
+ roles.roleProperties.forEach(roleProperty => {
+ if (roleProperty.assignment_scope === 'specified_groups' && !roleProperty?.groups?.length) {
+ throw new NodeOperationError(
+ this.getNode(),
+ 'Please specify a group for every role of the agent to create.',
+ );
+ }
+
+ // remove the `groups` param, only needed for scopes other than `specified_groups`
+ if (roleProperty.assignment_scope !== 'specified_groups' && roleProperty.groups) {
+ delete roleProperty.groups;
+ }
+ });
+}
+
+/**
+ * Adjust a roles fixed collection into the format expected by Freshservice API.
+ */
+export function adjustAgentRoles(roles: RolesParameter) {
+ return {
+ roles: roles.roleProperties.map(({ role, assignment_scope, groups }) => {
+ return {
+ role_id: role,
+ assignment_scope,
+ groups,
+ };
+ }),
+ };
+}
+
+export function formatFilters(filters: IDataObject) {
+ const query = Object.keys(filters).map(key => {
+ const value = filters[key];
+
+ if (!isNaN(Number(value))) {
+ return `${key}:${filters[key]}`; // number
+ }
+
+ if (typeof value === 'string' && value.endsWith('Z')) {
+ return `${key}:'${value.split('T')[0]}'`; // date
+ }
+
+ return `${key}:'${filters[key]}'`; // string
+ }).join(' AND ');
+
+ return {
+ query: `"${query}"`,
+ };
+}
+
+export function validateUpdateFields(
+ this: IExecuteFunctions,
+ updateFields: IDataObject,
+ resource: string,
+) {
+ if (!Object.keys(updateFields).length) {
+ const twoWordResources: { [key: string]: string } = {
+ agentGroup: 'agent group',
+ agentRole: 'agent role',
+ assetType: 'asset type',
+ requesterGroup: 'requester group',
+ };
+
+ throw new NodeOperationError(
+ this.getNode(),
+ `Please enter at least one field to update for the ${twoWordResources[resource] ?? resource}.`,
+ );
+ }
+}
+
+export const toArray = (str: string) => str.split(',').map(e => e.trim());
+
+export function adjustAddress(fixedCollection: IDataObject & AddressFixedCollection) {
+ if (!fixedCollection.address) return fixedCollection;
+
+ const adjusted = omit(fixedCollection, ['address']);
+ adjusted.address = fixedCollection.address.addressFields;
+
+ return adjusted;
+}
diff --git a/packages/nodes-base/nodes/Freshservice/constants.ts b/packages/nodes-base/nodes/Freshservice/constants.ts
new file mode 100644
index 0000000000..4223a31eb7
--- /dev/null
+++ b/packages/nodes-base/nodes/Freshservice/constants.ts
@@ -0,0 +1,44 @@
+// https://support.freshservice.com/support/solutions/articles/232303-list-of-languages-supported-in-freshservice
+
+const RAW_LANGUAGES: { [key: string]: string } = {
+ en: 'English',
+ ar: 'Arabic',
+ ca: 'Catalan',
+ cs: 'Czech',
+ 'cy-GB': 'Welsh',
+ da: 'Danish',
+ de: 'German',
+ es: 'Spanish',
+ 'es-LA': 'Spanish (Latin America)',
+ et: 'Estonian',
+ fi: 'Finnish',
+ fr: 'French',
+ he: 'Hebrew',
+ hu: 'Hungarian',
+ id: 'Indonesian',
+ it: 'Italian',
+ 'ja-JP': 'Japanese',
+ ko: 'Korean',
+ LV: 'Latvian',
+ 'nb-NO': 'Norwegian',
+ nl: 'Dutch',
+ pl: 'Polish',
+ pt: 'Portuguese',
+ 'pt-BR': 'Portuguese (Brazil)',
+ 'pt-PT': 'Portuguese (Portugal)',
+ 'ru-RU': 'Russian',
+ sk: 'Slovak',
+ 'sk-SK': 'Slovak',
+ sl: 'Slovenian',
+ 'sv-SE': 'Swedish',
+ th: 'Thai',
+ tr: 'Turkish',
+ UK: 'Ukrainian',
+ vi: 'Vietnamese',
+ 'zh-CN': 'Chinese (Simplified)',
+ 'zh-TW': 'Chinese (Traditional)',
+};
+
+export const LANGUAGES = Object.keys(RAW_LANGUAGES).map((key) => {
+ return ({ value: key, name: RAW_LANGUAGES[key] });
+});
diff --git a/packages/nodes-base/nodes/Freshservice/descriptions/AgentDescription.ts b/packages/nodes-base/nodes/Freshservice/descriptions/AgentDescription.ts
new file mode 100644
index 0000000000..17ea178ffd
--- /dev/null
+++ b/packages/nodes-base/nodes/Freshservice/descriptions/AgentDescription.ts
@@ -0,0 +1,697 @@
+import {
+ INodeProperties,
+} from 'n8n-workflow';
+
+import {
+ LANGUAGES,
+} from '../constants';
+
+export const agentOperations = [
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ displayOptions: {
+ show: {
+ resource: [
+ 'agent',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Create',
+ value: 'create',
+ description: 'Create an agent',
+ },
+ {
+ name: 'Delete',
+ value: 'delete',
+ description: 'Delete an agent',
+ },
+ {
+ name: 'Get',
+ value: 'get',
+ description: 'Retrieve an agent',
+ },
+ {
+ name: 'Get All',
+ value: 'getAll',
+ description: 'Retrieve all agents',
+ },
+ {
+ name: 'Update',
+ value: 'update',
+ description: 'Update an agent',
+ },
+ ],
+ default: 'create',
+ },
+] as INodeProperties[];
+
+export const agentFields = [
+ // ----------------------------------------
+ // agent: create
+ // ----------------------------------------
+ {
+ displayName: 'Email',
+ name: 'email',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'agent',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'First Name',
+ name: 'firstName',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'agent',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Roles',
+ name: 'roles',
+ description: 'Role to assign to the agent',
+ type: 'fixedCollection',
+ placeholder: 'Add Role',
+ typeOptions: {
+ multipleValues: true,
+ },
+ required: true,
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'agent',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Role Properties',
+ name: 'roleProperties',
+ values: [
+ {
+ displayName: 'Role Name/ID',
+ name: 'role',
+ description: 'Name of the role to assign to the agent. Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ type: 'options',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getAgentRoles',
+ ],
+ },
+ required: true,
+ default: '',
+ },
+ {
+ displayName: 'Scope',
+ name: 'assignment_scope',
+ description: 'Scope in which the agent may use the permissions granted by the role',
+ type: 'options',
+ options: [
+ {
+ name: 'Entire Helpdesk',
+ value: 'entire_helpdesk',
+ },
+ {
+ name: 'Member Groups',
+ value: 'member_groups',
+ },
+ {
+ name: 'Specified Groups',
+ value: 'specified_groups',
+ },
+ {
+ name: 'Assigned Items',
+ value: 'assigned_items',
+ },
+ ],
+ required: true,
+ default: 'specified_groups',
+ },
+ {
+ displayName: 'Group Names/IDs',
+ name: 'groups',
+ description: 'Groups in which the permissions granted by the role apply. Required only when Scope is Specified Groups - ignored otherwise. Choose from the list or specify an ID using an expression.',
+ type: 'multiOptions',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getAgentGroups',
+ ],
+ },
+ default: [],
+ },
+ ],
+ },
+ ],
+ },
+ {
+ displayName: 'Additional Fields',
+ name: 'additionalFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'agent',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Address',
+ name: 'address',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Background Information',
+ name: 'background_information',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Department Names/IDs',
+ name: 'department_ids',
+ type: 'multiOptions',
+ default: [],
+ description: 'IDs of the departments to which the agent belongs. Choose from the list or specify an ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getDepartments',
+ ],
+ },
+ },
+ {
+ displayName: 'Job Title',
+ name: 'job_title',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Language',
+ name: 'language',
+ type: 'options',
+ default: '',
+ options: LANGUAGES,
+ },
+ {
+ displayName: 'Last Name',
+ name: 'last_name',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Location Name/ID',
+ name: 'location_id',
+ type: 'options',
+ default: '',
+ description: 'Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getLocations',
+ ],
+ },
+ },
+ {
+ displayName: 'Member of Group Names/IDs',
+ name: 'member_of',
+ type: 'multiOptions',
+ default: [],
+ description: 'Comma-separated IDs of the groups that the agent is a member of. Choose from the list or specify an ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getAgentGroups',
+ ],
+ },
+ },
+ {
+ displayName: 'Mobile Phone',
+ name: 'mobile_phone_number',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Observer of Group Names/IDs',
+ name: 'observer_of',
+ type: 'multiOptions',
+ default: [],
+ description: 'Comma-separated IDs of the groups that the agent is an observer of. Choose from the list or specify an ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getAgentGroups',
+ ],
+ },
+ },
+ {
+ displayName: 'Scoreboard Level ID',
+ name: 'scoreboard_level_id',
+ type: 'options',
+ description: 'ID of the level of the agent in the Arcade',
+ default: 1,
+ options: [
+ {
+ name: 'Beginner',
+ value: 1,
+ },
+ {
+ name: 'Intermediate',
+ value: 2,
+ },
+ {
+ name: 'Professional',
+ value: 3,
+ },
+ {
+ name: 'Expert',
+ value: 4,
+ },
+ {
+ name: 'Master',
+ value: 5,
+ },
+ {
+ name: 'Guru',
+ value: 6,
+ },
+ ],
+ },
+ {
+ displayName: 'Time Format',
+ name: 'time_format',
+ type: 'options',
+ default: '12h',
+ options: [
+ {
+ name: '12-Hour Format',
+ value: '12h',
+ },
+ {
+ name: '24-Hour Format',
+ value: '24h',
+ },
+ ],
+ },
+ {
+ displayName: 'Work Phone',
+ name: 'work_phone_number',
+ type: 'string',
+ default: '',
+ },
+ ],
+ },
+
+ // ----------------------------------------
+ // agent: delete
+ // ----------------------------------------
+ {
+ displayName: 'Agent ID',
+ name: 'agentId',
+ description: 'ID of the agent to delete',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'agent',
+ ],
+ operation: [
+ 'delete',
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // agent: get
+ // ----------------------------------------
+ {
+ displayName: 'Agent ID',
+ name: 'agentId',
+ description: 'ID of the agent to retrieve',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'agent',
+ ],
+ operation: [
+ 'get',
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // agent: getAll
+ // ----------------------------------------
+ {
+ displayName: 'Return All',
+ name: 'returnAll',
+ type: 'boolean',
+ default: false,
+ description: 'Whether to return all results or only up to a given limit',
+ displayOptions: {
+ show: {
+ resource: [
+ 'agent',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Limit',
+ name: 'limit',
+ type: 'number',
+ default: 50,
+ description: 'How many results to return',
+ typeOptions: {
+ minValue: 1,
+ },
+ displayOptions: {
+ show: {
+ resource: [
+ 'agent',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ returnAll: [
+ false,
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Filters',
+ name: 'filters',
+ type: 'collection',
+ placeholder: 'Add Filter',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'agent',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Department Name/ID',
+ name: 'department_id',
+ type: 'options',
+ default: '',
+ description: 'ID of the department to which the agent belongs. Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getDepartments',
+ ],
+ },
+ },
+ {
+ displayName: 'Email',
+ name: 'email',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'First Name',
+ name: 'first_name',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Job Title',
+ name: 'job_title',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Language',
+ name: 'language',
+ type: 'options',
+ default: '',
+ options: LANGUAGES,
+ },
+ {
+ displayName: 'Last Name',
+ name: 'last_name',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Location Name/ID',
+ name: 'location_id',
+ type: 'options',
+ default: '',
+ description: 'Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getLocations',
+ ],
+ },
+ },
+ {
+ displayName: 'Mobile Phone Number',
+ name: 'mobile_phone_number',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Work Phone Number',
+ name: 'work_phone_number',
+ type: 'string',
+ default: '',
+ },
+ ],
+ },
+
+ // ----------------------------------------
+ // agent: update
+ // ----------------------------------------
+ {
+ displayName: 'Agent ID',
+ name: 'agentId',
+ description: 'ID of the agent to update',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'agent',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Update Fields',
+ name: 'updateFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'agent',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Address',
+ name: 'address',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Background Information',
+ name: 'background_information',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Department Names/IDs',
+ name: 'department_ids',
+ type: 'multiOptions',
+ default: [],
+ description: 'IDs of the departments to which the agent belongs. Choose from the list or specify an ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getDepartments',
+ ],
+ },
+ },
+ {
+ displayName: 'Email',
+ name: 'email',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'First Name',
+ name: 'first_name',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Job Title',
+ name: 'job_title',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Language',
+ name: 'language',
+ type: 'options',
+ default: '',
+ options: LANGUAGES,
+ },
+ {
+ displayName: 'Last Name',
+ name: 'last_name',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Location Name/ID',
+ name: 'location_id',
+ type: 'options',
+ default: '',
+ description: 'Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getLocations',
+ ],
+ },
+ },
+ {
+ displayName: 'Member of Group Names/IDs',
+ name: 'member_of',
+ type: 'multiOptions',
+ default: [],
+ description: 'Comma-separated IDs of the groups that the agent is a member of. Choose from the list or specify an ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getAgentGroups',
+ ],
+ },
+ },
+ {
+ displayName: 'Mobile Phone',
+ name: 'mobile_phone_number',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Observer of Group Names/IDs',
+ name: 'observer_of',
+ type: 'multiOptions',
+ default: [],
+ description: 'Comma-separated IDs of the groups that the agent is an observer of. Choose from the list or specify an ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getAgentGroups',
+ ],
+ },
+ },
+ {
+ displayName: 'Scoreboard Level ID',
+ name: 'scoreboard_level_id',
+ type: 'options',
+ description: 'ID of the level of the agent in the Arcade',
+ default: 1,
+ options: [
+ {
+ name: 'Beginner',
+ value: 1,
+ },
+ {
+ name: 'Intermediate',
+ value: 2,
+ },
+ {
+ name: 'Professional',
+ value: 3,
+ },
+ {
+ name: 'Expert',
+ value: 4,
+ },
+ {
+ name: 'Master',
+ value: 5,
+ },
+ {
+ name: 'Guru',
+ value: 6,
+ },
+ ],
+ },
+ {
+ displayName: 'Time Format',
+ name: 'time_format',
+ type: 'options',
+ default: '12h',
+ options: [
+ {
+ name: '12-Hour Format',
+ value: '12h',
+ },
+ {
+ name: '24-Hour Format',
+ value: '24h',
+ },
+ ],
+ },
+ {
+ displayName: 'Work Phone',
+ name: 'work_phone_number',
+ type: 'string',
+ default: '',
+ },
+ ],
+ },
+] as INodeProperties[];
diff --git a/packages/nodes-base/nodes/Freshservice/descriptions/AgentGroupDescription.ts b/packages/nodes-base/nodes/Freshservice/descriptions/AgentGroupDescription.ts
new file mode 100644
index 0000000000..fe7616e76e
--- /dev/null
+++ b/packages/nodes-base/nodes/Freshservice/descriptions/AgentGroupDescription.ts
@@ -0,0 +1,389 @@
+import {
+ INodeProperties,
+} from 'n8n-workflow';
+
+export const agentGroupOperations = [
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ displayOptions: {
+ show: {
+ resource: [
+ 'agentGroup',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Create',
+ value: 'create',
+ description: 'Create an agent group',
+ },
+ {
+ name: 'Delete',
+ value: 'delete',
+ description: 'Delete an agent group',
+ },
+ {
+ name: 'Get',
+ value: 'get',
+ description: 'Retrieve an agent group',
+ },
+ {
+ name: 'Get All',
+ value: 'getAll',
+ description: 'Retrieve all agent groups',
+ },
+ {
+ name: 'Update',
+ value: 'update',
+ description: 'Update an agent group',
+ },
+ ],
+ default: 'create',
+ },
+] as INodeProperties[];
+
+export const agentGroupFields = [
+ // ----------------------------------------
+ // agentGroup: create
+ // ----------------------------------------
+ {
+ displayName: 'Name',
+ name: 'name',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'agentGroup',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Additional Fields',
+ name: 'additionalFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'agentGroup',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Description',
+ name: 'description',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Escalate to Agent Name/ID',
+ name: 'escalate_to',
+ type: 'options',
+ default: '',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getAgents',
+ ],
+ },
+ description: 'ID of the user to whom an escalation email is sent if a ticket in this group is unassigned. Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ },
+ {
+ displayName: 'Member Names/IDs',
+ name: 'members',
+ type: 'multiOptions',
+ default: [],
+ description: 'Comma-separated IDs of agents who are members of this group. Choose from the list or specify an ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getAgents',
+ ],
+ },
+ },
+ {
+ displayName: 'Observer Names/IDs',
+ name: 'observers',
+ type: 'multiOptions',
+ default: [],
+ description: 'Comma-separated agent IDs who are observers of this group. Choose from the list or specify an ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getAgents',
+ ],
+ },
+ },
+ {
+ displayName: 'Unassigned For',
+ name: 'unassigned_for',
+ description: 'Time after which an escalation email is sent if a ticket in the group remains unassigned',
+ type: 'options',
+ default: '30m',
+ options: [
+ {
+ name: '30 Minutes',
+ value: '30m',
+ },
+ {
+ name: '1 Hour',
+ value: '1h',
+ },
+ {
+ name: '2 Hours',
+ value: '2h',
+ },
+ {
+ name: '8 Hours',
+ value: '8h',
+ },
+ {
+ name: '12 Hours',
+ value: '12h',
+ },
+ {
+ name: '1 Day',
+ value: '1d',
+ },
+ {
+ name: '2 Days',
+ value: '2d',
+ },
+ {
+ name: '3 Days',
+ value: '3d',
+ },
+ ],
+ },
+ ],
+ },
+
+ // ----------------------------------------
+ // agentGroup: delete
+ // ----------------------------------------
+ {
+ displayName: 'Agent Group ID',
+ name: 'agentGroupId',
+ description: 'ID of the agent group to delete',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'agentGroup',
+ ],
+ operation: [
+ 'delete',
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // agentGroup: get
+ // ----------------------------------------
+ {
+ displayName: 'Agent Group ID',
+ name: 'agentGroupId',
+ description: 'ID of the agent group to retrieve',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'agentGroup',
+ ],
+ operation: [
+ 'get',
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // agentGroup: getAll
+ // ----------------------------------------
+ {
+ displayName: 'Return All',
+ name: 'returnAll',
+ type: 'boolean',
+ default: false,
+ description: 'Whether to return all results or only up to a given limit',
+ displayOptions: {
+ show: {
+ resource: [
+ 'agentGroup',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Limit',
+ name: 'limit',
+ type: 'number',
+ default: 50,
+ description: 'How many results to return',
+ typeOptions: {
+ minValue: 1,
+ },
+ displayOptions: {
+ show: {
+ resource: [
+ 'agentGroup',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ returnAll: [
+ false,
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // agentGroup: update
+ // ----------------------------------------
+ {
+ displayName: 'Agent Group ID',
+ name: 'agentGroupId',
+ description: 'ID of the agent group to update',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'agentGroup',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Update Fields',
+ name: 'updateFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'agentGroup',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Description',
+ name: 'description',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Escalate to Agent Names/IDs',
+ name: 'escalate_to',
+ type: 'options',
+ default: '',
+ description: 'ID of the agent to whom an escalation email is sent if a ticket in this group is unassigned. Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getAgents',
+ ],
+ },
+ },
+ {
+ displayName: 'Member Names/IDs',
+ name: 'members',
+ type: 'multiOptions',
+ default: [],
+ description: 'Comma-separated IDs of agents who are members of this group. Choose from the list or specify an ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getAgents',
+ ],
+ },
+ },
+ {
+ displayName: 'Name',
+ name: 'name',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Observer Names/IDs',
+ name: 'observers',
+ type: 'multiOptions',
+ default: [],
+ description: 'Comma-separated agent user IDs who are observers of this group. Choose from the list or specify an ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getAgents',
+ ],
+ },
+ },
+ {
+ displayName: 'Unassigned For',
+ name: 'unassigned_for',
+ description: 'Time after which an escalation email is sent if a ticket in the group remains unassigned',
+ type: 'options',
+ default: '30m',
+ options: [
+ {
+ name: '30 Minutes',
+ value: '30m',
+ },
+ {
+ name: '1 Hour',
+ value: '1h',
+ },
+ {
+ name: '2 Hours',
+ value: '2h',
+ },
+ {
+ name: '8 Hours',
+ value: '8h',
+ },
+ {
+ name: '12 Hours',
+ value: '12h',
+ },
+ {
+ name: '1 Day',
+ value: '1d',
+ },
+ {
+ name: '2 Days',
+ value: '2d',
+ },
+ {
+ name: '3 Days',
+ value: '3d',
+ },
+ ],
+ },
+ ],
+ },
+] as INodeProperties[];
diff --git a/packages/nodes-base/nodes/Freshservice/descriptions/AgentRoleDescription.ts b/packages/nodes-base/nodes/Freshservice/descriptions/AgentRoleDescription.ts
new file mode 100644
index 0000000000..8595fb9926
--- /dev/null
+++ b/packages/nodes-base/nodes/Freshservice/descriptions/AgentRoleDescription.ts
@@ -0,0 +1,99 @@
+import {
+ INodeProperties,
+} from 'n8n-workflow';
+
+export const agentRoleOperations = [
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ displayOptions: {
+ show: {
+ resource: [
+ 'agentRole',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Get',
+ value: 'get',
+ description: 'Retrieve an agent role',
+ },
+ {
+ name: 'Get All',
+ value: 'getAll',
+ description: 'Retrieve all agent roles',
+ },
+ ],
+ default: 'get',
+ },
+] as INodeProperties[];
+
+export const agentRoleFields = [
+ // ----------------------------------------
+ // agentRole: get
+ // ----------------------------------------
+ {
+ displayName: 'Agent Role ID',
+ name: 'agentRoleId',
+ description: 'ID of the agent role to retrieve',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'agentRole',
+ ],
+ operation: [
+ 'get',
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // agentRole: getAll
+ // ----------------------------------------
+ {
+ displayName: 'Return All',
+ name: 'returnAll',
+ type: 'boolean',
+ default: false,
+ description: 'Whether to return all results or only up to a given limit',
+ displayOptions: {
+ show: {
+ resource: [
+ 'agentRole',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Limit',
+ name: 'limit',
+ type: 'number',
+ default: 50,
+ description: 'How many results to return',
+ typeOptions: {
+ minValue: 1,
+ },
+ displayOptions: {
+ show: {
+ resource: [
+ 'agentRole',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ returnAll: [
+ false,
+ ],
+ },
+ },
+ },
+] as INodeProperties[];
diff --git a/packages/nodes-base/nodes/Freshservice/descriptions/AnnouncementDescription.ts b/packages/nodes-base/nodes/Freshservice/descriptions/AnnouncementDescription.ts
new file mode 100644
index 0000000000..73c5e433f1
--- /dev/null
+++ b/packages/nodes-base/nodes/Freshservice/descriptions/AnnouncementDescription.ts
@@ -0,0 +1,369 @@
+import {
+ INodeProperties,
+} from 'n8n-workflow';
+
+export const announcementOperations = [
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ displayOptions: {
+ show: {
+ resource: [
+ 'announcement',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Create',
+ value: 'create',
+ description: 'Create an announcement',
+ },
+ {
+ name: 'Delete',
+ value: 'delete',
+ description: 'Delete an announcement',
+ },
+ {
+ name: 'Get',
+ value: 'get',
+ description: 'Retrieve an announcement',
+ },
+ {
+ name: 'Get All',
+ value: 'getAll',
+ description: 'Retrieve all announcements',
+ },
+ {
+ name: 'Update',
+ value: 'update',
+ description: 'Update an announcement',
+ },
+ ],
+ default: 'create',
+ },
+] as INodeProperties[];
+
+export const announcementFields = [
+ // ----------------------------------------
+ // announcement: create
+ // ----------------------------------------
+ {
+ displayName: 'Title',
+ name: 'title',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'announcement',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Body',
+ name: 'bodyHtml',
+ description: 'HTML supported',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'announcement',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Visibility',
+ name: 'visibility',
+ type: 'options',
+ required: true,
+ default: 'everyone',
+ options: [
+ {
+ name: 'Agents Only',
+ value: 'agents_only',
+ },
+ {
+ name: 'Agents and Groups',
+ value: 'grouped_visibility',
+ },
+ {
+ name: 'Everyone',
+ value: 'everyone',
+ },
+ ],
+ displayOptions: {
+ show: {
+ resource: [
+ 'announcement',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Visible From',
+ name: 'visibleFrom',
+ description: 'Timestamp at which announcement becomes active',
+ type: 'dateTime',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'announcement',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Additional Fields',
+ name: 'additionalFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'announcement',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Additional Emails',
+ name: 'additional_emails',
+ type: 'string',
+ default: '',
+ description: 'Comma-separated additional email addresses to which the announcement needs to be sent',
+ },
+ {
+ displayName: 'Department Names/IDs',
+ name: 'departments',
+ type: 'multiOptions',
+ default: [],
+ description: 'Comma-separated IDs of departments that may view this announcement. Choose from the list or specify an ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getDepartments',
+ ],
+ },
+ },
+ {
+ displayName: 'Visible From',
+ name: 'visible_from',
+ description: 'Timestamp at which announcement is active',
+ type: 'dateTime',
+ default: '',
+ },
+ {
+ displayName: 'Visible Until',
+ name: 'visible_till',
+ description: 'Timestamp until which announcement is active',
+ type: 'dateTime',
+ default: '',
+ },
+ ],
+ },
+
+ // ----------------------------------------
+ // announcement: delete
+ // ----------------------------------------
+ {
+ displayName: 'Announcement ID',
+ name: 'announcementId',
+ description: 'ID of the announcement to delete',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'announcement',
+ ],
+ operation: [
+ 'delete',
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // announcement: get
+ // ----------------------------------------
+ {
+ displayName: 'Announcement ID',
+ name: 'announcementId',
+ description: 'ID of the announcement to retrieve',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'announcement',
+ ],
+ operation: [
+ 'get',
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // announcement: getAll
+ // ----------------------------------------
+ {
+ displayName: 'Return All',
+ name: 'returnAll',
+ type: 'boolean',
+ default: false,
+ description: 'Whether to return all results or only up to a given limit',
+ displayOptions: {
+ show: {
+ resource: [
+ 'announcement',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Limit',
+ name: 'limit',
+ type: 'number',
+ default: 50,
+ description: 'How many results to return',
+ typeOptions: {
+ minValue: 1,
+ },
+ displayOptions: {
+ show: {
+ resource: [
+ 'announcement',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ returnAll: [
+ false,
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // announcement: update
+ // ----------------------------------------
+ {
+ displayName: 'Announcement ID',
+ name: 'announcementId',
+ description: 'ID of the announcement to update',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'announcement',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Update Fields',
+ name: 'updateFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'announcement',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Additional Emails',
+ name: 'additional_emails',
+ type: 'string',
+ default: '',
+ description: 'Comma-separated additional email addresses to which the announcement needs to be sent',
+ },
+ {
+ displayName: 'Body',
+ name: 'body_html',
+ type: 'string',
+ default: '',
+ description: 'HTML supported',
+ },
+ {
+ displayName: 'Department Names/IDs',
+ name: 'departments',
+ type: 'multiOptions',
+ default: [],
+ description: 'Comma-separated IDs of departments that may view this announcement. Choose from the list or specify an ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getDepartments',
+ ],
+ },
+ },
+ {
+ displayName: 'Title',
+ name: 'title',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Visibility',
+ name: 'visibility',
+ type: 'options',
+ default: 'everyone',
+ options: [
+ {
+ name: 'Agents Only',
+ value: 'agents_only',
+ },
+ {
+ name: 'Agents and Groups',
+ value: 'grouped_visibility',
+ },
+ {
+ name: 'Everyone',
+ value: 'everyone',
+ },
+ ],
+ },
+ ],
+ },
+] as INodeProperties[];
diff --git a/packages/nodes-base/nodes/Freshservice/descriptions/AssetDescription.ts b/packages/nodes-base/nodes/Freshservice/descriptions/AssetDescription.ts
new file mode 100644
index 0000000000..9d815a793b
--- /dev/null
+++ b/packages/nodes-base/nodes/Freshservice/descriptions/AssetDescription.ts
@@ -0,0 +1,378 @@
+import {
+ INodeProperties,
+} from 'n8n-workflow';
+
+export const assetOperations = [
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ displayOptions: {
+ show: {
+ resource: [
+ 'asset',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Create',
+ value: 'create',
+ description: 'Create an asset',
+ },
+ {
+ name: 'Delete',
+ value: 'delete',
+ description: 'Delete an asset',
+ },
+ {
+ name: 'Get',
+ value: 'get',
+ description: 'Retrieve an asset',
+ },
+ {
+ name: 'Get All',
+ value: 'getAll',
+ description: 'Retrieve all assets',
+ },
+ {
+ name: 'Update',
+ value: 'update',
+ description: 'Update an asset',
+ },
+ ],
+ default: 'create',
+ },
+] as INodeProperties[];
+
+export const assetFields = [
+ // ----------------------------------------
+ // asset: create
+ // ----------------------------------------
+ {
+ displayName: 'Asset Name',
+ name: 'name',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'asset',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Asset Type Name/ID',
+ name: 'assetTypeId',
+ type: 'options',
+ description: 'Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ required: true,
+ default: '',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getAssetTypes',
+ ],
+ },
+ displayOptions: {
+ show: {
+ resource: [
+ 'asset',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Asset Fields',
+ name: 'assetFieldsUi',
+ placeholder: 'Add Asset Field',
+ type: 'fixedCollection',
+ typeOptions: {
+ multipleValues: true,
+ },
+ displayOptions: {
+ show: {
+ resource: [
+ 'asset',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ default: {},
+ options: [
+ {
+ name: 'assetFieldValue',
+ displayName: 'Asset Field',
+ values: [
+ {
+ displayName: 'Name/ID',
+ name: 'name',
+ type: 'options',
+ typeOptions: {
+ loadOptionsDependsOn: [
+ 'assetTypeId',
+ ],
+ loadOptionsMethod: 'getAssetTypeFields',
+ },
+ default: '',
+ description: 'The ID of the field to add custom field to.',
+ },
+ {
+ displayName: 'Value',
+ name: 'value',
+ type: 'string',
+ default: '',
+ description: 'The value to set on custom field.',
+ },
+ ],
+ },
+ ],
+ },
+ // ----------------------------------------
+ // asset: delete
+ // ----------------------------------------
+ {
+ displayName: 'Asset Display ID',
+ name: 'assetDisplayId',
+ description: 'Display ID of the asset to delete. Do not confuse with asset ID.',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'asset',
+ ],
+ operation: [
+ 'delete',
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // asset: get
+ // ----------------------------------------
+ {
+ displayName: 'Asset Display ID',
+ name: 'assetDisplayId',
+ description: 'Display ID of the asset to retrieve. Do not confuse with asset ID.',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'asset',
+ ],
+ operation: [
+ 'get',
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // asset: getAll
+ // ----------------------------------------
+ {
+ displayName: 'Return All',
+ name: 'returnAll',
+ type: 'boolean',
+ default: false,
+ description: 'Whether to return all results or only up to a given limit',
+ displayOptions: {
+ show: {
+ resource: [
+ 'asset',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Limit',
+ name: 'limit',
+ type: 'number',
+ default: 50,
+ description: 'How many results to return',
+ typeOptions: {
+ minValue: 1,
+ },
+ displayOptions: {
+ show: {
+ resource: [
+ 'asset',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ returnAll: [
+ false,
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Filters',
+ name: 'filters',
+ type: 'collection',
+ placeholder: 'Add Filter',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'asset',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Agent Name/ID',
+ name: 'agent_id',
+ type: 'options',
+ default: '',
+ description: 'ID of the agent by whom the asset is managed. Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getAgents',
+ ],
+ },
+ },
+ {
+ displayName: 'Asset State',
+ name: 'asset_state',
+ type: 'string',
+ default: '',
+ description: 'Status of the asset to filter by. For example, "In use".',
+ },
+ {
+ displayName: 'Asset Type Name/ID',
+ name: 'asset_type_id',
+ type: 'options',
+ default: '',
+ description: 'ID of the asset type to filter by. Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getAssetTypes',
+ ],
+ },
+ },
+ {
+ displayName: 'Department Name/ID',
+ name: 'department_id',
+ type: 'options',
+ default: '',
+ description: 'ID of the department to which the asset belongs. Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getDepartments',
+ ],
+ },
+ },
+ {
+ displayName: 'Location Name/ID',
+ name: 'location_id',
+ type: 'options',
+ default: '',
+ description: 'ID of the location to filter by. Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getLocations',
+ ],
+ },
+ },
+ {
+ displayName: 'Name',
+ name: 'name',
+ type: 'string',
+ default: '',
+ description: 'Display name of the asset to filter by',
+ },
+ ],
+ },
+
+ // ----------------------------------------
+ // asset: update
+ // ----------------------------------------
+ {
+ displayName: 'Asset Display ID',
+ name: 'assetDisplayId',
+ description: 'Display ID of the asset to update. Do not confuse with asset ID.',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'asset',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Asset Fields',
+ name: 'assetFieldsUi',
+ placeholder: 'Add Asset Field',
+ type: 'fixedCollection',
+ typeOptions: {
+ multipleValues: true,
+ },
+ displayOptions: {
+ show: {
+ resource: [
+ 'asset',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ default: {},
+ options: [
+ {
+ name: 'assetFieldValue',
+ displayName: 'Asset Field',
+ values: [
+ {
+ displayName: 'Name/ID',
+ name: 'name',
+ type: 'options',
+ typeOptions: {
+ loadOptionsDependsOn: [
+ 'assetTypeId',
+ ],
+ loadOptionsMethod: 'getAssetTypeFields',
+ },
+ default: '',
+ description: 'The ID of the field to add custom field to.',
+ },
+ {
+ displayName: 'Value',
+ name: 'value',
+ type: 'string',
+ default: '',
+ description: 'The value to set on custom field.',
+ },
+ ],
+ },
+ ],
+ },
+] as INodeProperties[];
diff --git a/packages/nodes-base/nodes/Freshservice/descriptions/AssetTypeDescription.ts b/packages/nodes-base/nodes/Freshservice/descriptions/AssetTypeDescription.ts
new file mode 100644
index 0000000000..7c4af2bc7a
--- /dev/null
+++ b/packages/nodes-base/nodes/Freshservice/descriptions/AssetTypeDescription.ts
@@ -0,0 +1,247 @@
+import {
+ INodeProperties,
+} from 'n8n-workflow';
+
+export const assetTypeOperations = [
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ displayOptions: {
+ show: {
+ resource: [
+ 'assetType',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Create',
+ value: 'create',
+ description: 'Create an asset type',
+ },
+ {
+ name: 'Delete',
+ value: 'delete',
+ description: 'Delete an asset type',
+ },
+ {
+ name: 'Get',
+ value: 'get',
+ description: 'Retrieve an asset type',
+ },
+ {
+ name: 'Get All',
+ value: 'getAll',
+ description: 'Retrieve all asset types',
+ },
+ {
+ name: 'Update',
+ value: 'update',
+ description: 'Update an asset type',
+ },
+ ],
+ default: 'create',
+ },
+] as INodeProperties[];
+
+export const assetTypeFields = [
+ // ----------------------------------------
+ // assetType: create
+ // ----------------------------------------
+ {
+ displayName: 'Name',
+ name: 'name',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'assetType',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Additional Fields',
+ name: 'additionalFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'assetType',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Description',
+ name: 'description',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Parent Asset Type Name/ID',
+ name: 'parent_asset_type_id',
+ description: 'Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ type: 'options',
+ default: '',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getAssetTypes',
+ ],
+ },
+ },
+ ],
+ },
+
+ // ----------------------------------------
+ // assetType: delete
+ // ----------------------------------------
+ {
+ displayName: 'Asset Type ID',
+ name: 'assetTypeId',
+ description: 'ID of the asset type to delete',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'assetType',
+ ],
+ operation: [
+ 'delete',
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // assetType: get
+ // ----------------------------------------
+ {
+ displayName: 'Asset Type ID',
+ name: 'assetTypeId',
+ description: 'ID of the asset type to retrieve',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'assetType',
+ ],
+ operation: [
+ 'get',
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // assetType: getAll
+ // ----------------------------------------
+ {
+ displayName: 'Return All',
+ name: 'returnAll',
+ type: 'boolean',
+ default: false,
+ description: 'Whether to return all results or only up to a given limit',
+ displayOptions: {
+ show: {
+ resource: [
+ 'assetType',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Limit',
+ name: 'limit',
+ type: 'number',
+ default: 50,
+ description: 'How many results to return',
+ typeOptions: {
+ minValue: 1,
+ },
+ displayOptions: {
+ show: {
+ resource: [
+ 'assetType',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ returnAll: [
+ false,
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // assetType: update
+ // ----------------------------------------
+ {
+ displayName: 'Asset Type ID',
+ name: 'assetTypeId',
+ description: 'ID of the asset type to update',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'assetType',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Update Fields',
+ name: 'updateFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'assetType',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Description',
+ name: 'description',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Name',
+ name: 'name',
+ type: 'string',
+ default: '',
+ },
+ ],
+ },
+] as INodeProperties[];
diff --git a/packages/nodes-base/nodes/Freshservice/descriptions/ChangeDescription.ts b/packages/nodes-base/nodes/Freshservice/descriptions/ChangeDescription.ts
new file mode 100644
index 0000000000..c447678b42
--- /dev/null
+++ b/packages/nodes-base/nodes/Freshservice/descriptions/ChangeDescription.ts
@@ -0,0 +1,702 @@
+import {
+ INodeProperties,
+} from 'n8n-workflow';
+
+export const changeOperations = [
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ displayOptions: {
+ show: {
+ resource: [
+ 'change',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Create',
+ value: 'create',
+ description: 'Create a change',
+ },
+ {
+ name: 'Delete',
+ value: 'delete',
+ description: 'Delete a change',
+ },
+ {
+ name: 'Get',
+ value: 'get',
+ description: 'Retrieve a change',
+ },
+ {
+ name: 'Get All',
+ value: 'getAll',
+ description: 'Retrieve all changes',
+ },
+ {
+ name: 'Update',
+ value: 'update',
+ description: 'Update a change',
+ },
+ ],
+ default: 'create',
+ },
+] as INodeProperties[];
+
+export const changeFields = [
+ // ----------------------------------------
+ // change: create
+ // ----------------------------------------
+ {
+ displayName: 'Requester Name/ID',
+ name: 'requesterId',
+ description: 'ID of the requester of the change. Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ type: 'options',
+ required: true,
+ default: '',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getRequesters',
+ ],
+ },
+ displayOptions: {
+ show: {
+ resource: [
+ 'change',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Subject',
+ name: 'subject',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'change',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Planned Start Date',
+ name: 'plannedStartDate',
+ type: 'dateTime',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'change',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Planned End Date',
+ name: 'plannedEndDate',
+ type: 'dateTime',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'change',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Additional Fields',
+ name: 'additionalFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'change',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Agent Name/ID',
+ name: 'agent_id',
+ type: 'options',
+ default: '',
+ description: 'ID of the agent to whom the change is assigned. Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getAgents',
+ ],
+ },
+ },
+ {
+ displayName: 'Change Type',
+ name: 'change_type',
+ type: 'options',
+ default: 1,
+ options: [
+ {
+ name: 'Minor',
+ value: 1,
+ },
+ {
+ name: 'Standard',
+ value: 2,
+ },
+ {
+ name: 'Major',
+ value: 3,
+ },
+ {
+ name: 'Emergency',
+ value: 4,
+ },
+ ],
+ },
+ {
+ displayName: 'Department Name/ID',
+ name: 'department_id',
+ type: 'options',
+ default: '',
+ description: 'ID of the department requesting the change. Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getDepartments',
+ ],
+ },
+ },
+ {
+ displayName: 'Description',
+ name: 'description',
+ type: 'string',
+ default: '',
+ description: 'HTML supported',
+ },
+ {
+ displayName: 'Group Name/ID',
+ name: 'group_id',
+ type: 'options',
+ default: '',
+ description: 'ID of the agent group to which the change is assigned. Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getAgentGroups',
+ ],
+ },
+ },
+ {
+ displayName: 'Impact',
+ name: 'impact',
+ type: 'options',
+ default: 1,
+ options: [
+ {
+ name: 'Low',
+ value: 1,
+ },
+ {
+ name: 'Medium',
+ value: 2,
+ },
+ {
+ name: 'High',
+ value: 3,
+ },
+ ],
+ },
+ {
+ displayName: 'Priority',
+ name: 'priority',
+ type: 'options',
+ default: 1,
+ options: [
+ {
+ name: 'Low',
+ value: 1,
+ },
+ {
+ name: 'Medium',
+ value: 2,
+ },
+ {
+ name: 'High',
+ value: 3,
+ },
+ {
+ name: 'Urgent',
+ value: 4,
+ },
+ ],
+ },
+ {
+ displayName: 'Risk',
+ name: 'risk',
+ type: 'options',
+ default: 1,
+ options: [
+ {
+ name: 'Low',
+ value: 1,
+ },
+ {
+ name: 'Medium',
+ value: 2,
+ },
+ {
+ name: 'High',
+ value: 3,
+ },
+ {
+ name: 'Very High',
+ value: 4,
+ },
+ ],
+ },
+ {
+ displayName: 'Status',
+ name: 'status',
+ type: 'options',
+ default: 1,
+ options: [
+ {
+ name: 'Open',
+ value: 1,
+ },
+ {
+ name: 'Planning',
+ value: 2,
+ },
+ {
+ name: 'Approval',
+ value: 3,
+ },
+ {
+ name: 'Pending Release',
+ value: 4,
+ },
+ {
+ name: 'Pending Review',
+ value: 5,
+ },
+ {
+ name: 'Closed',
+ value: 6,
+ },
+ ],
+ },
+ {
+ displayName: 'Subject',
+ name: 'subject',
+ type: 'string',
+ default: '',
+ },
+ ],
+ },
+
+ // ----------------------------------------
+ // change: delete
+ // ----------------------------------------
+ {
+ displayName: 'Change ID',
+ name: 'changeId',
+ description: 'ID of the change to delete',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'change',
+ ],
+ operation: [
+ 'delete',
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // change: get
+ // ----------------------------------------
+ {
+ displayName: 'Change ID',
+ name: 'changeId',
+ description: 'ID of the change to retrieve',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'change',
+ ],
+ operation: [
+ 'get',
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // change: getAll
+ // ----------------------------------------
+ {
+ displayName: 'Return All',
+ name: 'returnAll',
+ type: 'boolean',
+ default: false,
+ description: 'Whether to return all results or only up to a given limit',
+ displayOptions: {
+ show: {
+ resource: [
+ 'change',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Limit',
+ name: 'limit',
+ type: 'number',
+ default: 50,
+ description: 'How many results to return',
+ typeOptions: {
+ minValue: 1,
+ },
+ displayOptions: {
+ show: {
+ resource: [
+ 'change',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ returnAll: [
+ false,
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Filters',
+ name: 'filters',
+ type: 'collection',
+ placeholder: 'Add Filter',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'change',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Predefined Filters',
+ name: 'filter',
+ type: 'options',
+ default: 'my_open',
+ options: [
+ {
+ name: 'Closed',
+ value: 'closed',
+ },
+ {
+ name: 'My Open',
+ value: 'my_open',
+ },
+ {
+ name: 'Release Requested',
+ value: 'release_requested',
+ },
+ {
+ name: 'Requester ID',
+ value: 'requester_id',
+ },
+ {
+ name: 'Unassigned',
+ value: 'unassigned',
+ },
+ ],
+ },
+ {
+ displayName: 'Sort Order',
+ name: 'sort_by',
+ type: 'options',
+ options: [
+ {
+ name: 'Ascending',
+ value: 'asc',
+ },
+ {
+ name: 'Descending',
+ value: 'desc',
+ },
+ ],
+ default: 'asc',
+ },
+ {
+ displayName: 'Updated Since',
+ name: 'updated_since',
+ type: 'dateTime',
+ default: '',
+ },
+ ],
+ },
+
+ // ----------------------------------------
+ // change: update
+ // ----------------------------------------
+ {
+ displayName: 'Change ID',
+ name: 'changeId',
+ description: 'ID of the change to update',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'change',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Update Fields',
+ name: 'updateFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'change',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Agent Name/ID',
+ name: 'agent_id',
+ type: 'options',
+ default: '',
+ description: 'ID of the agent to whom the change is assigned. Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getAgents',
+ ],
+ },
+ },
+ {
+ displayName: 'Change Type',
+ name: 'change_type',
+ type: 'options',
+ default: 1,
+ options: [
+ {
+ name: 'Minor',
+ value: 1,
+ },
+ {
+ name: 'Standard',
+ value: 2,
+ },
+ {
+ name: 'Major',
+ value: 3,
+ },
+ {
+ name: 'Emergency',
+ value: 4,
+ },
+ ],
+ },
+ {
+ displayName: 'Department Name/ID',
+ name: 'department_id',
+ type: 'options',
+ default: '',
+ description: 'ID of the department requesting the change. Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getDepartments',
+ ],
+ },
+ },
+ {
+ displayName: 'Description',
+ name: 'description',
+ type: 'string',
+ default: '',
+ description: 'HTML supported',
+ },
+ {
+ displayName: 'Group Name/ID',
+ name: 'group_id',
+ type: 'options',
+ default: '',
+ description: 'ID of the agent group to which the change is assigned. Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getAgentGroups',
+ ],
+ },
+ },
+ {
+ displayName: 'Impact',
+ name: 'impact',
+ type: 'options',
+ default: 1,
+ description: 'Impact of the change',
+ options: [
+ {
+ name: 'Low',
+ value: 1,
+ },
+ {
+ name: 'Medium',
+ value: 2,
+ },
+ {
+ name: 'High',
+ value: 3,
+ },
+ ],
+ },
+ {
+ displayName: 'Priority',
+ name: 'priority',
+ type: 'options',
+ default: 1,
+ options: [
+ {
+ name: 'Low',
+ value: 1,
+ },
+ {
+ name: 'Medium',
+ value: 2,
+ },
+ {
+ name: 'High',
+ value: 3,
+ },
+ {
+ name: 'Urgent',
+ value: 4,
+ },
+ ],
+ },
+ {
+ displayName: 'Requester Name/ID',
+ name: 'requester_id',
+ type: 'options',
+ default: '',
+ description: 'ID of the requester of the change. Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getRequesters',
+ ],
+ },
+ },
+ {
+ displayName: 'Risk',
+ name: 'risk',
+ type: 'options',
+ default: 1,
+ options: [
+ {
+ name: 'Low',
+ value: 1,
+ },
+ {
+ name: 'Medium',
+ value: 2,
+ },
+ {
+ name: 'High',
+ value: 3,
+ },
+ {
+ name: 'Very High',
+ value: 4,
+ },
+ ],
+ },
+ {
+ displayName: 'Status',
+ name: 'status',
+ type: 'options',
+ default: 1,
+ options: [
+ {
+ name: 'Open',
+ value: 1,
+ },
+ {
+ name: 'Planning',
+ value: 2,
+ },
+ {
+ name: 'Approval',
+ value: 3,
+ },
+ {
+ name: 'Pending Release',
+ value: 4,
+ },
+ {
+ name: 'Pending Review',
+ value: 5,
+ },
+ {
+ name: 'Closed',
+ value: 6,
+ },
+ ],
+ },
+ {
+ displayName: 'Subject',
+ name: 'subject',
+ type: 'string',
+ default: '',
+ },
+ ],
+ },
+] as INodeProperties[];
diff --git a/packages/nodes-base/nodes/Freshservice/descriptions/DepartmentDescription.ts b/packages/nodes-base/nodes/Freshservice/descriptions/DepartmentDescription.ts
new file mode 100644
index 0000000000..7efcca4eff
--- /dev/null
+++ b/packages/nodes-base/nodes/Freshservice/descriptions/DepartmentDescription.ts
@@ -0,0 +1,275 @@
+import {
+ INodeProperties,
+} from 'n8n-workflow';
+
+export const departmentOperations = [
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ displayOptions: {
+ show: {
+ resource: [
+ 'department',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Create',
+ value: 'create',
+ description: 'Create a department',
+ },
+ {
+ name: 'Delete',
+ value: 'delete',
+ description: 'Delete a department',
+ },
+ {
+ name: 'Get',
+ value: 'get',
+ description: 'Retrieve a department',
+ },
+ {
+ name: 'Get All',
+ value: 'getAll',
+ description: 'Retrieve all departments',
+ },
+ {
+ name: 'Update',
+ value: 'update',
+ description: 'Update a department',
+ },
+ ],
+ default: 'create',
+ },
+] as INodeProperties[];
+
+export const departmentFields = [
+ // ----------------------------------------
+ // department: create
+ // ----------------------------------------
+ {
+ displayName: 'Name',
+ name: 'name',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'department',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Additional Fields',
+ name: 'additionalFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'department',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Description',
+ name: 'description',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Domains',
+ name: 'domains',
+ type: 'string',
+ default: '',
+ description: 'Comma-separated email domains associated with the department',
+ },
+ ],
+ },
+
+ // ----------------------------------------
+ // department: delete
+ // ----------------------------------------
+ {
+ displayName: 'Department ID',
+ name: 'departmentId',
+ description: 'ID of the department to delete',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'department',
+ ],
+ operation: [
+ 'delete',
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // department: get
+ // ----------------------------------------
+ {
+ displayName: 'Department ID',
+ name: 'departmentId',
+ description: 'ID of the department to retrieve',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'department',
+ ],
+ operation: [
+ 'get',
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // department: getAll
+ // ----------------------------------------
+ {
+ displayName: 'Return All',
+ name: 'returnAll',
+ type: 'boolean',
+ default: false,
+ description: 'Whether to return all results or only up to a given limit',
+ displayOptions: {
+ show: {
+ resource: [
+ 'department',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Limit',
+ name: 'limit',
+ type: 'number',
+ default: 50,
+ description: 'How many results to return',
+ typeOptions: {
+ minValue: 1,
+ },
+ displayOptions: {
+ show: {
+ resource: [
+ 'department',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ returnAll: [
+ false,
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Filters',
+ name: 'filters',
+ type: 'collection',
+ placeholder: 'Add Filter',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'department',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Name',
+ name: 'name',
+ type: 'string',
+ default: '',
+ description: 'Name of the department',
+ },
+ ],
+ },
+
+ // ----------------------------------------
+ // department: update
+ // ----------------------------------------
+ {
+ displayName: 'Department ID',
+ name: 'departmentId',
+ description: 'ID of the department to update',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'department',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Update Fields',
+ name: 'updateFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'department',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Description',
+ name: 'description',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Domains',
+ name: 'domains',
+ type: 'string',
+ default: '',
+ description: 'Comma-separated email domains associated with the department',
+ },
+ {
+ displayName: 'Name',
+ name: 'name',
+ type: 'string',
+ default: '',
+ },
+ ],
+ },
+] as INodeProperties[];
diff --git a/packages/nodes-base/nodes/Freshservice/descriptions/LocationDescription.ts b/packages/nodes-base/nodes/Freshservice/descriptions/LocationDescription.ts
new file mode 100644
index 0000000000..3fdc2d3ac8
--- /dev/null
+++ b/packages/nodes-base/nodes/Freshservice/descriptions/LocationDescription.ts
@@ -0,0 +1,326 @@
+import {
+ INodeProperties,
+} from 'n8n-workflow';
+
+export const locationOperations = [
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ displayOptions: {
+ show: {
+ resource: [
+ 'location',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Create',
+ value: 'create',
+ description: 'Create a location',
+ },
+ {
+ name: 'Delete',
+ value: 'delete',
+ description: 'Delete a location',
+ },
+ {
+ name: 'Get',
+ value: 'get',
+ description: 'Retrieve a location',
+ },
+ {
+ name: 'Get All',
+ value: 'getAll',
+ description: 'Retrieve all locations',
+ },
+ {
+ name: 'Update',
+ value: 'update',
+ description: 'Update a location',
+ },
+ ],
+ default: 'create',
+ },
+] as INodeProperties[];
+
+export const locationFields = [
+ // ----------------------------------------
+ // location: create
+ // ----------------------------------------
+ {
+ displayName: 'Name',
+ name: 'name',
+ description: 'Name of the location',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'location',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Additional Fields',
+ name: 'additionalFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'location',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Address',
+ name: 'address',
+ placeholder: 'Add Address Fields',
+ type: 'fixedCollection',
+ default: {},
+ options: [
+ {
+ displayName: 'Address Details',
+ name: 'addressFields',
+ values: [
+ {
+ displayName: 'Line 1',
+ name: 'line1',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Line 2',
+ name: 'line2',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'City',
+ name: 'city',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Country',
+ name: 'country',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'State',
+ name: 'state',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Zip Code',
+ name: 'zipcode',
+ type: 'string',
+ default: '',
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+
+ // ----------------------------------------
+ // location: delete
+ // ----------------------------------------
+ {
+ displayName: 'Location ID',
+ name: 'locationId',
+ description: 'ID of the location to delete',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'location',
+ ],
+ operation: [
+ 'delete',
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // location: get
+ // ----------------------------------------
+ {
+ displayName: 'Location ID',
+ name: 'locationId',
+ description: 'ID of the location to retrieve',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'location',
+ ],
+ operation: [
+ 'get',
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // location: getAll
+ // ----------------------------------------
+ {
+ displayName: 'Return All',
+ name: 'returnAll',
+ type: 'boolean',
+ default: false,
+ description: 'Whether to return all results or only up to a given limit',
+ displayOptions: {
+ show: {
+ resource: [
+ 'location',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Limit',
+ name: 'limit',
+ type: 'number',
+ default: 50,
+ description: 'How many results to return',
+ typeOptions: {
+ minValue: 1,
+ },
+ displayOptions: {
+ show: {
+ resource: [
+ 'location',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ returnAll: [
+ false,
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // location: update
+ // ----------------------------------------
+ {
+ displayName: 'Location ID',
+ name: 'locationId',
+ description: 'ID of the location to update',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'location',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Update Fields',
+ name: 'updateFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'location',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Name',
+ name: 'name',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Address',
+ name: 'address',
+ placeholder: 'Add Address Fields',
+ type: 'fixedCollection',
+ default: {},
+ options: [
+ {
+ displayName: 'Address Details',
+ name: 'addressFields',
+ values: [
+ {
+ displayName: 'Line 1',
+ name: 'line1',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Line 2',
+ name: 'line2',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'City',
+ name: 'city',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Country',
+ name: 'country',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'State',
+ name: 'state',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Zip Code',
+ name: 'zipcode',
+ type: 'string',
+ default: '',
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+] as INodeProperties[];
diff --git a/packages/nodes-base/nodes/Freshservice/descriptions/ProblemDescription.ts b/packages/nodes-base/nodes/Freshservice/descriptions/ProblemDescription.ts
new file mode 100644
index 0000000000..742429a3d5
--- /dev/null
+++ b/packages/nodes-base/nodes/Freshservice/descriptions/ProblemDescription.ts
@@ -0,0 +1,496 @@
+import {
+ INodeProperties,
+} from 'n8n-workflow';
+
+export const problemOperations = [
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ displayOptions: {
+ show: {
+ resource: [
+ 'problem',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Create',
+ value: 'create',
+ description: 'Create a problem',
+ },
+ {
+ name: 'Delete',
+ value: 'delete',
+ description: 'Delete a problem',
+ },
+ {
+ name: 'Get',
+ value: 'get',
+ description: 'Retrieve a problem',
+ },
+ {
+ name: 'Get All',
+ value: 'getAll',
+ description: 'Retrieve all problems',
+ },
+ {
+ name: 'Update',
+ value: 'update',
+ description: 'Update a problem',
+ },
+ ],
+ default: 'create',
+ },
+] as INodeProperties[];
+
+export const problemFields = [
+ // ----------------------------------------
+ // problem: create
+ // ----------------------------------------
+ {
+ displayName: 'Subject',
+ name: 'subject',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'problem',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Requester Name/ID',
+ name: 'requesterId',
+ description: 'ID of the initiator of the problem. Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ type: 'options',
+ required: true,
+ default: '',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getRequesters',
+ ],
+ },
+ displayOptions: {
+ show: {
+ resource: [
+ 'problem',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Due By',
+ name: 'dueBy',
+ description: 'Date when the problem is due to be solved',
+ type: 'dateTime',
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'problem',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Additional Fields',
+ name: 'additionalFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'problem',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Agent Name/ID',
+ name: 'agent_id',
+ type: 'options',
+ default: '',
+ description: 'ID of the agent to whom the problem is assigned. Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getAgents',
+ ],
+ },
+ },
+ {
+ displayName: 'Department Name/ID',
+ name: 'department_id',
+ type: 'options',
+ default: '',
+ description: 'ID of the department initiating the problem. Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getDepartments',
+ ],
+ },
+ },
+ {
+ displayName: 'Description',
+ name: 'description',
+ description: 'HTML supported',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Group Name/ID',
+ name: 'group_id',
+ type: 'options',
+ default: '',
+ description: 'ID of the agent group to which the problem is assigned. Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getAgentGroups',
+ ],
+ },
+ },
+ {
+ displayName: 'Impact',
+ name: 'impact',
+ type: 'options',
+ default: 1,
+ options: [
+ {
+ name: 'Low',
+ value: 1,
+ },
+ {
+ name: 'Medium',
+ value: 2,
+ },
+ {
+ name: 'High',
+ value: 3,
+ },
+ ],
+ },
+ {
+ displayName: 'Priority',
+ name: 'priority',
+ type: 'options',
+ default: 1,
+ options: [
+ {
+ name: 'Low',
+ value: 1,
+ },
+ {
+ name: 'Medium',
+ value: 2,
+ },
+ {
+ name: 'High',
+ value: 3,
+ },
+ {
+ name: 'Urgent',
+ value: 4,
+ },
+ ],
+ },
+ {
+ displayName: 'Status',
+ name: 'status',
+ type: 'options',
+ default: 1,
+ options: [
+ {
+ name: 'Open',
+ value: 1,
+ },
+ {
+ name: 'Change Requested',
+ value: 2,
+ },
+ {
+ name: 'Closed',
+ value: 3,
+ },
+ ],
+ },
+ ],
+ },
+
+ // ----------------------------------------
+ // problem: delete
+ // ----------------------------------------
+ {
+ displayName: 'Problem ID',
+ name: 'problemId',
+ description: 'ID of the problem to delete',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'problem',
+ ],
+ operation: [
+ 'delete',
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // problem: get
+ // ----------------------------------------
+ {
+ displayName: 'Problem ID',
+ name: 'problemId',
+ description: 'ID of the problem to retrieve',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'problem',
+ ],
+ operation: [
+ 'get',
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // problem: getAll
+ // ----------------------------------------
+ {
+ displayName: 'Return All',
+ name: 'returnAll',
+ type: 'boolean',
+ default: false,
+ description: 'Whether to return all results or only up to a given limit',
+ displayOptions: {
+ show: {
+ resource: [
+ 'problem',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Limit',
+ name: 'limit',
+ type: 'number',
+ default: 50,
+ description: 'How many results to return',
+ typeOptions: {
+ minValue: 1,
+ },
+ displayOptions: {
+ show: {
+ resource: [
+ 'problem',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ returnAll: [
+ false,
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // problem: update
+ // ----------------------------------------
+ {
+ displayName: 'Problem ID',
+ name: 'problemId',
+ description: 'ID of the problem to update',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'problem',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Update Fields',
+ name: 'updateFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'problem',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Agent Name/ID',
+ name: 'agent_id',
+ type: 'options',
+ default: '',
+ description: 'ID of the agent to whom the problem is assigned. Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getAgents',
+ ],
+ },
+ },
+ {
+ displayName: 'Department Name/ID',
+ name: 'department_id',
+ type: 'options',
+ default: '',
+ description: 'ID of the department initiating the problem. Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getDepartments',
+ ],
+ },
+ },
+ {
+ displayName: 'Description',
+ name: 'description',
+ type: 'string',
+ default: '',
+ description: 'HTML supported',
+ },
+ {
+ displayName: 'Due By',
+ name: 'due_by',
+ description: 'Date when the problem is due to be solved',
+ type: 'dateTime',
+ default: '',
+ },
+ {
+ displayName: 'Group Name/ID',
+ name: 'group_id',
+ type: 'options',
+ default: '',
+ description: 'ID of the agent group to which the problem is assigned. Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getAgentGroups',
+ ],
+ },
+ },
+ {
+ displayName: 'Impact',
+ name: 'impact',
+ type: 'options',
+ default: 1,
+ options: [
+ {
+ name: 'Low',
+ value: 1,
+ },
+ {
+ name: 'Medium',
+ value: 2,
+ },
+ {
+ name: 'High',
+ value: 3,
+ },
+ ],
+ },
+ {
+ displayName: 'Priority',
+ name: 'priority',
+ type: 'options',
+ default: 1,
+ options: [
+ {
+ name: 'Low',
+ value: 1,
+ },
+ {
+ name: 'Medium',
+ value: 2,
+ },
+ {
+ name: 'High',
+ value: 3,
+ },
+ {
+ name: 'Urgent',
+ value: 4,
+ },
+ ],
+ },
+ {
+ displayName: 'Requester Name/ID',
+ name: 'requester_id',
+ type: 'options',
+ default: '',
+ description: 'ID of the initiator of the problem. Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getRequesters',
+ ],
+ },
+ },
+ {
+ displayName: 'Status',
+ name: 'status',
+ type: 'options',
+ default: 1,
+ options: [
+ {
+ name: 'Open',
+ value: 1,
+ },
+ {
+ name: 'Change Requested',
+ value: 2,
+ },
+ {
+ name: 'Closed',
+ value: 3,
+ },
+ ],
+ },
+ {
+ displayName: 'Subject',
+ name: 'subject',
+ type: 'string',
+ default: '',
+ },
+ ],
+ },
+] as INodeProperties[];
diff --git a/packages/nodes-base/nodes/Freshservice/descriptions/ProductDescription.ts b/packages/nodes-base/nodes/Freshservice/descriptions/ProductDescription.ts
new file mode 100644
index 0000000000..df54737060
--- /dev/null
+++ b/packages/nodes-base/nodes/Freshservice/descriptions/ProductDescription.ts
@@ -0,0 +1,364 @@
+import {
+ INodeProperties,
+} from 'n8n-workflow';
+
+export const productOperations = [
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ displayOptions: {
+ show: {
+ resource: [
+ 'product',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Create',
+ value: 'create',
+ description: 'Create a product',
+ },
+ {
+ name: 'Delete',
+ value: 'delete',
+ description: 'Delete a product',
+ },
+ {
+ name: 'Get',
+ value: 'get',
+ description: 'Retrieve a product',
+ },
+ {
+ name: 'Get All',
+ value: 'getAll',
+ description: 'Retrieve all products',
+ },
+ {
+ name: 'Update',
+ value: 'update',
+ description: 'Update a product',
+ },
+ ],
+ default: 'create',
+ },
+] as INodeProperties[];
+
+export const productFields = [
+ // ----------------------------------------
+ // product: create
+ // ----------------------------------------
+ {
+ displayName: 'Asset Type Name/ID',
+ name: 'assetTypeId',
+ type: 'options',
+ description: 'Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ required: true,
+ default: '',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getAssetTypes',
+ ],
+ },
+ displayOptions: {
+ show: {
+ resource: [
+ 'product',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Name',
+ name: 'name',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'product',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Additional Fields',
+ name: 'additionalFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'product',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Description',
+ name: 'description',
+ type: 'string',
+ default: '',
+ description: 'HTML supported',
+ },
+ {
+ displayName: 'Manufacturer',
+ name: 'manufacturer',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Mode of Procurement',
+ name: 'mode_of_procurement',
+ type: 'options',
+ default: 'Buy',
+ options: [
+ {
+ name: 'Buy',
+ value: 'Buy',
+ },
+ {
+ name: 'Lease',
+ value: 'Lease',
+ },
+ {
+ name: 'Both',
+ value: 'Both',
+ },
+ ],
+ },
+ {
+ displayName: 'Status',
+ name: 'status',
+ type: 'options',
+ default: 'In Production',
+ options: [
+ {
+ name: 'In Production',
+ value: 'In Production',
+ },
+ {
+ name: 'In Pipeline',
+ value: 'In Pipeline',
+ },
+ {
+ name: 'Retired',
+ value: 'Retired',
+ },
+ ],
+ },
+ ],
+ },
+
+ // ----------------------------------------
+ // product: delete
+ // ----------------------------------------
+ {
+ displayName: 'Product ID',
+ name: 'productId',
+ description: 'ID of the product to delete',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'product',
+ ],
+ operation: [
+ 'delete',
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // product: get
+ // ----------------------------------------
+ {
+ displayName: 'Product ID',
+ name: 'productId',
+ description: 'ID of the product to retrieve',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'product',
+ ],
+ operation: [
+ 'get',
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // product: getAll
+ // ----------------------------------------
+ {
+ displayName: 'Return All',
+ name: 'returnAll',
+ type: 'boolean',
+ default: false,
+ description: 'Whether to return all results or only up to a given limit',
+ displayOptions: {
+ show: {
+ resource: [
+ 'product',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Limit',
+ name: 'limit',
+ type: 'number',
+ default: 50,
+ description: 'How many results to return',
+ typeOptions: {
+ minValue: 1,
+ },
+ displayOptions: {
+ show: {
+ resource: [
+ 'product',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ returnAll: [
+ false,
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // product: update
+ // ----------------------------------------
+ {
+ displayName: 'Product ID',
+ name: 'productId',
+ description: 'ID of the product to update',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'product',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Update Fields',
+ name: 'updateFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'product',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Asset Type Name/ID',
+ name: 'asset_type_id',
+ type: 'options',
+ description: 'Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ default: '',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getAssetTypes',
+ ],
+ },
+ },
+ {
+ displayName: 'Description',
+ name: 'description',
+ type: 'string',
+ default: '',
+ description: 'HTML supported',
+ },
+ {
+ displayName: 'Manufacturer',
+ name: 'manufacturer',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Mode of Procurement',
+ name: 'mode_of_procurement',
+ type: 'options',
+ default: 'Buy',
+ options: [
+ {
+ name: 'Buy',
+ value: 'Buy',
+ },
+ {
+ name: 'Lease',
+ value: 'Lease',
+ },
+ {
+ name: 'Both',
+ value: 'Both',
+ },
+ ],
+ },
+ {
+ displayName: 'Name',
+ name: 'name',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Status',
+ name: 'status',
+ type: 'options',
+ default: 'In Production',
+ options: [
+ {
+ name: 'In Production',
+ value: 'In Production',
+ },
+ {
+ name: 'In Pipeline',
+ value: 'In Pipeline',
+ },
+ {
+ name: 'Retired',
+ value: 'Retired',
+ },
+ ],
+ },
+ ],
+ },
+] as INodeProperties[];
diff --git a/packages/nodes-base/nodes/Freshservice/descriptions/ReleaseDescription.ts b/packages/nodes-base/nodes/Freshservice/descriptions/ReleaseDescription.ts
new file mode 100644
index 0000000000..bc57e0bb48
--- /dev/null
+++ b/packages/nodes-base/nodes/Freshservice/descriptions/ReleaseDescription.ts
@@ -0,0 +1,501 @@
+import {
+ INodeProperties,
+} from 'n8n-workflow';
+
+export const releaseOperations = [
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ displayOptions: {
+ show: {
+ resource: [
+ 'release',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Create',
+ value: 'create',
+ description: 'Create a release',
+ },
+ {
+ name: 'Delete',
+ value: 'delete',
+ description: 'Delete a release',
+ },
+ {
+ name: 'Get',
+ value: 'get',
+ description: 'Retrieve a release',
+ },
+ {
+ name: 'Get All',
+ value: 'getAll',
+ description: 'Retrieve all releases',
+ },
+ {
+ name: 'Update',
+ value: 'update',
+ description: 'Update a release',
+ },
+ ],
+ default: 'create',
+ },
+] as INodeProperties[];
+
+export const releaseFields = [
+ // ----------------------------------------
+ // release: create
+ // ----------------------------------------
+ {
+ displayName: 'Subject',
+ name: 'subject',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'release',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Release Type',
+ name: 'releaseType',
+ type: 'options',
+ default: 1,
+ displayOptions: {
+ show: {
+ resource: [
+ 'release',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Minor',
+ value: 1,
+ },
+ {
+ name: 'Standard',
+ value: 2,
+ },
+ {
+ name: 'Major',
+ value: 3,
+ },
+ {
+ name: 'Emergency',
+ value: 4,
+ },
+ ],
+ },
+ {
+ displayName: 'Priority',
+ name: 'priority',
+ type: 'options',
+ default: 1,
+ displayOptions: {
+ show: {
+ resource: [
+ 'release',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Low',
+ value: 1,
+ },
+ {
+ name: 'Medium',
+ value: 2,
+ },
+ {
+ name: 'High',
+ value: 3,
+ },
+ {
+ name: 'Urgent',
+ value: 4,
+ },
+ ],
+ },
+ {
+ displayName: 'Status',
+ name: 'status',
+ type: 'options',
+ default: 1,
+ displayOptions: {
+ show: {
+ resource: [
+ 'release',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Open',
+ value: 1,
+ },
+ {
+ name: 'On Hold',
+ value: 2,
+ },
+ {
+ name: 'In Progress',
+ value: 3,
+ },
+ {
+ name: 'Incomplete',
+ value: 4,
+ },
+ {
+ name: 'Completed',
+ value: 5,
+ },
+ ],
+ },
+ {
+ displayName: 'Planned Start Date',
+ name: 'plannedStartDate',
+ type: 'dateTime',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'release',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Planned End Date',
+ name: 'plannedEndDate',
+ type: 'dateTime',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'release',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Additional Fields',
+ name: 'additionalFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'release',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Department Name/ID',
+ name: 'department_id',
+ type: 'options',
+ default: '',
+ description: 'ID of the department initiating the release. Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getDepartments',
+ ],
+ },
+ },
+ {
+ displayName: 'Description',
+ name: 'description',
+ description: 'HTML supported',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Group Name/ID',
+ name: 'group_id',
+ type: 'options',
+ default: '',
+ description: 'ID of the agent group to which the release is assigned. Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getAgentGroups',
+ ],
+ },
+ },
+ ],
+ },
+
+ // ----------------------------------------
+ // release: delete
+ // ----------------------------------------
+ {
+ displayName: 'Release ID',
+ name: 'releaseId',
+ description: 'ID of the release to delete',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'release',
+ ],
+ operation: [
+ 'delete',
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // release: get
+ // ----------------------------------------
+ {
+ displayName: 'Release ID',
+ name: 'releaseId',
+ description: 'ID of the release to retrieve',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'release',
+ ],
+ operation: [
+ 'get',
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // release: getAll
+ // ----------------------------------------
+ {
+ displayName: 'Return All',
+ name: 'returnAll',
+ type: 'boolean',
+ default: false,
+ description: 'Whether to return all results or only up to a given limit',
+ displayOptions: {
+ show: {
+ resource: [
+ 'release',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Limit',
+ name: 'limit',
+ type: 'number',
+ default: 50,
+ description: 'How many results to return',
+ typeOptions: {
+ minValue: 1,
+ },
+ displayOptions: {
+ show: {
+ resource: [
+ 'release',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ returnAll: [
+ false,
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // release: update
+ // ----------------------------------------
+ {
+ displayName: 'Release ID',
+ name: 'releaseId',
+ description: 'ID of the release to update',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'release',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Update Fields',
+ name: 'updateFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'release',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Department Name/ID',
+ name: 'department_id',
+ type: 'options',
+ default: '',
+ description: 'ID of the department initiating the release. Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getDepartments',
+ ],
+ },
+ },
+ {
+ displayName: 'Description',
+ name: 'description',
+ type: 'string',
+ default: '',
+ description: 'HTML supported',
+ },
+ {
+ displayName: 'Group Name/ID',
+ name: 'group_id',
+ type: 'options',
+ default: '',
+ description: 'ID of the agent group to which the release is assigned. Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getAgentGroups',
+ ],
+ },
+ },
+ {
+ displayName: 'Priority',
+ name: 'priority',
+ type: 'options',
+ default: 1,
+ options: [
+ {
+ name: 'Low',
+ value: 1,
+ },
+ {
+ name: 'Medium',
+ value: 2,
+ },
+ {
+ name: 'High',
+ value: 3,
+ },
+ {
+ name: 'Urgent',
+ value: 4,
+ },
+ ],
+ },
+ {
+ displayName: 'Release Type',
+ name: 'release_type',
+ type: 'options',
+ default: 1,
+ options: [
+ {
+ name: 'Minor',
+ value: 1,
+ },
+ {
+ name: 'Standard',
+ value: 2,
+ },
+ {
+ name: 'Major',
+ value: 3,
+ },
+ {
+ name: 'Emergency',
+ value: 4,
+ },
+ ],
+ },
+ {
+ displayName: 'Status',
+ name: 'status',
+ type: 'options',
+ default: 1,
+ options: [
+ {
+ name: 'Open',
+ value: 1,
+ },
+ {
+ name: 'On Hold',
+ value: 2,
+ },
+ {
+ name: 'In Progress',
+ value: 3,
+ },
+ {
+ name: 'Incomplete',
+ value: 4,
+ },
+ {
+ name: 'Completed',
+ value: 5,
+ },
+ ],
+ },
+ {
+ displayName: 'Subject',
+ name: 'subject',
+ type: 'string',
+ default: '',
+ },
+ ],
+ },
+] as INodeProperties[];
diff --git a/packages/nodes-base/nodes/Freshservice/descriptions/RequesterDescription.ts b/packages/nodes-base/nodes/Freshservice/descriptions/RequesterDescription.ts
new file mode 100644
index 0000000000..a2a348ec5f
--- /dev/null
+++ b/packages/nodes-base/nodes/Freshservice/descriptions/RequesterDescription.ts
@@ -0,0 +1,512 @@
+import {
+ INodeProperties,
+} from 'n8n-workflow';
+import { LANGUAGES } from '../constants';
+
+export const requesterOperations = [
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ displayOptions: {
+ show: {
+ resource: [
+ 'requester',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Create',
+ value: 'create',
+ description: 'Create a requester',
+ },
+ {
+ name: 'Delete',
+ value: 'delete',
+ description: 'Delete a requester',
+ },
+ {
+ name: 'Get',
+ value: 'get',
+ description: 'Retrieve a requester',
+ },
+ {
+ name: 'Get All',
+ value: 'getAll',
+ description: 'Retrieve all requesters',
+ },
+ {
+ name: 'Update',
+ value: 'update',
+ description: 'Update a requester',
+ },
+ ],
+ default: 'create',
+ },
+] as INodeProperties[];
+
+export const requesterFields = [
+ // ----------------------------------------
+ // requester: create
+ // ----------------------------------------
+ {
+ displayName: 'First Name',
+ name: 'firstName',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'requester',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Primary Email',
+ name: 'primaryEmail',
+ type: 'string',
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'requester',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Additional Fields',
+ name: 'additionalFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'requester',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Address',
+ name: 'address',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Background Information',
+ name: 'background_information',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Department Names/IDs',
+ name: 'department_ids',
+ type: 'multiOptions',
+ default: [],
+ description: 'Comma-separated IDs of the departments associated with the requester. Choose from the list or specify an ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getDepartments',
+ ],
+ },
+ },
+ {
+ displayName: 'Job Title',
+ name: 'job_title',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Language',
+ name: 'language',
+ type: 'options',
+ default: '',
+ options: LANGUAGES,
+ },
+ {
+ displayName: 'Last Name',
+ name: 'last_name',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Location ID',
+ name: 'location_id',
+ description: 'Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ type: 'options',
+ default: '',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getLocations',
+ ],
+ },
+ },
+ {
+ displayName: 'Mobile Phone',
+ name: 'mobile_phone_number',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Secondary Emails',
+ name: 'secondary_emails',
+ type: 'string',
+ default: '',
+ description: 'Comma-separated secondary emails associated with the requester',
+ },
+ {
+ displayName: 'Time Format',
+ name: 'time_format',
+ type: 'options',
+ default: '12h',
+ options: [
+ {
+ name: '12-Hour Format',
+ value: '12h',
+ },
+ {
+ name: '24-Hour Format',
+ value: '24h',
+ },
+ ],
+ },
+ {
+ displayName: 'Work Phone',
+ name: 'work_phone_number',
+ type: 'string',
+ default: '',
+ },
+ ],
+ },
+
+ // ----------------------------------------
+ // requester: delete
+ // ----------------------------------------
+ {
+ displayName: 'Requester ID',
+ name: 'requesterId',
+ description: 'ID of the requester to delete',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'requester',
+ ],
+ operation: [
+ 'delete',
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // requester: get
+ // ----------------------------------------
+ {
+ displayName: 'Requester ID',
+ name: 'requesterId',
+ description: 'ID of the requester to retrieve',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'requester',
+ ],
+ operation: [
+ 'get',
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // requester: getAll
+ // ----------------------------------------
+ {
+ displayName: 'Return All',
+ name: 'returnAll',
+ type: 'boolean',
+ default: false,
+ description: 'Whether to return all results or only up to a given limit',
+ displayOptions: {
+ show: {
+ resource: [
+ 'requester',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Limit',
+ name: 'limit',
+ type: 'number',
+ default: 50,
+ description: 'How many results to return',
+ typeOptions: {
+ minValue: 1,
+ },
+ displayOptions: {
+ show: {
+ resource: [
+ 'requester',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ returnAll: [
+ false,
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Filters',
+ name: 'filters',
+ type: 'collection',
+ placeholder: 'Add Filter',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'requester',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Department Name/ID',
+ name: 'department_id',
+ description: 'Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ type: 'options',
+ default: '',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getDepartments',
+ ],
+ },
+ },
+ {
+ displayName: 'First Name',
+ name: 'first_name',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Job Title',
+ name: 'job_title',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Language',
+ name: 'language',
+ type: 'options',
+ default: '',
+ options: LANGUAGES,
+ },
+ {
+ displayName: 'Last Name',
+ name: 'last_name',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Location Name/ID',
+ name: 'location_id',
+ type: 'options',
+ description: 'Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ default: '',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getLocations',
+ ],
+ },
+ },
+ {
+ displayName: 'Mobile Phone Number',
+ name: 'mobile_phone_number',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Primary Email',
+ name: 'primary_email',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Work Phone Number',
+ name: 'work_phone_number',
+ type: 'string',
+ default: '',
+ },
+ ],
+ },
+
+ // ----------------------------------------
+ // requester: update
+ // ----------------------------------------
+ {
+ displayName: 'Requester ID',
+ name: 'requesterId',
+ description: 'ID of the requester to update',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'requester',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Update Fields',
+ name: 'updateFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'requester',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Address',
+ name: 'address',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Background Information',
+ name: 'background_information',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Department Names/IDs',
+ name: 'department_ids',
+ type: 'multiOptions',
+ default: [],
+ description: 'Comma-separated IDs of the departments associated with the requester. Choose from the list or specify an ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getDepartments',
+ ],
+ },
+ },
+ {
+ displayName: 'First Name',
+ name: 'first_name',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Job Title',
+ name: 'job_title',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Language',
+ name: 'language',
+ type: 'options',
+ default: '',
+ options: LANGUAGES,
+ },
+ {
+ displayName: 'Last Name',
+ name: 'last_name',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Location Name/ID',
+ name: 'location_id',
+ type: 'options',
+ default: '',
+ description: 'Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getLocations',
+ ],
+ },
+ },
+ {
+ displayName: 'Mobile Phone',
+ name: 'mobile_phone_number',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Primary Email',
+ name: 'primary_email',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Secondary Emails',
+ name: 'secondary_emails',
+ type: 'string',
+ default: '',
+ description: 'Comma-separated secondary emails associated with the requester',
+ },
+ {
+ displayName: 'Time Format',
+ name: 'time_format',
+ type: 'options',
+ default: '12h',
+ options: [
+ {
+ name: '12-Hour Format',
+ value: '12h',
+ },
+ {
+ name: '24-Hour Format',
+ value: '24h',
+ },
+ ],
+ },
+ {
+ displayName: 'Work Phone',
+ name: 'work_phone_number',
+ type: 'string',
+ default: '',
+ },
+ ],
+ },
+] as INodeProperties[];
diff --git a/packages/nodes-base/nodes/Freshservice/descriptions/RequesterGroupDescription.ts b/packages/nodes-base/nodes/Freshservice/descriptions/RequesterGroupDescription.ts
new file mode 100644
index 0000000000..1ed51ee6e4
--- /dev/null
+++ b/packages/nodes-base/nodes/Freshservice/descriptions/RequesterGroupDescription.ts
@@ -0,0 +1,237 @@
+import {
+ INodeProperties,
+} from 'n8n-workflow';
+
+export const requesterGroupOperations = [
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ displayOptions: {
+ show: {
+ resource: [
+ 'requesterGroup',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Create',
+ value: 'create',
+ description: 'Create a requester group',
+ },
+ {
+ name: 'Delete',
+ value: 'delete',
+ description: 'Delete a requester group',
+ },
+ {
+ name: 'Get',
+ value: 'get',
+ description: 'Retrieve a requester group',
+ },
+ {
+ name: 'Get All',
+ value: 'getAll',
+ description: 'Retrieve all requester groups',
+ },
+ {
+ name: 'Update',
+ value: 'update',
+ description: 'Update a requester group',
+ },
+ ],
+ default: 'create',
+ },
+] as INodeProperties[];
+
+export const requesterGroupFields = [
+ // ----------------------------------------
+ // requesterGroup: create
+ // ----------------------------------------
+ {
+ displayName: 'Name',
+ name: 'name',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'requesterGroup',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Additional Fields',
+ name: 'additionalFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'requesterGroup',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Description',
+ name: 'description',
+ type: 'string',
+ default: '',
+ },
+ ],
+ },
+
+ // ----------------------------------------
+ // requesterGroup: delete
+ // ----------------------------------------
+ {
+ displayName: 'Requester Group ID',
+ name: 'requesterGroupId',
+ description: 'ID of the requester group to delete',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'requesterGroup',
+ ],
+ operation: [
+ 'delete',
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // requesterGroup: get
+ // ----------------------------------------
+ {
+ displayName: 'Requester Group ID',
+ name: 'requesterGroupId',
+ description: 'ID of the requester group to retrieve',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'requesterGroup',
+ ],
+ operation: [
+ 'get',
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // requesterGroup: getAll
+ // ----------------------------------------
+ {
+ displayName: 'Return All',
+ name: 'returnAll',
+ type: 'boolean',
+ default: false,
+ description: 'Whether to return all results or only up to a given limit',
+ displayOptions: {
+ show: {
+ resource: [
+ 'requesterGroup',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Limit',
+ name: 'limit',
+ type: 'number',
+ default: 50,
+ description: 'How many results to return',
+ typeOptions: {
+ minValue: 1,
+ },
+ displayOptions: {
+ show: {
+ resource: [
+ 'requesterGroup',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ returnAll: [
+ false,
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // requesterGroup: update
+ // ----------------------------------------
+ {
+ displayName: 'Requester Group ID',
+ name: 'requesterGroupId',
+ description: 'ID of the requester group to update',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'requesterGroup',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Update Fields',
+ name: 'updateFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'requesterGroup',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Description',
+ name: 'description',
+ type: 'string',
+ default: '',
+ description: 'Description of the requester group',
+ },
+ {
+ displayName: 'Name',
+ name: 'name',
+ type: 'string',
+ default: '',
+ description: 'Name of the requester group',
+ },
+ ],
+ },
+] as INodeProperties[];
diff --git a/packages/nodes-base/nodes/Freshservice/descriptions/SoftwareDescription.ts b/packages/nodes-base/nodes/Freshservice/descriptions/SoftwareDescription.ts
new file mode 100644
index 0000000000..d8a7255a5e
--- /dev/null
+++ b/packages/nodes-base/nodes/Freshservice/descriptions/SoftwareDescription.ts
@@ -0,0 +1,347 @@
+import {
+ INodeProperties,
+} from 'n8n-workflow';
+
+export const softwareOperations = [
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ displayOptions: {
+ show: {
+ resource: [
+ 'software',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Create',
+ value: 'create',
+ description: 'Create a software application',
+ },
+ {
+ name: 'Delete',
+ value: 'delete',
+ description: 'Delete a software application',
+ },
+ {
+ name: 'Get',
+ value: 'get',
+ description: 'Retrieve a software application',
+ },
+ {
+ name: 'Get All',
+ value: 'getAll',
+ description: 'Retrieve all software applications',
+ },
+ {
+ name: 'Update',
+ value: 'update',
+ description: 'Update a software application',
+ },
+ ],
+ default: 'create',
+ },
+] as INodeProperties[];
+
+export const softwareFields = [
+ // ----------------------------------------
+ // software: create
+ // ----------------------------------------
+ {
+ displayName: 'Application Type',
+ name: 'applicationType',
+ type: 'options',
+ required: true,
+ options: [
+ {
+ name: 'Desktop',
+ value: 'desktop',
+ },
+ {
+ name: 'Mobile',
+ value: 'mobile',
+ },
+ {
+ name: 'SaaS',
+ value: 'saas',
+ },
+ ],
+ default: 'desktop',
+ displayOptions: {
+ show: {
+ resource: [
+ 'software',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Name',
+ name: 'name',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'software',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Additional Fields',
+ name: 'additionalFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'software',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Description',
+ name: 'description',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Notes',
+ name: 'notes',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Status',
+ name: 'status',
+ type: 'options',
+ default: 'managed',
+ options: [
+ {
+ name: 'Disabled',
+ value: 'disabled',
+ },
+ {
+ name: 'Ignored',
+ value: 'ignored',
+ },
+ {
+ name: 'Needs Review',
+ value: 'needs review',
+ },
+ {
+ name: 'Restricted',
+ value: 'restricted',
+ },
+ ],
+ },
+ ],
+ },
+
+ // ----------------------------------------
+ // software: delete
+ // ----------------------------------------
+ {
+ displayName: 'Software ID',
+ name: 'softwareId',
+ description: 'ID of the software application to delete',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'software',
+ ],
+ operation: [
+ 'delete',
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // software: get
+ // ----------------------------------------
+ {
+ displayName: 'Software ID',
+ name: 'softwareId',
+ description: 'ID of the software application to retrieve',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'software',
+ ],
+ operation: [
+ 'get',
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // software: getAll
+ // ----------------------------------------
+ {
+ displayName: 'Return All',
+ name: 'returnAll',
+ type: 'boolean',
+ default: false,
+ description: 'Whether to return all results or only up to a given limit',
+ displayOptions: {
+ show: {
+ resource: [
+ 'software',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Limit',
+ name: 'limit',
+ type: 'number',
+ default: 50,
+ description: 'How many results to return',
+ typeOptions: {
+ minValue: 1,
+ },
+ displayOptions: {
+ show: {
+ resource: [
+ 'software',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ returnAll: [
+ false,
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // software: update
+ // ----------------------------------------
+ {
+ displayName: 'Software ID',
+ name: 'softwareId',
+ description: 'ID of the software application to update',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'software',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Update Fields',
+ name: 'updateFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'software',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Application Type',
+ name: 'application_type',
+ type: 'options',
+ default: 'desktop',
+ description: 'Type of the software',
+ options: [
+ {
+ name: 'Desktop',
+ value: 'desktop',
+ },
+ {
+ name: 'Mobile',
+ value: 'mobile',
+ },
+ {
+ name: 'SaaS',
+ value: 'saas',
+ },
+ ],
+ },
+ {
+ displayName: 'Description',
+ name: 'description',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Name',
+ name: 'name',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Notes',
+ name: 'notes',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Status',
+ name: 'status',
+ type: 'options',
+ default: 'managed',
+ options: [
+ {
+ name: 'Disabled',
+ value: 'disabled',
+ },
+ {
+ name: 'Ignored',
+ value: 'ignored',
+ },
+ {
+ name: 'Needs Review',
+ value: 'needs review',
+ },
+ {
+ name: 'Restricted',
+ value: 'restricted',
+ },
+ ],
+ },
+ ],
+ },
+] as INodeProperties[];
diff --git a/packages/nodes-base/nodes/Freshservice/descriptions/TicketDescription.ts b/packages/nodes-base/nodes/Freshservice/descriptions/TicketDescription.ts
new file mode 100644
index 0000000000..006e355a79
--- /dev/null
+++ b/packages/nodes-base/nodes/Freshservice/descriptions/TicketDescription.ts
@@ -0,0 +1,640 @@
+import {
+ INodeProperties,
+} from 'n8n-workflow';
+
+export const ticketOperations = [
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ displayOptions: {
+ show: {
+ resource: [
+ 'ticket',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Create',
+ value: 'create',
+ description: 'Create a ticket',
+ },
+ {
+ name: 'Delete',
+ value: 'delete',
+ description: 'Delete a ticket',
+ },
+ {
+ name: 'Get',
+ value: 'get',
+ description: 'Retrieve a ticket',
+ },
+ {
+ name: 'Get All',
+ value: 'getAll',
+ description: 'Retrieve all tickets',
+ },
+ {
+ name: 'Update',
+ value: 'update',
+ description: 'Update a ticket',
+ },
+ ],
+ default: 'create',
+ },
+] as INodeProperties[];
+
+export const ticketFields = [
+ // ----------------------------------------
+ // ticket: create
+ // ----------------------------------------
+ {
+ displayName: 'Email',
+ name: 'email',
+ description: 'Email address of the ticket author',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'ticket',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Subject',
+ name: 'subject',
+ type: 'string',
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'ticket',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Description',
+ name: 'description',
+ type: 'string',
+ default: '',
+ description: 'HTML supported',
+ displayOptions: {
+ show: {
+ resource: [
+ 'ticket',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Priority',
+ name: 'priority',
+ type: 'options',
+ default: 1,
+ displayOptions: {
+ show: {
+ resource: [
+ 'ticket',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Low',
+ value: 1,
+ },
+ {
+ name: 'Medium',
+ value: 2,
+ },
+ {
+ name: 'High',
+ value: 3,
+ },
+ {
+ name: 'Urgent',
+ value: 4,
+ },
+ ],
+ },
+ {
+ displayName: 'Status',
+ name: 'status',
+ type: 'options',
+ default: 2,
+ displayOptions: {
+ show: {
+ resource: [
+ 'ticket',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Open',
+ value: 2,
+ },
+ {
+ name: 'Pending',
+ value: 3,
+ },
+ {
+ name: 'Resolved',
+ value: 4,
+ },
+ {
+ name: 'Closed',
+ value: 5,
+ },
+ ],
+ },
+ {
+ displayName: 'Additional Fields',
+ name: 'additionalFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'ticket',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'CC Emails',
+ name: 'cc_emails',
+ type: 'string',
+ default: '',
+ description: 'Comma-separated email addresses to add in the CC field of the ticket email',
+ },
+ {
+ displayName: 'Department Name/ID',
+ name: 'department_id',
+ type: 'options',
+ default: '',
+ description: 'ID of the department to which this ticket belongs. Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getDepartments',
+ ],
+ },
+ },
+ {
+ displayName: 'Group Name/ID',
+ name: 'group_id',
+ type: 'options',
+ default: '',
+ description: 'ID of the group to which the ticket has been assigned. Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getAgentGroups',
+ ],
+ },
+ },
+ {
+ displayName: 'Impact',
+ name: 'impact',
+ type: 'options',
+ default: 1,
+ options: [
+ {
+ name: 'Low',
+ value: 1,
+ },
+ {
+ name: 'Medium',
+ value: 2,
+ },
+ {
+ name: 'High',
+ value: 3,
+ },
+ ],
+ },
+ {
+ displayName: 'Name',
+ name: 'name',
+ type: 'string',
+ default: '',
+ description: 'Name of the ticket author',
+ },
+ {
+ displayName: 'Requester Name/ID',
+ name: 'requester_id',
+ type: 'options',
+ description: 'Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ default: '',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getRequesters',
+ ],
+ },
+ },
+ ],
+ },
+
+ // ----------------------------------------
+ // ticket: delete
+ // ----------------------------------------
+ {
+ displayName: 'Ticket ID',
+ name: 'ticketId',
+ description: 'ID of the ticket to delete',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'ticket',
+ ],
+ operation: [
+ 'delete',
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // ticket: get
+ // ----------------------------------------
+ {
+ displayName: 'Ticket ID',
+ name: 'ticketId',
+ description: 'ID of the ticket to retrieve',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'ticket',
+ ],
+ operation: [
+ 'get',
+ ],
+ },
+ },
+ },
+
+ // ----------------------------------------
+ // ticket: getAll
+ // ----------------------------------------
+ {
+ displayName: 'Return All',
+ name: 'returnAll',
+ type: 'boolean',
+ default: false,
+ description: 'Whether to return all results or only up to a given limit',
+ displayOptions: {
+ show: {
+ resource: [
+ 'ticket',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Limit',
+ name: 'limit',
+ type: 'number',
+ default: 50,
+ description: 'How many results to return',
+ typeOptions: {
+ minValue: 1,
+ },
+ displayOptions: {
+ show: {
+ resource: [
+ 'ticket',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ returnAll: [
+ false,
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Filters',
+ name: 'filters',
+ type: 'collection',
+ placeholder: 'Add Filter',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'ticket',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Agent Name/ID',
+ name: 'agent_id',
+ type: 'options',
+ default: '',
+ description: 'ID of the agent to whom the tickets have been assigned. Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getAgents',
+ ],
+ },
+ },
+ {
+ displayName: 'Group Name/ID',
+ name: 'group_id',
+ type: 'options',
+ default: '',
+ description: 'ID of the group to which the tickets have been assigned. Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getAgentGroups',
+ ],
+ },
+ },
+ {
+ displayName: 'Impact',
+ name: 'impact',
+ type: 'options',
+ default: 1,
+ options: [
+ {
+ name: 'Low',
+ value: 1,
+ },
+ {
+ name: 'Medium',
+ value: 2,
+ },
+ {
+ name: 'High',
+ value: 3,
+ },
+ ],
+ },
+ {
+ displayName: 'Priority',
+ name: 'priority',
+ type: 'options',
+ default: 1,
+ options: [
+ {
+ name: 'Low',
+ value: 1,
+ },
+ {
+ name: 'Medium',
+ value: 2,
+ },
+ {
+ name: 'High',
+ value: 3,
+ },
+ {
+ name: 'Urgent',
+ value: 4,
+ },
+ ],
+ },
+ {
+ displayName: 'Status',
+ name: 'status',
+ type: 'options',
+ default: 2,
+ options: [
+ {
+ name: 'Open',
+ value: 2,
+ },
+ {
+ name: 'Pending',
+ value: 3,
+ },
+ {
+ name: 'Resolved',
+ value: 4,
+ },
+ {
+ name: 'Closed',
+ value: 5,
+ },
+ ],
+ },
+ {
+ displayName: 'Created On',
+ name: 'created_at',
+ type: 'dateTime',
+ default: '',
+ description: 'Date when the ticket was created',
+ },
+ {
+ displayName: 'Due By',
+ name: 'due_by',
+ description: 'Date when the ticket is due to be resolved',
+ type: 'dateTime',
+ default: '',
+ },
+ ],
+ },
+
+ // ----------------------------------------
+ // ticket: update
+ // ----------------------------------------
+ {
+ displayName: 'Ticket ID',
+ name: 'ticketId',
+ description: 'ID of the ticket to update',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'ticket',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Update Fields',
+ name: 'updateFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'ticket',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Department Name/ID',
+ name: 'department_id',
+ type: 'options',
+ default: '',
+ description: 'ID of the department to which this ticket has been assigned. Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getDepartments',
+ ],
+ },
+ },
+ {
+ displayName: 'Description',
+ name: 'description',
+ type: 'string',
+ default: '',
+ description: 'HTML supported',
+ },
+ {
+ displayName: 'Email',
+ name: 'email',
+ type: 'string',
+ default: '',
+ description: 'Email address of the ticket author',
+ },
+ {
+ displayName: 'Group Name/ID',
+ name: 'group_id',
+ type: 'options',
+ default: '',
+ description: 'ID of the group to which the ticket has been assigned. Choose from the list or specify an ID. You can also specify the ID using an expression.',
+ typeOptions: {
+ loadOptionsMethod: [
+ 'getAgentGroups',
+ ],
+ },
+ },
+ {
+ displayName: 'Impact',
+ name: 'impact',
+ type: 'options',
+ default: 1,
+ options: [
+ {
+ name: 'Low',
+ value: 1,
+ },
+ {
+ name: 'Medium',
+ value: 2,
+ },
+ {
+ name: 'High',
+ value: 3,
+ },
+ ],
+ },
+ {
+ displayName: 'Name',
+ name: 'name',
+ type: 'string',
+ default: '',
+ description: 'Name of the ticket author',
+ },
+ {
+ displayName: 'Phone',
+ name: 'phone',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'Priority',
+ name: 'priority',
+ type: 'options',
+ default: 1,
+ description: 'Priority of the ticket',
+ options: [
+ {
+ name: 'Low',
+ value: 1,
+ },
+ {
+ name: 'Medium',
+ value: 2,
+ },
+ {
+ name: 'High',
+ value: 3,
+ },
+ {
+ name: 'Urgent',
+ value: 4,
+ },
+ ],
+ },
+ {
+ displayName: 'Status',
+ name: 'status',
+ type: 'options',
+ default: 2,
+ options: [
+ {
+ name: 'Open',
+ value: 2,
+ },
+ {
+ name: 'Pending',
+ value: 3,
+ },
+ {
+ name: 'Resolved',
+ value: 4,
+ },
+ {
+ name: 'Closed',
+ value: 5,
+ },
+ ],
+ },
+ {
+ displayName: 'Subject',
+ name: 'subject',
+ type: 'string',
+ default: '',
+ },
+ ],
+ },
+] as INodeProperties[];
diff --git a/packages/nodes-base/nodes/Freshservice/descriptions/index.ts b/packages/nodes-base/nodes/Freshservice/descriptions/index.ts
new file mode 100644
index 0000000000..cc2f6a8916
--- /dev/null
+++ b/packages/nodes-base/nodes/Freshservice/descriptions/index.ts
@@ -0,0 +1,16 @@
+export * from './AgentDescription';
+export * from './AgentGroupDescription';
+export * from './AgentRoleDescription';
+export * from './AnnouncementDescription';
+export * from './AssetDescription';
+export * from './AssetTypeDescription';
+export * from './ChangeDescription';
+export * from './DepartmentDescription';
+export * from './LocationDescription';
+export * from './ProblemDescription';
+export * from './ProductDescription';
+export * from './ReleaseDescription';
+export * from './RequesterDescription';
+export * from './RequesterGroupDescription';
+export * from './SoftwareDescription';
+export * from './TicketDescription';
diff --git a/packages/nodes-base/nodes/Freshservice/freshservice.svg b/packages/nodes-base/nodes/Freshservice/freshservice.svg
new file mode 100644
index 0000000000..f806e022d7
--- /dev/null
+++ b/packages/nodes-base/nodes/Freshservice/freshservice.svg
@@ -0,0 +1,16 @@
+
+
diff --git a/packages/nodes-base/nodes/Freshservice/types.d.ts b/packages/nodes-base/nodes/Freshservice/types.d.ts
new file mode 100644
index 0000000000..764a62ea27
--- /dev/null
+++ b/packages/nodes-base/nodes/Freshservice/types.d.ts
@@ -0,0 +1,32 @@
+import { IDataObject } from 'n8n-workflow';
+
+export type FreshserviceCredentials = {
+ apiKey: string;
+ domain: string;
+};
+
+export type LoadedResource = {
+ id: string;
+ name: string;
+};
+
+export type LoadedUser = {
+ active: boolean;
+ id: string;
+ first_name: string;
+ last_name?: string;
+};
+
+export type RolesParameter = IDataObject & {
+ roleProperties: Array<{
+ role: number;
+ assignment_scope: 'entire_helpdesk' | 'member_groups' | 'specified_groups' | 'assigned_items';
+ groups?: number[];
+ }>
+};
+
+export type AddressFixedCollection = {
+ address?: {
+ addressFields: object
+ }
+}
diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json
index 68b11df490..18129f8d3f 100644
--- a/packages/nodes-base/package.json
+++ b/packages/nodes-base/package.json
@@ -87,6 +87,7 @@
"dist/credentials/FacebookGraphApi.credentials.js",
"dist/credentials/FacebookGraphAppApi.credentials.js",
"dist/credentials/FreshdeskApi.credentials.js",
+ "dist/credentials/FreshserviceApi.credentials.js",
"dist/credentials/FreshworksCrmApi.credentials.js",
"dist/credentials/FileMaker.credentials.js",
"dist/credentials/FlowApi.credentials.js",
@@ -383,6 +384,7 @@
"dist/nodes/Facebook/FacebookGraphApi.node.js",
"dist/nodes/Facebook/FacebookTrigger.node.js",
"dist/nodes/FileMaker/FileMaker.node.js",
+ "dist/nodes/Freshservice/Freshservice.node.js",
"dist/nodes/Ftp.node.js",
"dist/nodes/Freshdesk/Freshdesk.node.js",
"dist/nodes/FreshworksCrm/FreshworksCrm.node.js",