diff --git a/packages/nodes-base/credentials/AcuitySchedulingApi.credentials.ts b/packages/nodes-base/credentials/AcuitySchedulingApi.credentials.ts new file mode 100644 index 0000000000..5ee2344ef1 --- /dev/null +++ b/packages/nodes-base/credentials/AcuitySchedulingApi.credentials.ts @@ -0,0 +1,23 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +export class AcuitySchedulingApi implements ICredentialType { + name = 'acuitySchedulingApi'; + displayName = 'Acuity Scheduling API'; + properties = [ + { + displayName: 'User ID', + name: 'userId', + type: 'string' as NodePropertyTypes, + default: '', + }, + { + displayName: 'API Key', + name: 'apiKey', + type: 'string' as NodePropertyTypes, + default: '', + }, + ]; +} diff --git a/packages/nodes-base/nodes/AcuityScheduling/AcuitySchedulingTrigger.node.ts b/packages/nodes-base/nodes/AcuityScheduling/AcuitySchedulingTrigger.node.ts new file mode 100644 index 0000000000..dc9a9b072d --- /dev/null +++ b/packages/nodes-base/nodes/AcuityScheduling/AcuitySchedulingTrigger.node.ts @@ -0,0 +1,136 @@ +import { + IHookFunctions, + IWebhookFunctions, +} from 'n8n-core'; + +import { + IDataObject, + INodeTypeDescription, + INodeType, + IWebhookResponseData, +} from 'n8n-workflow'; + +import { + acuitySchedulingApiRequest, +} from './GenericFunctions'; + +export class AcuitySchedulingTrigger implements INodeType { + description: INodeTypeDescription = { + displayName: 'Acuity Scheduling Trigger', + name: 'acuityScheduling', + icon: 'file:acuityScheduling.png', + group: ['trigger'], + version: 1, + description: 'Handle Acuity Scheduling events via webhooks', + defaults: { + name: 'Acuity Scheduling Trigger', + color: '#000000', + }, + inputs: [], + outputs: ['main'], + credentials: [ + { + name: 'acuitySchedulingApi', + required: true, + } + ], + webhooks: [ + { + name: 'default', + httpMethod: 'POST', + responseMode: 'onReceived', + path: 'webhook', + }, + ], + properties: [ + { + displayName: 'Event', + name: 'event', + type: 'options', + required: true, + default: '', + options: [ + { + name: 'appointment.scheduled', + value: 'appointment.scheduled', + description: 'is called once when an appointment is initially booked', + }, + { + name: 'appointment.rescheduled', + value: 'appointment.rescheduled', + description: 'is called when the appointment is rescheduled to a new time', + }, + { + name: 'appointment.canceled', + value: 'appointment.canceled', + description: 'is called whenever an appointment is canceled', + }, + { + name: 'appointment.changed', + value: 'appointment.changed', + description: 'is called when the appointment is changed in any way', + }, + { + name: 'order.completed', + value: 'order.completed', + description: 'is called when an order is completed', + }, + ], + }, + ], + }; + // @ts-ignore + webhookMethods = { + default: { + async checkExists(this: IHookFunctions): Promise { + const webhookData = this.getWorkflowStaticData('node'); + if (webhookData.webhookId === undefined) { + return false; + } + const endpoint = '/webhooks'; + const webhooks = await acuitySchedulingApiRequest.call(this, 'GET', endpoint); + if (Array.isArray(webhooks)) { + for (const webhook of webhooks) { + if (webhook.id === webhookData.webhookId) { + return true; + } + } + } + return false; + }, + async create(this: IHookFunctions): Promise { + const webhookUrl = this.getNodeWebhookUrl('default'); + const webhookData = this.getWorkflowStaticData('node'); + const event = this.getNodeParameter('event') as string; + const endpoint = '/webhooks'; + const body: IDataObject = { + target: webhookUrl, + event, + }; + const { id } = await acuitySchedulingApiRequest.call(this, 'POST', endpoint, body); + webhookData.webhookId = id; + return true; + }, + async delete(this: IHookFunctions): Promise { + const webhookData = this.getWorkflowStaticData('node'); + const endpoint = `/webhooks/${webhookData.webhookId}`; + try { + await acuitySchedulingApiRequest.call(this, 'DELETE', endpoint); + } catch(error) { + return false; + } + delete webhookData.webhookId; + return true; + }, + }, + }; + + async webhook(this: IWebhookFunctions): Promise { + const req = this.getRequestObject(); + return { + workflowData: [ + this.helpers.returnJsonArray(req.body), + ], + }; + } +} diff --git a/packages/nodes-base/nodes/AcuityScheduling/GenericFunctions.ts b/packages/nodes-base/nodes/AcuityScheduling/GenericFunctions.ts new file mode 100644 index 0000000000..e14ff589d5 --- /dev/null +++ b/packages/nodes-base/nodes/AcuityScheduling/GenericFunctions.ts @@ -0,0 +1,34 @@ +import { OptionsWithUri } from 'request'; +import { + IExecuteFunctions, + IExecuteSingleFunctions, + IHookFunctions, + ILoadOptionsFunctions, + IWebhookFunctions, +} from 'n8n-core'; +import { IDataObject } from 'n8n-workflow'; + +export async function acuitySchedulingApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IWebhookFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise { // tslint:disable-line:no-any + const credentials = this.getCredentials('acuitySchedulingApi'); + if (credentials === undefined) { + throw new Error('No credentials got returned!'); + } + + const base64Key = Buffer.from(`${credentials.userId}:${credentials.apiKey}`).toString('base64'); + let options: OptionsWithUri = { + headers: { + Authorization: `Basic ${base64Key}`, + 'Content-Type': 'application/json', + }, + method, + qs, + body, + uri: uri ||`https://acuityscheduling.com/api/v1${resource}`, + json: true + }; + try { + return await this.helpers.request!(options); + } catch (error) { + throw new Error('Acuity Scheduling Error: ' + error.message); + } +} diff --git a/packages/nodes-base/nodes/AcuityScheduling/acuityScheduling.png b/packages/nodes-base/nodes/AcuityScheduling/acuityScheduling.png new file mode 100644 index 0000000000..df087b60e8 Binary files /dev/null and b/packages/nodes-base/nodes/AcuityScheduling/acuityScheduling.png differ diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 829d20caf4..e72da6de67 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -27,6 +27,7 @@ "n8n": { "credentials": [ "dist/credentials/ActiveCampaignApi.credentials.js", + "dist/credentials/AcuitySchedulingApi.credentials.js", "dist/credentials/AirtableApi.credentials.js", "dist/credentials/Amqp.credentials.js", "dist/credentials/AsanaApi.credentials.js", @@ -86,6 +87,7 @@ "dist/nodes/ActiveCampaign/ActiveCampaign.node.js", "dist/nodes/ActiveCampaign/ActiveCampaignTrigger.node.js", "dist/nodes/Airtable/Airtable.node.js", + "dist/nodes/AcuityScheduling/AcuitySchedulingTrigger.node.js", "dist/nodes/Amqp/Amqp.node.js", "dist/nodes/Amqp/AmqpTrigger.node.js", "dist/nodes/Asana/Asana.node.js",