n8n/packages/nodes-base/nodes/Freshservice/GenericFunctions.ts
Iván Ovejero 65820b3b54
Add Freshservice node (#2090)
*  Create Freshservice node

* 👕 Fix lintings

*  Adjust from agent to department

*  Adjust from location to ticket

* 👕 Fix lintings

* ✏️ Improve descriptions

* 🔥 Remove logging

* 🔥 Remove unused helper

* ✏️ Fix helper documentation

*  Simplify roles in agent:create

* 🔥 Remove logging

*  Minor improvements

* ✏️ Adjust dynamic lists descriptions

*  Set default values for problem:create

*  Set default values for change:create

*  Handle deletion with empty response

*  Update getCredentials call to new style

* ✏️ Reword multiOptions descriptions

*  Add special handling for validation errors

* 🔥 Remove concatenated name from filters

*  Fix additional params in announcement:create

* ✏️ Clarify asset display ID vs asset ID

*  Fix asset:update arg typo

*  Fix predefined filters in change:getAll

*  Fix software status options

* ✏️ Reword created_at in ticket:getAll

*  Add status to ticket:update

* 👕 Fix lint

*  Improvements

*  Minor improvements

Co-authored-by: ricardo <ricardoespinoza105@gmail.com>
Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com>
2021-09-05 19:24:56 +02:00

244 lines
5.7 KiB
TypeScript

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;
}