mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-26 13:14:07 -08:00
7ce7285f7a
* Changes to types so that credentials can be always loaded from DB This first commit changes all return types from the execute functions and calls to get credentials to be async so we can use await. This is a first step as previously credentials were loaded in memory and always available. We will now be loading them from the DB which requires turning the whole call chain async. * Fix updated files * Removed unnecessary credential loading to improve performance * Fix typo * ⚡ Fix issue * Updated new nodes to load credentials async * ⚡ Remove not needed comment Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com>
257 lines
5.6 KiB
TypeScript
257 lines
5.6 KiB
TypeScript
import {
|
|
ICredentialDataDecryptedObject,
|
|
IDataObject,
|
|
ILoadOptionsFunctions,
|
|
INodePropertyOptions,
|
|
INodeType,
|
|
INodeTypeDescription,
|
|
IWebhookFunctions,
|
|
IWebhookResponseData,
|
|
} from 'n8n-workflow';
|
|
|
|
import {
|
|
IHookFunctions,
|
|
} from 'n8n-core';
|
|
|
|
import {
|
|
getAutomaticSecret,
|
|
taigaApiRequest,
|
|
} from './GenericFunctions';
|
|
|
|
// import {
|
|
// createHmac,
|
|
// } from 'crypto';
|
|
|
|
export class TaigaTrigger implements INodeType {
|
|
description: INodeTypeDescription = {
|
|
displayName: 'Taiga Trigger',
|
|
name: 'taigaTrigger',
|
|
icon: 'file:taiga.svg',
|
|
group: ['trigger'],
|
|
version: 1,
|
|
subtitle: '={{"project:" + $parameter["projectSlug"]}}',
|
|
description: 'Handle Taiga events via webhook',
|
|
defaults: {
|
|
name: 'Taiga Trigger',
|
|
color: '#772244',
|
|
},
|
|
inputs: [],
|
|
outputs: ['main'],
|
|
credentials: [
|
|
{
|
|
name: 'taigaApi',
|
|
required: true,
|
|
},
|
|
],
|
|
webhooks: [
|
|
{
|
|
name: 'default',
|
|
httpMethod: 'POST',
|
|
responseMode: 'onReceived',
|
|
path: 'webhook',
|
|
},
|
|
],
|
|
properties: [
|
|
{
|
|
displayName: 'Project ID',
|
|
name: 'projectId',
|
|
type: 'options',
|
|
typeOptions: {
|
|
loadOptionsMethod: 'getUserProjects',
|
|
},
|
|
default: '',
|
|
description: 'Project ID',
|
|
required: true,
|
|
},
|
|
{
|
|
displayName: 'Resources',
|
|
name: 'resources',
|
|
type: 'multiOptions',
|
|
required: true,
|
|
default: [
|
|
'all',
|
|
],
|
|
options: [
|
|
{
|
|
name: 'All',
|
|
value: 'all',
|
|
},
|
|
{
|
|
name: 'Issue',
|
|
value: 'issue',
|
|
},
|
|
{
|
|
name: 'Milestone (Sprint)',
|
|
value: 'milestone',
|
|
},
|
|
{
|
|
name: 'Task',
|
|
value: 'task',
|
|
},
|
|
{
|
|
name: 'User Story',
|
|
value: 'userstory',
|
|
},
|
|
{
|
|
name: 'Wikipage',
|
|
value: 'wikipage',
|
|
},
|
|
],
|
|
description: 'Resources to listen to',
|
|
},
|
|
{
|
|
displayName: 'Operations',
|
|
name: 'operations',
|
|
type: 'multiOptions',
|
|
required: true,
|
|
default: [
|
|
'all',
|
|
],
|
|
description: 'Operations to listen to',
|
|
options: [
|
|
{
|
|
name: 'All',
|
|
value: 'all',
|
|
},
|
|
{
|
|
name: 'Create',
|
|
value: 'create',
|
|
},
|
|
{
|
|
name: 'Delete',
|
|
value: 'delete',
|
|
},
|
|
{
|
|
name: 'Update',
|
|
value: 'change',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
};
|
|
|
|
methods = {
|
|
loadOptions: {
|
|
// Get all the available projects to display them to user so that he can
|
|
// select them easily
|
|
async getUserProjects(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
|
const returnData: INodePropertyOptions[] = [];
|
|
|
|
const { id } = await taigaApiRequest.call(this, 'GET', '/users/me');
|
|
|
|
const projects = await taigaApiRequest.call(this, 'GET', '/projects', {}, { member: id });
|
|
for (const project of projects) {
|
|
const projectName = project.name;
|
|
const projectId = project.id;
|
|
returnData.push({
|
|
name: projectName,
|
|
value: projectId,
|
|
});
|
|
}
|
|
return returnData;
|
|
},
|
|
},
|
|
};
|
|
|
|
// @ts-ignore
|
|
webhookMethods = {
|
|
default: {
|
|
async checkExists(this: IHookFunctions): Promise<boolean> {
|
|
const webhookUrl = this.getNodeWebhookUrl('default') as string;
|
|
|
|
const webhookData = this.getWorkflowStaticData('node');
|
|
|
|
const endpoint = `/webhooks`;
|
|
|
|
const webhooks = await taigaApiRequest.call(this, 'GET', endpoint);
|
|
|
|
for (const webhook of webhooks) {
|
|
if (webhook.url === webhookUrl) {
|
|
webhookData.webhookId = webhook.id;
|
|
webhookData.key = webhook.key;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
},
|
|
async create(this: IHookFunctions): Promise<boolean> {
|
|
const credentials = await this.getCredentials('taigaApi') as ICredentialDataDecryptedObject;
|
|
|
|
const webhookUrl = this.getNodeWebhookUrl('default') as string;
|
|
|
|
const webhookData = this.getWorkflowStaticData('node');
|
|
|
|
const projectId = this.getNodeParameter('projectId') as string;
|
|
|
|
const key = getAutomaticSecret(credentials);
|
|
|
|
const body: IDataObject = {
|
|
name: `n8n-webhook:${webhookUrl}`,
|
|
url: webhookUrl,
|
|
key,
|
|
project: projectId,
|
|
};
|
|
const { id } = await taigaApiRequest.call(this, 'POST', '/webhooks', body);
|
|
|
|
webhookData.webhookId = id;
|
|
webhookData.key = key;
|
|
|
|
return true;
|
|
},
|
|
async delete(this: IHookFunctions): Promise<boolean> {
|
|
const webhookData = this.getWorkflowStaticData('node');
|
|
try {
|
|
await taigaApiRequest.call(this, 'DELETE', `/webhooks/${webhookData.webhookId}`);
|
|
} catch (error) {
|
|
return false;
|
|
}
|
|
delete webhookData.webhookId;
|
|
delete webhookData.key;
|
|
return true;
|
|
},
|
|
},
|
|
};
|
|
|
|
async webhook(this: IWebhookFunctions): Promise<IWebhookResponseData> {
|
|
const body = this.getRequestObject().body as WebhookPayload;
|
|
|
|
const operations = this.getNodeParameter('operations', []) as Operations[];
|
|
const resources = this.getNodeParameter('resources', []) as Resources[];
|
|
|
|
if (!operations.includes('all') && !operations.includes(body.action)) {
|
|
return {};
|
|
}
|
|
|
|
if (!resources.includes('all') && !resources.includes(body.type)) {
|
|
return {};
|
|
}
|
|
|
|
// TODO: Signature does not match payload hash
|
|
// https://github.com/taigaio/taiga-back/issues/1031
|
|
|
|
// const webhookData = this.getWorkflowStaticData('node');
|
|
// const headerData = this.getHeaderData();
|
|
|
|
// // @ts-ignore
|
|
// const requestSignature = headerData['x-taiga-webhook-signature'];
|
|
// console.log(requestSignature);
|
|
|
|
// if (requestSignature === undefined) {
|
|
// return {};
|
|
// }
|
|
|
|
// const computedSignature = createHmac('sha1', webhookData.key as string).update(JSON.stringify(body)).digest('hex');
|
|
|
|
// if (requestSignature !== computedSignature) {
|
|
// return {};
|
|
// }
|
|
|
|
return {
|
|
workflowData: [
|
|
this.helpers.returnJsonArray(body),
|
|
],
|
|
};
|
|
}
|
|
}
|