n8n/packages/nodes-base/nodes/Cisco/Webex/CiscoWebexTrigger.node.ts
Omar Ajoue 7ce7285f7a
Load credentials from the database (#1741)
* 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>
2021-08-20 18:57:30 +02:00

689 lines
15 KiB
TypeScript

import {
IHookFunctions,
IWebhookFunctions,
} from 'n8n-core';
import {
IDataObject,
INodeType,
INodeTypeDescription,
IWebhookResponseData,
NodeOperationError,
} from 'n8n-workflow';
import {
getAutomaticSecret,
getEvents,
mapResource,
webexApiRequest,
webexApiRequestAllItems,
} from './GenericFunctions';
import {
createHmac,
} from 'crypto';
export class CiscoWebexTrigger implements INodeType {
description: INodeTypeDescription = {
displayName: 'Webex by Cisco Trigger',
name: 'ciscoWebexTrigger',
icon: 'file:ciscoWebex.png',
group: ['trigger'],
version: 1,
subtitle: '={{$parameter["resource"] + ":" + $parameter["event"]}}',
description: 'Starts the workflow when Cisco Webex events occur.',
defaults: {
name: 'Webex Trigger',
color: '#29b6f6',
},
inputs: [],
outputs: ['main'],
credentials: [
{
name: 'ciscoWebexOAuth2Api',
required: true,
},
],
webhooks: [
{
name: 'default',
httpMethod: 'POST',
responseMode: 'onReceived',
path: 'webhook',
},
],
properties: [
{
displayName: 'Resource',
name: 'resource',
type: 'options',
options: [
{
name: 'Attachment Action',
value: 'attachmentAction',
},
{
name: 'Meeting',
value: 'meeting',
},
{
name: 'Membership',
value: 'membership',
},
{
name: 'Message',
value: 'message',
},
// {
// name: 'Telephony Call',
// value: 'telephonyCall',
// },
{
name: 'Recording',
value: 'recording',
},
{
name: 'Room',
value: 'room',
},
{
name: '*',
value: 'all',
},
],
default: 'meeting',
required: true,
},
...getEvents(),
{
displayName: 'Resolve Data',
name: 'resolveData',
type: 'boolean',
displayOptions: {
show: {
resource: [
'attachmentAction',
],
},
},
default: true,
description: 'By default the response only contain a reference to the data the user inputed<br />If this option gets activated it will resolve the data automatically.',
},
{
displayName: 'Filters',
name: 'filters',
type: 'collection',
placeholder: 'Add Filter',
default: {},
options: [
{
displayName: 'Has Files',
name: 'hasFiles',
type: 'boolean',
displayOptions: {
show: {
'/resource': [
'message',
],
'/event': [
'created',
'deleted',
],
},
},
default: false,
description: 'Limit to messages which contain file content attachments',
},
{
displayName: 'Is Locked',
name: 'isLocked',
type: 'boolean',
displayOptions: {
show: {
'/resource': [
'room',
],
'/event': [
'created',
'updated',
],
},
},
default: false,
description: 'Limit to rooms that are locked',
},
{
displayName: 'Is Moderator',
name: 'isModerator',
type: 'boolean',
displayOptions: {
show: {
'/resource': [
'membership',
],
'/event': [
'created',
'updated',
'deleted',
],
},
},
default: false,
description: 'Limit to moderators of a room',
},
{
displayName: 'Mentioned People',
name: 'mentionedPeople',
type: 'string',
displayOptions: {
show: {
'/resource': [
'message',
],
'/event': [
'created',
'deleted',
],
},
},
default: '',
description: `Limit to messages which contain these mentioned people, by person ID; accepts me as a shorthand for your own person ID; separate multiple values with commas`,
},
{
displayName: 'Message ID',
name: 'messageId',
type: 'string',
displayOptions: {
show: {
'/resource': [
'attachmentAction',
],
'/event': [
'created',
],
},
},
default: '',
description: 'Limit to a particular message, by ID',
},
{
displayName: 'Owned By',
name: 'ownedBy',
displayOptions: {
show: {
'/resource': [
'meeting',
],
},
},
type: 'string',
default: '',
},
{
displayName: 'Person Email',
name: 'personEmail',
type: 'string',
displayOptions: {
show: {
'/resource': [
'membership',
],
'/event': [
'created',
'updated',
'deleted',
],
},
},
default: '',
description: 'Limit to a particular person, by email',
},
{
displayName: 'Person Email',
name: 'personEmail',
type: 'string',
displayOptions: {
show: {
'/resource': [
'message',
],
'/event': [
'created',
'deleted',
],
},
},
default: '',
description: 'Limit to a particular person, by email',
},
{
displayName: 'Person ID',
name: 'personId',
type: 'string',
displayOptions: {
show: {
'/resource': [
'attachmentAction',
],
'/event': [
'created',
],
},
},
default: '',
description: 'Limit to a particular person, by ID',
},
{
displayName: 'Person ID',
name: 'personId',
type: 'string',
displayOptions: {
show: {
'/resource': [
'membership',
],
'/event': [
'created',
'updated',
'deleted',
],
},
},
default: '',
description: 'Limit to a particular person, by ID',
},
{
displayName: 'Person ID',
name: 'personId',
type: 'string',
displayOptions: {
show: {
'/resource': [
'message',
],
'/event': [
'created',
'deleted',
],
},
},
default: '',
description: 'Limit to a particular person, by ID',
},
{
displayName: 'Room ID',
name: 'roomId',
type: 'string',
displayOptions: {
show: {
'/resource': [
'attachmentAction',
],
'/event': [
'created',
],
},
},
default: '',
description: 'Limit to a particular room, by ID',
},
{
displayName: 'Room ID',
name: 'roomId',
type: 'string',
displayOptions: {
show: {
'/resource': [
'membership',
],
'/event': [
'created',
'updated',
'deleted',
],
},
},
default: '',
description: 'Limit to a particular room, by ID',
},
{
displayName: 'Room ID',
name: 'roomId',
type: 'string',
displayOptions: {
show: {
'/resource': [
'message',
],
'/event': [
'created',
'updated',
],
},
},
default: '',
description: 'Limit to a particular room, by ID',
},
{
displayName: 'Room Type',
name: 'roomType',
type: 'options',
options: [
{
name: 'Direct',
value: 'direct',
},
{
name: 'Group',
value: 'group',
},
],
displayOptions: {
show: {
'/resource': [
'message',
],
'/event': [
'created',
'deleted',
],
},
},
default: '',
description: `Limit to a particular room type`,
},
{
displayName: 'Type',
name: 'type',
type: 'options',
options: [
{
name: 'Direct',
value: 'direct',
},
{
name: 'Group',
value: 'group',
},
],
displayOptions: {
show: {
'/resource': [
'room',
],
'/event': [
'created',
'updated',
],
},
},
default: '',
description: `Limit to a particular room type`,
},
// {
// displayName: 'Call Type',
// name: 'callType',
// type: 'options',
// options: [
// {
// name: 'Emergency',
// value: 'emergency',
// },
// {
// name: 'External',
// value: 'external',
// },
// {
// name: 'Location',
// value: 'location',
// },
// {
// name: 'Disconnected',
// value: 'disconnected',
// },
// {
// name: 'Organization',
// value: 'organization',
// },
// {
// name: 'Other',
// value: 'other',
// },
// {
// name: 'Repair',
// value: 'repair',
// },
// ],
// displayOptions: {
// show: {
// '/resource': [
// 'telephonyCall',
// ],
// '/event': [
// 'created',
// 'deleted',
// 'updated',
// ],
// },
// },
// default: '',
// description: `Limit to a particular call type`,
// },
// {
// displayName: 'Person ID',
// name: 'personId',
// type: 'string',
// displayOptions: {
// show: {
// '/resource': [
// 'telephonyCall',
// ],
// '/event': [
// 'created',
// 'deleted',
// 'updated',
// ],
// },
// },
// default: '',
// description: 'Limit to a particular person, by ID',
// },
// {
// displayName: 'Personality',
// name: 'personality',
// type: 'options',
// options: [
// {
// name: 'Click To Dial',
// value: 'clickToDial',
// },
// {
// name: 'Originator',
// value: 'originator',
// },
// {
// name: 'Terminator',
// value: 'terminator',
// },
// ],
// displayOptions: {
// show: {
// '/resource': [
// 'telephonyCall',
// ],
// '/event': [
// 'created',
// 'deleted',
// 'updated',
// ],
// },
// },
// default: '',
// description: `Limit to a particular call personality`,
// },
// {
// displayName: 'State',
// name: 'state',
// type: 'options',
// options: [
// {
// name: 'Alerting',
// value: 'alerting',
// },
// {
// name: 'Connected',
// value: 'connected',
// },
// {
// name: 'Connecting',
// value: 'connecting',
// },
// {
// name: 'Disconnected',
// value: 'disconnected',
// },
// {
// name: 'Held',
// value: 'held',
// },
// {
// name: 'Remote Held',
// value: 'remoteHeld',
// },
// ],
// displayOptions: {
// show: {
// '/resource': [
// 'telephonyCall',
// ],
// '/event': [
// 'created',
// 'deleted',
// 'updated',
// ],
// },
// },
// default: '',
// description: `Limit to a particular call state`,
// },
],
},
],
};
// @ts-ignore (because of request)
webhookMethods = {
default: {
async checkExists(this: IHookFunctions): Promise<boolean> {
const webhookUrl = this.getNodeWebhookUrl('default');
const webhookData = this.getWorkflowStaticData('node');
const resource = this.getNodeParameter('resource') as string;
const event = this.getNodeParameter('event') as string;
// Check all the webhooks which exist already if it is identical to the
// one that is supposed to get created.
const data = await webexApiRequestAllItems.call(this, 'items', 'GET', '/webhooks');
for (const webhook of data) {
if (webhook.url === webhookUrl
&& webhook.resource === mapResource(resource)
&& webhook.event === event
&& webhook.status === 'active') {
webhookData.webhookId = webhook.id as string;
return true;
}
}
return false;
},
async create(this: IHookFunctions): Promise<boolean> {
const webhookData = this.getWorkflowStaticData('node');
const webhookUrl = this.getNodeWebhookUrl('default');
const event = this.getNodeParameter('event') as string;
const resource = this.getNodeParameter('resource') as string;
const filters = this.getNodeParameter('filters', {}) as IDataObject;
const credentials = await this.getCredentials('ciscoWebexOAuth2Api');
if (credentials === undefined) {
throw new NodeOperationError(this.getNode(), 'Credentials could not be obtained');
}
const secret = getAutomaticSecret(credentials);
const filter = [];
for (const key of Object.keys(filters)) {
if (key !== 'ownedBy') {
filter.push(`${key}=${filters[key]}`);
}
}
const endpoint = '/webhooks';
const body: IDataObject = {
name: `n8n-webhook:${webhookUrl}`,
targetUrl: webhookUrl,
event,
resource: mapResource(resource),
};
if (filters.ownedBy) {
body['ownedBy'] = filters.ownedBy as string;
}
body['secret'] = secret;
if (filter.length) {
body['filter'] = filter.join('&');
}
const responseData = await webexApiRequest.call(this, 'POST', endpoint, body);
if (responseData.id === undefined) {
// Required data is missing so was not successful
return false;
}
webhookData.webhookId = responseData.id as string;
webhookData.secret = secret;
return true;
},
async delete(this: IHookFunctions): Promise<boolean> {
const webhookData = this.getWorkflowStaticData('node');
if (webhookData.webhookId !== undefined) {
const endpoint = `/webhooks/${webhookData.webhookId}`;
try {
await webexApiRequest.call(this, 'DELETE', endpoint);
} catch (error) {
return false;
}
// Remove from the static workflow data so that it is clear
// that no webhooks are registred anymore
delete webhookData.webhookId;
}
return true;
},
},
};
async webhook(this: IWebhookFunctions): Promise<IWebhookResponseData> {
let bodyData = this.getBodyData();
const webhookData = this.getWorkflowStaticData('node');
const headers = this.getHeaderData() as IDataObject;
const req = this.getRequestObject();
const resolveData = this.getNodeParameter('resolveData', false) as boolean;
//@ts-ignore
const computedSignature = createHmac('sha1', webhookData.secret).update(req.rawBody).digest('hex');
if (headers['x-spark-signature'] !== computedSignature) {
return {};
}
if (resolveData) {
const { data: { id } } = bodyData as { data: { id: string } };
bodyData = await webexApiRequest.call(this, 'GET', `/attachment/actions/${id}`);
}
return {
workflowData: [
this.helpers.returnJsonArray(bodyData),
],
};
}
}