diff --git a/packages/nodes-base/credentials/MauticApi.credentials.ts b/packages/nodes-base/credentials/MauticApi.credentials.ts
new file mode 100644
index 0000000000..28938b2443
--- /dev/null
+++ b/packages/nodes-base/credentials/MauticApi.credentials.ts
@@ -0,0 +1,29 @@
+import {
+ ICredentialType,
+ NodePropertyTypes,
+} from 'n8n-workflow';
+
+export class MauticApi implements ICredentialType {
+ name = 'mauticApi';
+ displayName = 'Mautic API';
+ properties = [
+ {
+ displayName: 'URL',
+ name: 'url',
+ type: 'string' as NodePropertyTypes,
+ default: '',
+ },
+ {
+ displayName: 'Username',
+ name: 'username',
+ type: 'string' as NodePropertyTypes,
+ default: '',
+ },
+ {
+ displayName: 'Password',
+ name: 'password',
+ type: 'string' as NodePropertyTypes,
+ default: '',
+ },
+ ];
+}
diff --git a/packages/nodes-base/nodes/Mautic/ContactDescription.ts b/packages/nodes-base/nodes/Mautic/ContactDescription.ts
new file mode 100644
index 0000000000..092690e7d3
--- /dev/null
+++ b/packages/nodes-base/nodes/Mautic/ContactDescription.ts
@@ -0,0 +1,549 @@
+import { INodeProperties } from "n8n-workflow";
+
+export const contactOperations = [
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ displayOptions: {
+ show: {
+ resource: [
+ 'contact',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Create',
+ value: 'create',
+ description: 'Create a new contact',
+ },
+ {
+ name: 'Update',
+ value: 'update',
+ description: 'Update a contact',
+ },
+ {
+ name: 'Get',
+ value: 'get',
+ description: 'Get data of a contact',
+ },
+ {
+ name: 'Get All',
+ value: 'getAll',
+ description: 'Get data of all contacts',
+ },
+ {
+ name: 'Delete',
+ value: 'delete',
+ description: 'Delete a contact',
+ },
+ ],
+ default: 'create',
+ description: 'The operation to perform.',
+ },
+] as INodeProperties[];
+
+export const contactFields = [
+
+/* -------------------------------------------------------------------------- */
+/* contact:create */
+/* -------------------------------------------------------------------------- */
+ {
+ displayName: 'JSON Parameters',
+ name: 'jsonParameters',
+ type: 'boolean',
+ default: false,
+ description: '',
+ displayOptions: {
+ show: {
+ operation: [
+ 'create',
+ ],
+ resource: [
+ 'contact',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'First Name',
+ name: 'firstName',
+ type: 'string',
+ displayOptions: {
+ show: {
+ resource: [
+ 'contact',
+ ],
+ operation: [
+ 'create',
+ ],
+ jsonParameters: [
+ false,
+ ]
+ },
+ },
+ default: '',
+ description: 'First Name',
+ },
+ {
+ displayName: 'Last Name',
+ name: 'lastName',
+ type: 'string',
+ displayOptions: {
+ show: {
+ resource: [
+ 'contact',
+ ],
+ operation: [
+ 'create',
+ ],
+ jsonParameters: [
+ false,
+ ],
+ },
+ },
+ default: '',
+ description: 'LastName',
+ },
+ {
+ displayName: 'Primary Company',
+ name: 'company',
+ type: 'options',
+ typeOptions: {
+ loadOptionsMethod: 'getCompanies',
+ },
+ displayOptions: {
+ show: {
+ resource: [
+ 'contact',
+ ],
+ operation: [
+ 'create',
+ ],
+ jsonParameters: [
+ false,
+ ],
+ },
+ },
+ default: '',
+ description: 'Primary company',
+ },
+ {
+ displayName: 'Position',
+ name: 'position',
+ type: 'string',
+ displayOptions: {
+ show: {
+ resource: [
+ 'contact',
+ ],
+ operation: [
+ 'create',
+ ],
+ jsonParameters: [
+ false,
+ ],
+ },
+ },
+ default: '',
+ description: 'Position',
+ },
+ {
+ displayName: 'Title',
+ name: 'title',
+ type: 'string',
+ displayOptions: {
+ show: {
+ resource: [
+ 'contact',
+ ],
+ operation: [
+ 'create',
+ ],
+ jsonParameters: [
+ false,
+ ],
+ },
+ },
+ default: '',
+ description: 'Title',
+ },
+ {
+ displayName: 'Body',
+ name: 'bodyJson',
+ type: 'json',
+ displayOptions: {
+ show: {
+ operation: [
+ 'create',
+ ],
+ resource: [
+ 'contact',
+ ],
+ jsonParameters: [
+ true,
+ ],
+ },
+ },
+ default: '',
+ description: 'Contact parameters',
+ },
+ {
+ displayName: 'Additional Fields',
+ name: 'additionalFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'contact',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'IP Address',
+ name: 'ipAddress',
+ type: 'string',
+ default: '',
+ description: 'IP address to associate with the contact',
+ },
+ {
+ displayName: 'Last Active',
+ name: 'lastActive',
+ type: 'dateTime',
+ default: '',
+ description: 'Date/time in UTC;',
+ },
+ {
+ displayName: 'Owner ID',
+ name: 'ownerId',
+ type: 'string',
+ default: '',
+ description: 'ID of a Mautic user to assign this contact to',
+ },
+ ],
+ },
+
+/* -------------------------------------------------------------------------- */
+/* contact:update */
+/* -------------------------------------------------------------------------- */
+ {
+ displayName: 'Contact ID',
+ name: 'contactId',
+ type: 'string',
+ displayOptions: {
+ show: {
+ operation: [
+ 'update',
+ ],
+ resource: [
+ 'contact',
+ ],
+ },
+ },
+ default: '',
+ description: 'Contact ID',
+ },
+ {
+ displayName: 'JSON Parameters',
+ name: 'jsonParameters',
+ type: 'boolean',
+ default: false,
+ description: '',
+ displayOptions: {
+ show: {
+ operation: [
+ 'update',
+ ],
+ resource: [
+ 'contact',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Update Fields',
+ name: 'updateFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'contact',
+ ],
+ operation: [
+ 'update',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Body',
+ name: 'bodyJson',
+ type: 'json',
+ displayOptions: {
+ show: {
+ '/jsonParameters': [
+ true,
+ ],
+ },
+ },
+ default: '',
+ description: 'Contact parameters',
+ },
+ {
+ displayName: 'First Name',
+ name: 'firstName',
+ type: 'string',
+ displayOptions: {
+ show: {
+ '/jsonParameters': [
+ false,
+ ],
+ },
+ },
+ default: '',
+ description: 'First Name',
+ },
+ {
+ displayName: 'Last Name',
+ name: 'lastName',
+ type: 'string',
+ displayOptions: {
+ show: {
+ '/jsonParameters': [
+ false,
+ ],
+ },
+ },
+ default: '',
+ description: 'LastName',
+ },
+ {
+ displayName: 'Primary Company',
+ name: 'company',
+ type: 'options',
+ typeOptions: {
+ loadOptionsMethod: 'getCompanies',
+ },
+ displayOptions: {
+ show: {
+ '/jsonParameters': [
+ false,
+ ],
+ },
+ },
+ default: '',
+ description: 'Primary company',
+ },
+ {
+ displayName: 'Position',
+ name: 'position',
+ type: 'string',
+ displayOptions: {
+ show: {
+ '/jsonParameters': [
+ false,
+ ],
+ },
+ },
+ default: '',
+ description: 'Position',
+ },
+ {
+ displayName: 'Title',
+ name: 'title',
+ type: 'string',
+ displayOptions: {
+ show: {
+ '/jsonParameters': [
+ false,
+ ],
+ },
+ },
+ default: '',
+ description: 'Title',
+ },
+ {
+ displayName: 'IP Address',
+ name: 'ipAddress',
+ type: 'string',
+ default: '',
+ description: 'IP address to associate with the contact',
+ },
+ {
+ displayName: 'Last Active',
+ name: 'lastActive',
+ type: 'dateTime',
+ default: '',
+ description: 'Date/time in UTC;',
+ },
+ {
+ displayName: 'Owner ID',
+ name: 'ownerId',
+ type: 'string',
+ default: '',
+ description: 'ID of a Mautic user to assign this contact to',
+ },
+ ],
+ },
+/* -------------------------------------------------------------------------- */
+/* contact:get */
+/* -------------------------------------------------------------------------- */
+ {
+ displayName: 'Contact ID',
+ name: 'contactId',
+ type: 'string',
+ displayOptions: {
+ show: {
+ operation: [
+ 'get',
+ ],
+ resource: [
+ 'contact',
+ ],
+ },
+ },
+ default: '',
+ description: 'Contact ID',
+ },
+/* -------------------------------------------------------------------------- */
+/* contact:getAll */
+/* -------------------------------------------------------------------------- */
+ {
+ displayName: 'Return All',
+ name: 'returnAll',
+ type: 'boolean',
+ displayOptions: {
+ show: {
+ resource: [
+ 'contact',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ },
+ },
+ default: false,
+ description: 'If all results should be returned or only up to a given limit.',
+ },
+ {
+ displayName: 'Limit',
+ name: 'limit',
+ type: 'number',
+ displayOptions: {
+ show: {
+ resource: [
+ 'contact',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ returnAll: [
+ false,
+ ],
+ },
+ },
+ typeOptions: {
+ minValue: 1,
+ maxValue: 30,
+ },
+ default: 30,
+ description: 'How many results to return.',
+ },
+ {
+ displayName: 'Filters',
+ name: 'filters',
+ type: 'collection',
+ placeholder: 'Add Filter',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'contact',
+ ],
+ operation: [
+ 'getAll',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Search',
+ name: 'search',
+ type: 'string',
+ default: '',
+ description: 'String or search command to filter entities by.',
+ },
+ {
+ displayName: 'Order By',
+ name: 'orderBy',
+ type: 'string',
+ default: '',
+ description: `Column to sort by. Can use any column listed in the response.
+ However, all properties in the response that are written in camelCase need to be
+ changed a bit. Before every capital add an underscore _ and then change the capital
+ letters to non-capital letters. So dateIdentified becomes date_identified, modifiedByUser
+ becomes modified_by_user etc.`,
+ },
+ {
+ displayName: 'Order By Dir',
+ name: 'orderByDir',
+ type: 'options',
+ default: '',
+ options: [
+ {
+ name: 'ASC',
+ valie: 'asc',
+ },
+ {
+ name: 'Desc',
+ valie: 'desc',
+ },
+ ],
+ description: 'Sort direction: asc or desc.',
+ },
+ {
+ displayName: 'Published Only',
+ name: 'publishedOnly',
+ type: 'boolean',
+ default: false,
+ description: 'Only return currently published entities.',
+ },
+ {
+ displayName: 'Minimal',
+ name: 'minimal',
+ type: 'boolean',
+ default: false,
+ description: 'Return only array of entities without additional lists in it.',
+ },
+ ]
+ },
+/* -------------------------------------------------------------------------- */
+/* contact:delete */
+/* -------------------------------------------------------------------------- */
+ {
+ displayName: 'Contact ID',
+ name: 'contactId',
+ type: 'string',
+ displayOptions: {
+ show: {
+ operation: [
+ 'delete',
+ ],
+ resource: [
+ 'contact',
+ ],
+ },
+ },
+ default: '',
+ description: 'Contact ID',
+ },
+] as INodeProperties[];
diff --git a/packages/nodes-base/nodes/Mautic/GenericFunctions.ts b/packages/nodes-base/nodes/Mautic/GenericFunctions.ts
new file mode 100644
index 0000000000..f2330e51cf
--- /dev/null
+++ b/packages/nodes-base/nodes/Mautic/GenericFunctions.ts
@@ -0,0 +1,79 @@
+import { OptionsWithUri } from 'request';
+
+import {
+ IExecuteFunctions,
+ IHookFunctions,
+ ILoadOptionsFunctions,
+ IExecuteSingleFunctions
+} from 'n8n-core';
+
+import {
+ IDataObject,
+} from 'n8n-workflow';
+
+export async function mauticApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, endpoint: string, body: any = {}, query?: IDataObject, uri?: string): Promise { // tslint:disable-line:no-any
+ const credentials = this.getCredentials('mauticApi');
+ if (credentials === undefined) {
+ throw new Error('No credentials got returned!');
+ }
+ const base64Key = Buffer.from(`${credentials.username}:${credentials.password}`).toString('base64');
+ const options: OptionsWithUri = {
+ headers: { Authorization: `Basic ${base64Key}` },
+ method,
+ qs: query,
+ uri: uri || `${credentials.url}/api${endpoint}`,
+ body,
+ json: true
+ };
+ try {
+ return await this.helpers.request!(options);
+ } catch (err) {
+ const errorMessage = err.error || err.error.message;
+
+ if (errorMessage !== undefined) {
+ throw errorMessage;
+ }
+ throw err
+ }
+}
+
+/**
+ * Make an API request to paginated mautic endpoint
+ * and return all results
+ */
+export async function mauticApiRequestAllItems(this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions, propertyName: string, method: string, endpoint: string, body: any = {}, query: IDataObject = {}): Promise { // tslint:disable-line:no-any
+
+ const returnData: IDataObject[] = [];
+
+ let responseData;
+ let data: IDataObject[] = [];
+ query.limit = 30;
+ query.start = 0;
+
+ let uri: string | undefined;
+
+ do {
+ responseData = await mauticApiRequest.call(this, method, endpoint, body, query, uri);
+ const values = Object.values(responseData[propertyName])
+ for (const value of values) {
+ data.push(value as IDataObject)
+ }
+ returnData.push.apply(returnData, data);
+ query.start++;
+ } while (
+ responseData.total !== undefined &&
+ ((query.limit * query.start) - parseInt(responseData.total)) <= 0
+ );
+
+ return returnData;
+}
+
+export function validateJSON(json: string | undefined): any { // tslint:disable-line:no-any
+ let result;
+ try {
+ result = JSON.parse(json!);
+ } catch (exception) {
+ result = undefined;
+ }
+ return result;
+}
diff --git a/packages/nodes-base/nodes/Mautic/Mautic.node.ts b/packages/nodes-base/nodes/Mautic/Mautic.node.ts
new file mode 100644
index 0000000000..b223f28e5d
--- /dev/null
+++ b/packages/nodes-base/nodes/Mautic/Mautic.node.ts
@@ -0,0 +1,229 @@
+import {
+ IExecuteFunctions,
+} from 'n8n-core';
+import {
+ IDataObject,
+ INodeTypeDescription,
+ INodeExecutionData,
+ INodeType,
+ ILoadOptionsFunctions,
+ INodePropertyOptions,
+} from 'n8n-workflow';
+import {
+ mauticApiRequest,
+ mauticApiRequestAllItems,
+ validateJSON,
+} from './GenericFunctions';
+import {
+ contactFields,
+ contactOperations,
+} from './ContactDescription';
+
+export class Mautic implements INodeType {
+ description: INodeTypeDescription = {
+ displayName: 'Mautic',
+ name: 'mautic',
+ icon: 'file:mautic.png',
+ group: ['output'],
+ version: 1,
+ subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
+ description: 'Consume Mautic API',
+ defaults: {
+ name: 'Mautic',
+ color: '#52619b',
+ },
+ inputs: ['main'],
+ outputs: ['main'],
+ credentials: [
+ {
+ name: 'mauticApi',
+ required: true,
+ }
+ ],
+ properties: [
+ {
+ displayName: 'Resource',
+ name: 'resource',
+ type: 'options',
+ options: [
+ {
+ name: 'Contact',
+ value: 'contact',
+ description: 'Use this endpoint to manipulate and obtain details on Mautic’s contacts.',
+ },
+ ],
+ default: 'contact',
+ description: 'Resource to consume.',
+ },
+ ...contactOperations,
+ ...contactFields,
+ ],
+ };
+
+ methods = {
+ loadOptions: {
+ // Get all the available companies to display them to user so that he can
+ // select them easily
+ async getCompanies(this: ILoadOptionsFunctions): Promise {
+ const returnData: INodePropertyOptions[] = [];
+ let companies;
+ try {
+ companies = await mauticApiRequestAllItems.call(this, 'companies', 'GET', '/companies');
+ } catch (err) {
+ throw new Error(`Mautic Error: ${JSON.stringify(err)}`);
+ }
+ for (const company of companies) {
+ returnData.push({
+ name: company.fields.all.companyname,
+ value: company.id,
+ });
+ }
+ return returnData;
+ },
+ },
+ };
+
+ async execute(this: IExecuteFunctions): Promise {
+ const items = this.getInputData();
+ const returnData: IDataObject[] = [];
+ const length = items.length as unknown as number;
+ let qs: IDataObject;
+ let responseData;
+ for (let i = 0; i < length; i++) {
+ qs = {};
+ const resource = this.getNodeParameter('resource', 0) as string;
+ const operation = this.getNodeParameter('operation', 0) as string;
+ if (resource === 'contact') {
+ //https://developer.mautic.org/?php#create-contact
+ if (operation === 'create') {
+ const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
+ const jsonActive = this.getNodeParameter('jsonParameters', i) as boolean;
+ let body: IDataObject = {};
+ if (!jsonActive) {
+ const firstName = this.getNodeParameter('firstName', i) as string;
+ const lastName = this.getNodeParameter('lastName', i) as string;
+ const company = this.getNodeParameter('company', i) as string;
+ const position = this.getNodeParameter('position', i) as string;
+ const title = this.getNodeParameter('title', i) as string;
+ body.firstname = firstName;
+ body.lastname = lastName;
+ body.company = company;
+ body.position = position;
+ body.title = title;
+ } else {
+ const json = validateJSON(this.getNodeParameter('bodyJson', i) as string);
+ if (json !== undefined) {
+ body = { ...json };
+ } else {
+ throw new Error('Invalid JSON')
+ }
+ }
+ if (additionalFields.ipAddress) {
+ body.ipAddress = additionalFields.ipAddress as string;
+ }
+ if (additionalFields.lastActive) {
+ body.ipAddress = additionalFields.lastActive as string;
+ }
+ if (additionalFields.ownerId) {
+ body.ownerId = additionalFields.ownerId as string;
+ }
+ try {
+ responseData = await mauticApiRequest.call(this, 'POST', '/contacts/new', body);
+ responseData = responseData.contact;
+ } catch (err) {
+ throw new Error(`Mautic Error: ${JSON.stringify(err)}`);
+ }
+ }
+ //https://developer.mautic.org/?php#edit-contact
+ if (operation === 'update') {
+ const updateFields = this.getNodeParameter('updateFields', i) as IDataObject;
+ const contactId = this.getNodeParameter('contactId', i) as string;
+ let body: IDataObject = {};
+ if (updateFields.firstName) {
+ body.firstname = updateFields.firstName as string;
+ }
+ if (updateFields.lastName) {
+ body.lastname = updateFields.lastName as string;
+ }
+ if (updateFields.company) {
+ body.company = updateFields.company as string;
+ }
+ if (updateFields.position) {
+ body.position = updateFields.position as string;
+ }
+ if (updateFields.title) {
+ body.title = updateFields.title as string;
+ }
+ if (updateFields.bodyJson) {
+ const json = validateJSON(updateFields.bodyJson as string);
+ if (json !== undefined) {
+ body = { ...json };
+ } else {
+ throw new Error('Invalid JSON')
+ }
+ }
+ if (updateFields.ipAddress) {
+ body.ipAddress = updateFields.ipAddress as string;
+ }
+ if (updateFields.lastActive) {
+ body.ipAddress = updateFields.lastActive as string;
+ }
+ if (updateFields.ownerId) {
+ body.ownerId = updateFields.ownerId as string;
+ }
+ try {
+ responseData = await mauticApiRequest.call(this, 'PATCH', `/contacts/${contactId}/edit`, body);
+ responseData = responseData.contact;
+ } catch (err) {
+ throw new Error(`Mautic Error: ${JSON.stringify(err)}`);
+ }
+ }
+ //https://developer.mautic.org/?php#get-contact
+ if (operation === 'get') {
+ const contactId = this.getNodeParameter('contactId', i) as string;
+ try {
+ responseData = await mauticApiRequest.call(this, 'GET', `/contacts/${contactId}`);
+ responseData = responseData.contact;
+ } catch (err) {
+ throw new Error(`Mautic Error: ${JSON.stringify(err)}`);
+ }
+ }
+ //https://developer.mautic.org/?php#list-contacts
+ if (operation === 'getAll') {
+ const returnAll = this.getNodeParameter('returnAll', i) as boolean;
+ const filters = this.getNodeParameter('filters', i) as IDataObject;
+ qs = Object.assign(qs, filters);
+ try {
+ if (returnAll === true) {
+ responseData = await mauticApiRequestAllItems.call(this, 'contacts', 'GET', '/contacts', {}, qs);
+ } else {
+ qs.limit = this.getNodeParameter('limit', i) as number;
+ qs.start = 0;
+ responseData = await mauticApiRequest.call(this, 'GET', '/contacts', {}, qs);
+ responseData = responseData.contacts;
+ responseData = Object.values(responseData);
+ }
+ } catch (err) {
+ throw new Error(`Mautic Error: ${JSON.stringify(err)}`);
+ }
+ }
+ //https://developer.mautic.org/?php#delete-contact
+ if (operation === 'delete') {
+ const contactId = this.getNodeParameter('contactId', i) as string;
+ try {
+ responseData = await mauticApiRequest.call(this, 'DELETE', `/contacts/${contactId}/delete`);
+ responseData = responseData.contact;
+ } catch (err) {
+ throw new Error(`Mautic Error: ${JSON.stringify(err)}`);
+ }
+ }
+ }
+ if (Array.isArray(responseData)) {
+ returnData.push.apply(returnData, responseData as IDataObject[]);
+ } else {
+ returnData.push(responseData as IDataObject);
+ }
+ }
+ return [this.helpers.returnJsonArray(returnData)];
+ }
+}
diff --git a/packages/nodes-base/nodes/Mautic/MauticTrigger.node.ts b/packages/nodes-base/nodes/Mautic/MauticTrigger.node.ts
new file mode 100644
index 0000000000..18ad63225d
--- /dev/null
+++ b/packages/nodes-base/nodes/Mautic/MauticTrigger.node.ts
@@ -0,0 +1,156 @@
+import {
+ parse as urlParse,
+} from 'url';
+
+import {
+ IHookFunctions,
+ IWebhookFunctions,
+} from 'n8n-core';
+
+import {
+ INodeTypeDescription,
+ INodeType,
+ IWebhookResponseData,
+ IDataObject,
+ INodePropertyOptions,
+ ILoadOptionsFunctions,
+} from 'n8n-workflow';
+
+import {
+ mauticApiRequest,
+} from './GenericFunctions';
+
+export class MauticTrigger implements INodeType {
+ description: INodeTypeDescription = {
+ displayName: 'Mautic Trigger',
+ name: 'mauticTrigger',
+ icon: 'file:mautic.png',
+ group: ['trigger'],
+ version: 1,
+ description: 'Handle Mautic events via webhooks',
+ defaults: {
+ name: 'Mautic Trigger',
+ color: '#52619b',
+ },
+ inputs: [],
+ outputs: ['main'],
+ credentials: [
+ {
+ name: 'mauticApi',
+ required: true,
+ }
+ ],
+ webhooks: [
+ {
+ name: 'default',
+ httpMethod: 'POST',
+ responseMode: 'onReceived',
+ path: 'webhook',
+ },
+ ],
+ properties: [
+ {
+ displayName: 'Events',
+ name: 'events',
+ type: 'multiOptions',
+ required: true,
+ typeOptions: {
+ loadOptionsMethod: 'getEvents',
+ },
+ default: [],
+ }, {
+ displayName: 'Events Order',
+ name: 'eventsOrder',
+ type: 'options',
+ default: '',
+ options: [
+ {
+ name: 'Asc',
+ value: 'ASC',
+ },
+ {
+ name: 'Desc',
+ value: 'DESC',
+ },
+ ],
+ description: 'Order direction for queued events in one webhook. Can be “DESC” or “ASC”',
+ },
+ ],
+ };
+ methods = {
+ loadOptions: {
+ // Get all the events to display them to user so that he can
+ // select them easily
+ async getEvents(this: ILoadOptionsFunctions): Promise {
+ const returnData: INodePropertyOptions[] = [];
+ const { triggers } = await mauticApiRequest.call(this, 'GET', '/hooks/triggers');
+ for (const [key, value] of Object.entries(triggers)) {
+ const eventId = key;
+ const eventName = (value as IDataObject).label as string;
+ const eventDecription = (value as IDataObject).description as string;
+ returnData.push({
+ name: eventName,
+ value: eventId,
+ description: eventDecription,
+ });
+ }
+ return returnData;
+ },
+ }
+ };
+ // @ts-ignore
+ webhookMethods = {
+ default: {
+ async checkExists(this: IHookFunctions): Promise {
+ const webhookData = this.getWorkflowStaticData('node');
+ if (webhookData.webhookId === undefined) {
+ return false;
+ }
+ const endpoint = `/hooks/${webhookData.webhookId}`;
+ try {
+ await mauticApiRequest.call(this, 'GET', endpoint, {});
+ } catch (e) {
+ return false;
+ }
+ return true;
+ },
+ async create(this: IHookFunctions): Promise {
+ const webhookUrl = this.getNodeWebhookUrl('default') as string;
+ const webhookData = this.getWorkflowStaticData('node');
+ const events = this.getNodeParameter('events', 0) as string[];
+ const eventsOrder = this.getNodeParameter('eventsOrder', 0) as string;
+ const urlParts = urlParse(webhookUrl);
+ const body: IDataObject = {
+ name: `n8n-webhook:${urlParts.path}`,
+ description: `n8n webhook`,
+ webhookUrl: webhookUrl,
+ triggers: events,
+ eventsOrderbyDir: eventsOrder,
+ isPublished: true,
+ }
+ const { hook } = await mauticApiRequest.call(this, 'POST', '/hooks/new', body);
+ webhookData.webhookId = hook.id;
+ return true;
+ },
+ async delete(this: IHookFunctions): Promise {
+ const webhookData = this.getWorkflowStaticData('node');
+ try {
+ await mauticApiRequest.call(this, 'DELETE', `/hooks/${webhookData.webhookId}/delete`);
+ } 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/Mautic/mautic.png b/packages/nodes-base/nodes/Mautic/mautic.png
new file mode 100644
index 0000000000..7ed1375e5f
Binary files /dev/null and b/packages/nodes-base/nodes/Mautic/mautic.png differ
diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json
index 16792b6f8a..863001b2ba 100644
--- a/packages/nodes-base/package.json
+++ b/packages/nodes-base/package.json
@@ -56,6 +56,7 @@
"dist/credentials/MattermostApi.credentials.js",
"dist/credentials/MongoDb.credentials.js",
"dist/credentials/MySql.credentials.js",
+ "dist/credentials/MauticApi.credentials.js",
"dist/credentials/NextCloudApi.credentials.js",
"dist/credentials/OpenWeatherMapApi.credentials.js",
"dist/credentials/PipedriveApi.credentials.js",
@@ -135,8 +136,10 @@
"dist/nodes/MoveBinaryData.node.js",
"dist/nodes/MongoDb/MongoDb.node.js",
"dist/nodes/MySql/MySql.node.js",
- "dist/nodes/NextCloud/NextCloud.node.js",
- "dist/nodes/Mandrill/Mandrill.node.js",
+ "dist/nodes/NextCloud/NextCloud.node.js",
+ "dist/nodes/Mandrill/Mandrill.node.js",
+ "dist/nodes/Mautic/Mautic.node.js",
+ "dist/nodes/Mautic/MauticTrigger.node.js",
"dist/nodes/NoOp.node.js",
"dist/nodes/OpenWeatherMap.node.js",
"dist/nodes/Pipedrive/Pipedrive.node.js",