From fa31c0c7b64d0126d82a165d5cacaafd073013ba Mon Sep 17 00:00:00 2001 From: Mutasem Aldmour <4711238+mutdmour@users.noreply.github.com> Date: Sat, 7 Aug 2021 09:35:59 +0200 Subject: [PATCH 001/111] :bug: Fix UI blocking with large workflows (#2053) * fix reactivity bug * Clean up props * add comma * clean up, reduce reactivity more * ts issues --- packages/editor-ui/src/components/Node.vue | 18 +++++++++++++----- .../src/components/mixins/nodeBase.ts | 1 + packages/editor-ui/src/views/NodeView.vue | 1 + 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/packages/editor-ui/src/components/Node.vue b/packages/editor-ui/src/components/Node.vue index fd17eaea9e..22bb4ed800 100644 --- a/packages/editor-ui/src/components/Node.vue +++ b/packages/editor-ui/src/components/Node.vue @@ -125,22 +125,30 @@ export default mixins(externalHooks, nodeBase, nodeHelpers, workflowHelpers).ext return 'play'; } }, - nodeSubtitle (): string | undefined { - return this.getNodeSubtitle(this.data, this.nodeType, this.workflow); - }, workflowRunning (): boolean { return this.$store.getters.isActionActive('workflowRunning'); }, - workflow () { - return this.getWorkflow(); + }, + watch: { + isActive(newValue, oldValue) { + if (!newValue && oldValue) { + this.setSubtitle(); + } }, }, + mounted() { + this.setSubtitle(); + }, data () { return { isTouchActive: false, + nodeSubtitle: '', }; }, methods: { + setSubtitle() { + this.nodeSubtitle = this.getNodeSubtitle(this.data, this.nodeType, this.getWorkflow()) || ''; + }, disableNode () { this.disableNodes([this.data]); }, diff --git a/packages/editor-ui/src/components/mixins/nodeBase.ts b/packages/editor-ui/src/components/mixins/nodeBase.ts index bc8c3b1cc0..d687a2fd09 100644 --- a/packages/editor-ui/src/components/mixins/nodeBase.ts +++ b/packages/editor-ui/src/components/mixins/nodeBase.ts @@ -57,6 +57,7 @@ export const nodeBase = mixins( 'nodeId', 'instance', 'isReadOnly', + 'isActive', ], methods: { __addNode (node: INodeUi) { diff --git a/packages/editor-ui/src/views/NodeView.vue b/packages/editor-ui/src/views/NodeView.vue index 10a7997424..a2178fb950 100644 --- a/packages/editor-ui/src/views/NodeView.vue +++ b/packages/editor-ui/src/views/NodeView.vue @@ -26,6 +26,7 @@ :name="nodeData.name" :isReadOnly="isReadOnly" :instance="instance" + :isActive="!!activeNode && activeNode.name === nodeData.name" > From 244a2ba409fb1463bceb0f0ece7611add7be463b Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Sat, 7 Aug 2021 03:41:00 -0400 Subject: [PATCH 002/111] :zap: Add record type id to supported objects on Salesforce Node (#2056) --- .../nodes/Salesforce/AccountDescription.ts | 18 ++++++ .../nodes/Salesforce/CaseDescription.ts | 18 ++++++ .../nodes/Salesforce/CaseInterface.ts | 1 + .../nodes/Salesforce/ContactDescription.ts | 18 ++++++ .../Salesforce/CustomObjectDescription.ts | 63 +++++++++++++++++++ .../nodes/Salesforce/LeadDescription.ts | 18 ++++++ .../nodes/Salesforce/Salesforce.node.ts | 57 ++++++++++++++++- 7 files changed, 192 insertions(+), 1 deletion(-) diff --git a/packages/nodes-base/nodes/Salesforce/AccountDescription.ts b/packages/nodes-base/nodes/Salesforce/AccountDescription.ts index 6b03793360..b00a4331dc 100644 --- a/packages/nodes-base/nodes/Salesforce/AccountDescription.ts +++ b/packages/nodes-base/nodes/Salesforce/AccountDescription.ts @@ -291,6 +291,15 @@ export const accountFields = [ default: '', description: 'Phone number for the account.', }, + { + displayName: 'Record Type ID', + name: 'recordTypeId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getRecordTypes', + }, + default: '', + }, { displayName: 'SicDesc', name: 'sicDesc', @@ -539,6 +548,15 @@ export const accountFields = [ default: '', description: 'Phone number for the account.', }, + { + displayName: 'Record Type ID', + name: 'recordTypeId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getRecordTypes', + }, + default: '', + }, { displayName: 'Type', name: 'type', diff --git a/packages/nodes-base/nodes/Salesforce/CaseDescription.ts b/packages/nodes-base/nodes/Salesforce/CaseDescription.ts index 69bed22293..aadf26e206 100644 --- a/packages/nodes-base/nodes/Salesforce/CaseDescription.ts +++ b/packages/nodes-base/nodes/Salesforce/CaseDescription.ts @@ -208,6 +208,15 @@ export const caseFields = [ default: '', description: 'The reason why the case was created, such as Instructions not clear, or User didn’t attend training.', }, + { + displayName: 'Record Type ID', + name: 'recordTypeId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getRecordTypes', + }, + default: '', + }, { displayName: 'Status', name: 'status', @@ -405,6 +414,15 @@ export const caseFields = [ default: '', description: 'The reason why the case was created, such as Instructions not clear, or User didn’t attend training.', }, + { + displayName: 'Record Type ID', + name: 'recordTypeId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getRecordTypes', + }, + default: '', + }, { displayName: 'Status', name: 'status', diff --git a/packages/nodes-base/nodes/Salesforce/CaseInterface.ts b/packages/nodes-base/nodes/Salesforce/CaseInterface.ts index 169d47c14d..76ac5016c9 100644 --- a/packages/nodes-base/nodes/Salesforce/CaseInterface.ts +++ b/packages/nodes-base/nodes/Salesforce/CaseInterface.ts @@ -16,6 +16,7 @@ export interface ICase { SuppliedEmail?: string; SuppliedPhone?: string; SuppliedCompany?: string; + RecordTypeId?: string; } export interface ICaseComment { diff --git a/packages/nodes-base/nodes/Salesforce/ContactDescription.ts b/packages/nodes-base/nodes/Salesforce/ContactDescription.ts index 4e9901fc6e..e47db24b0d 100644 --- a/packages/nodes-base/nodes/Salesforce/ContactDescription.ts +++ b/packages/nodes-base/nodes/Salesforce/ContactDescription.ts @@ -384,6 +384,15 @@ export const contactFields = [ default: '', description: 'Phone number for the contact.', }, + { + displayName: 'Record Type ID', + name: 'recordTypeId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getRecordTypes', + }, + default: '', + }, { displayName: 'Salutation', name: 'salutation', @@ -680,6 +689,15 @@ export const contactFields = [ default: '', description: 'Phone number for the contact.', }, + { + displayName: 'Record Type ID', + name: 'recordTypeId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getRecordTypes', + }, + default: '', + }, { displayName: 'Salutation', name: 'salutation', diff --git a/packages/nodes-base/nodes/Salesforce/CustomObjectDescription.ts b/packages/nodes-base/nodes/Salesforce/CustomObjectDescription.ts index 35f6c7f32e..972633ed79 100644 --- a/packages/nodes-base/nodes/Salesforce/CustomObjectDescription.ts +++ b/packages/nodes-base/nodes/Salesforce/CustomObjectDescription.ts @@ -512,4 +512,67 @@ export const customObjectFields = [ }, ], }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + displayOptions: { + show: { + operation: [ + 'create', + 'upsert', + ], + resource: [ + 'customObject', + ], + }, + }, + default: {}, + placeholder: 'Add Field', + options: [ + { + displayName: 'Record Type ID', + name: 'recordTypeId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getRecordTypes', + loadOptionsDependsOn: [ + 'customObject', + ], + }, + default: '', + }, + ], + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + displayOptions: { + show: { + operation: [ + 'update', + ], + resource: [ + 'customObject', + ], + }, + }, + default: {}, + placeholder: 'Add Field', + options: [ + { + displayName: 'Record Type ID', + name: 'recordTypeId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getRecordTypes', + loadOptionsDependsOn: [ + 'customObject', + ], + }, + default: '', + }, + ], + }, ] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Salesforce/LeadDescription.ts b/packages/nodes-base/nodes/Salesforce/LeadDescription.ts index cff54682c1..fe33c3eb45 100644 --- a/packages/nodes-base/nodes/Salesforce/LeadDescription.ts +++ b/packages/nodes-base/nodes/Salesforce/LeadDescription.ts @@ -320,6 +320,15 @@ export const leadFields = [ default: '', description: 'Postal code for the address of the lead. Label is Zip/Postal Code.', }, + { + displayName: 'Record Type ID', + name: 'recordTypeId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getRecordTypes', + }, + default: '', + }, { displayName: 'Rating', name: 'rating', @@ -578,6 +587,15 @@ export const leadFields = [ default: '', description: 'Phone number for the lead.', }, + { + displayName: 'Record Type ID', + name: 'recordTypeId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getRecordTypes', + }, + default: '', + }, { displayName: 'Rating', name: 'rating', diff --git a/packages/nodes-base/nodes/Salesforce/Salesforce.node.ts b/packages/nodes-base/nodes/Salesforce/Salesforce.node.ts index b91ad6cb16..b8a61e32f6 100644 --- a/packages/nodes-base/nodes/Salesforce/Salesforce.node.ts +++ b/packages/nodes-base/nodes/Salesforce/Salesforce.node.ts @@ -121,6 +121,7 @@ import { import { LoggerProxy as Logger, } from 'n8n-workflow'; +import { query } from '../Elasticsearch/descriptions/placeholders'; export class Salesforce implements INodeType { description: INodeTypeDescription = { @@ -408,7 +409,6 @@ export class Salesforce implements INodeType { const resource = this.getNodeParameter('resource', 0) as string; // TODO: find a way to filter this object to get just the lead sources instead of the whole object const { fields } = await salesforceApiRequest.call(this, 'GET', `/sobjects/${resource}/describe`); - for (const field of fields) { if (field.custom === true) { const fieldName = field.label; @@ -422,6 +422,29 @@ export class Salesforce implements INodeType { sortOptions(returnData); return returnData; }, + // Get all the record types to display them to user so that he can + // select them easily + async getRecordTypes(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + let resource = this.getNodeParameter('resource', 0) as string; + if (resource === 'customObject') { + resource = this.getNodeParameter('customObject', 0) as string; + } + const qs = { + q: `SELECT Id, Name, SobjectType, IsActive FROM RecordType WHERE SobjectType = '${resource}'`, + }; + const types = await salesforceApiRequestAllItems.call(this, 'records', 'GET', '/query', {}, qs); + for (const type of types) { + if (type.IsActive === true) { + returnData.push({ + name: type.Name, + value: type.Id, + }); + } + } + sortOptions(returnData); + return returnData; + }, // Get all the external id fields to display them to user so that he can // select them easily async getExternalIdFields(this: ILoadOptionsFunctions): Promise { @@ -1061,6 +1084,9 @@ export class Salesforce implements INodeType { if (additionalFields.mobilePhone !== undefined) { body.MobilePhone = additionalFields.mobilePhone as string; } + if (additionalFields.recordTypeId !== undefined) { + body.RecordTypeId = additionalFields.recordTypeId as string; + } if (additionalFields.customFieldsUi) { const customFields = (additionalFields.customFieldsUi as IDataObject).customFieldsValues as IDataObject[]; if (customFields) { @@ -1163,6 +1189,9 @@ export class Salesforce implements INodeType { if (updateFields.mobilePhone !== undefined) { body.MobilePhone = updateFields.mobilePhone as string; } + if (updateFields.recordTypeId !== undefined) { + body.RecordTypeId = updateFields.recordTypeId as string; + } if (updateFields.customFieldsUi) { const customFields = (updateFields.customFieldsUi as IDataObject).customFieldsValues as IDataObject[]; if (customFields) { @@ -1267,6 +1296,9 @@ export class Salesforce implements INodeType { if (additionalFields.jigsaw !== undefined) { body.Jigsaw = additionalFields.jigsaw as string; } + if (additionalFields.recordTypeId !== undefined) { + body.RecordTypeId = additionalFields.recordTypeId as string; + } if (additionalFields.owner !== undefined) { body.OwnerId = additionalFields.owner as string; } @@ -1381,6 +1413,9 @@ export class Salesforce implements INodeType { if (updateFields.email !== undefined) { body.Email = updateFields.email as string; } + if (updateFields.recordTypeId !== undefined) { + body.RecordTypeId = updateFields.recordTypeId as string; + } if (updateFields.phone !== undefined) { body.Phone = updateFields.phone as string; } @@ -1551,6 +1586,7 @@ export class Salesforce implements INodeType { if (operation === 'create' || operation === 'upsert') { const customObject = this.getNodeParameter('customObject', i) as string; const customFieldsUi = this.getNodeParameter('customFieldsUi', i) as IDataObject; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; const body: IDataObject = {}; if (customFieldsUi) { const customFields = (customFieldsUi as IDataObject).customFieldsValues as IDataObject[]; @@ -1561,6 +1597,9 @@ export class Salesforce implements INodeType { } } } + if (additionalFields.recordTypeId) { + body.RecordTypeId = additionalFields.recordTypeId as string; + } let endpoint = `/sobjects/${customObject}`; let method = 'POST'; if (operation === 'upsert') { @@ -1578,7 +1617,11 @@ export class Salesforce implements INodeType { const recordId = this.getNodeParameter('recordId', i) as string; const customObject = this.getNodeParameter('customObject', i) as string; const customFieldsUi = this.getNodeParameter('customFieldsUi', i) as IDataObject; + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; const body: IDataObject = {}; + if (updateFields.recordTypeId) { + body.RecordTypeId = updateFields.recordTypeId as string; + } if (customFieldsUi) { const customFields = (customFieldsUi as IDataObject).customFieldsValues as IDataObject[]; if (customFields) { @@ -1926,6 +1969,9 @@ export class Salesforce implements INodeType { if (additionalFields.shippingPostalCode !== undefined) { body.ShippingPostalCode = additionalFields.shippingPostalCode as string; } + if (additionalFields.recordTypeId !== undefined) { + body.RecordTypeId = additionalFields.recordTypeId as string; + } if (additionalFields.customFieldsUi) { const customFields = (additionalFields.customFieldsUi as IDataObject).customFieldsValues as IDataObject[]; if (customFields) { @@ -1974,6 +2020,9 @@ export class Salesforce implements INodeType { if (updateFields.sicDesc !== undefined) { body.SicDesc = updateFields.sicDesc as string; } + if (updateFields.recordTypeId !== undefined) { + body.RecordTypeId = updateFields.recordTypeId as string; + } if (updateFields.website !== undefined) { body.Website = updateFields.website as string; } @@ -2145,6 +2194,9 @@ export class Salesforce implements INodeType { if (additionalFields.suppliedCompany !== undefined) { body.SuppliedCompany = additionalFields.suppliedCompany as string; } + if (additionalFields.recordTypeId !== undefined) { + body.RecordTypeId = additionalFields.recordTypeId as string; + } if (additionalFields.customFieldsUi) { const customFields = (additionalFields.customFieldsUi as IDataObject).customFieldsValues as IDataObject[]; if (customFields) { @@ -2185,6 +2237,9 @@ export class Salesforce implements INodeType { if (updateFields.accountId !== undefined) { body.AccountId = updateFields.accountId as string; } + if (updateFields.recordTypeId !== undefined) { + body.RecordTypeId = updateFields.recordTypeId as string; + } if (updateFields.contactId !== undefined) { body.ContactId = updateFields.contactId as string; } From 89719102091b04379ce27d2af0f7dcfe6824d61f Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Sat, 7 Aug 2021 03:46:33 -0400 Subject: [PATCH 003/111] :zap: Change displayName to Cisco by Webex (#2058) * :zap: change displayName to Cisco by Webex * :zap: Change default name back Co-authored-by: Jan Oberhauser --- packages/nodes-base/nodes/Cisco/Webex/CiscoWebex.node.ts | 4 ++-- .../nodes-base/nodes/Cisco/Webex/CiscoWebexTrigger.node.ts | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/nodes-base/nodes/Cisco/Webex/CiscoWebex.node.ts b/packages/nodes-base/nodes/Cisco/Webex/CiscoWebex.node.ts index 136a8c9aac..430f69cfd6 100644 --- a/packages/nodes-base/nodes/Cisco/Webex/CiscoWebex.node.ts +++ b/packages/nodes-base/nodes/Cisco/Webex/CiscoWebex.node.ts @@ -33,7 +33,7 @@ import * as moment from 'moment-timezone'; export class CiscoWebex implements INodeType { description: INodeTypeDescription = { - displayName: 'Cisco Webex', + displayName: 'Webex by Cisco', name: 'ciscoWebex', icon: 'file:ciscoWebex.png', group: ['transform'], @@ -41,7 +41,7 @@ export class CiscoWebex implements INodeType { subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', description: 'Consume the Cisco Webex API', defaults: { - name: 'Cisco Webex', + name: 'Webex', color: '#29b6f6', }, credentials: [ diff --git a/packages/nodes-base/nodes/Cisco/Webex/CiscoWebexTrigger.node.ts b/packages/nodes-base/nodes/Cisco/Webex/CiscoWebexTrigger.node.ts index 43902109cc..7a33970f1c 100644 --- a/packages/nodes-base/nodes/Cisco/Webex/CiscoWebexTrigger.node.ts +++ b/packages/nodes-base/nodes/Cisco/Webex/CiscoWebexTrigger.node.ts @@ -24,7 +24,7 @@ import { export class CiscoWebexTrigger implements INodeType { description: INodeTypeDescription = { - displayName: 'Cisco Webex Trigger', + displayName: 'Webex by Cisco Trigger', name: 'ciscoWebexTrigger', icon: 'file:ciscoWebex.png', group: ['trigger'], @@ -32,7 +32,7 @@ export class CiscoWebexTrigger implements INodeType { subtitle: '={{$parameter["resource"] + ":" + $parameter["event"]}}', description: 'Starts the workflow when Cisco Webex events occur.', defaults: { - name: 'Cisco Webex Trigger', + name: 'Webex Trigger', color: '#29b6f6', }, inputs: [], @@ -662,7 +662,7 @@ export class CiscoWebexTrigger implements INodeType { 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) { From 1529550b14ea447fc73ca6e5050239e10146a908 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Sun, 8 Aug 2021 09:51:44 +0200 Subject: [PATCH 004/111] :sparkles: Add Monica CRM node (#2045) * :sparkles: Create Monica node * :zap: Fix default values * :fire: Remove excess spacing * :zap: Improvements * :zap: Improvements * :zap: Add environment parameter * :zap: Add sort parameter to contact:getAll * :zap: Minor improvements Co-authored-by: ricardo Co-authored-by: Jan Oberhauser --- .../credentials/MonicaCrmApi.credentials.ts | 48 + .../nodes/MonicaCrm/GenericFunctions.ts | 103 ++ .../nodes/MonicaCrm/MonicaCrm.node.ts | 1268 +++++++++++++++++ .../descriptions/ActivityDescription.ts | 324 +++++ .../MonicaCrm/descriptions/CallDescription.ts | 259 ++++ .../descriptions/ContactDescription.ts | 400 ++++++ .../descriptions/ContactFieldDescription.ts | 292 ++++ .../descriptions/ContactTagDescription.ts | 117 ++ .../descriptions/ConversationDescription.ts | 207 +++ .../ConversationMessageDescription.ts | 217 +++ .../descriptions/JournalEntryDescription.ts | 237 +++ .../MonicaCrm/descriptions/NoteDescription.ts | 269 ++++ .../descriptions/ReminderDescription.ts | 394 +++++ .../MonicaCrm/descriptions/TagDescription.ts | 198 +++ .../MonicaCrm/descriptions/TaskDescription.ts | 277 ++++ .../nodes/MonicaCrm/descriptions/index.ts | 12 + .../nodes-base/nodes/MonicaCrm/monicaCrm.png | Bin 0 -> 4007 bytes .../nodes-base/nodes/MonicaCrm/types.d.ts | 13 + packages/nodes-base/package.json | 2 + 19 files changed, 4637 insertions(+) create mode 100644 packages/nodes-base/credentials/MonicaCrmApi.credentials.ts create mode 100644 packages/nodes-base/nodes/MonicaCrm/GenericFunctions.ts create mode 100644 packages/nodes-base/nodes/MonicaCrm/MonicaCrm.node.ts create mode 100644 packages/nodes-base/nodes/MonicaCrm/descriptions/ActivityDescription.ts create mode 100644 packages/nodes-base/nodes/MonicaCrm/descriptions/CallDescription.ts create mode 100644 packages/nodes-base/nodes/MonicaCrm/descriptions/ContactDescription.ts create mode 100644 packages/nodes-base/nodes/MonicaCrm/descriptions/ContactFieldDescription.ts create mode 100644 packages/nodes-base/nodes/MonicaCrm/descriptions/ContactTagDescription.ts create mode 100644 packages/nodes-base/nodes/MonicaCrm/descriptions/ConversationDescription.ts create mode 100644 packages/nodes-base/nodes/MonicaCrm/descriptions/ConversationMessageDescription.ts create mode 100644 packages/nodes-base/nodes/MonicaCrm/descriptions/JournalEntryDescription.ts create mode 100644 packages/nodes-base/nodes/MonicaCrm/descriptions/NoteDescription.ts create mode 100644 packages/nodes-base/nodes/MonicaCrm/descriptions/ReminderDescription.ts create mode 100644 packages/nodes-base/nodes/MonicaCrm/descriptions/TagDescription.ts create mode 100644 packages/nodes-base/nodes/MonicaCrm/descriptions/TaskDescription.ts create mode 100644 packages/nodes-base/nodes/MonicaCrm/descriptions/index.ts create mode 100644 packages/nodes-base/nodes/MonicaCrm/monicaCrm.png create mode 100644 packages/nodes-base/nodes/MonicaCrm/types.d.ts diff --git a/packages/nodes-base/credentials/MonicaCrmApi.credentials.ts b/packages/nodes-base/credentials/MonicaCrmApi.credentials.ts new file mode 100644 index 0000000000..45d400a7df --- /dev/null +++ b/packages/nodes-base/credentials/MonicaCrmApi.credentials.ts @@ -0,0 +1,48 @@ +import { + ICredentialType, + INodeProperties, +} from 'n8n-workflow'; + +export class MonicaCrmApi implements ICredentialType { + name = 'monicaCrmApi'; + displayName = 'Monica CRM API'; + documentationUrl = 'monicaCrm'; + properties: INodeProperties[] = [ + { + displayName: 'Environment', + name: 'environment', + type: 'options', + default: 'cloudHosted', + options: [ + { + name: 'Cloud-hosted', + value: 'cloudHosted', + }, + { + name: 'Self-hosted', + value: 'selfHosted', + }, + ], + }, + { + displayName: 'Self-hosted domain', + name: 'domain', + type: 'string', + default: '', + placeholder: 'https://www.mydomain.com', + displayOptions: { + show: { + environment: [ + 'selfHosted', + ], + }, + }, + }, + { + displayName: 'API Token', + name: 'apiToken', + type: 'string', + default: '', + }, + ]; +} diff --git a/packages/nodes-base/nodes/MonicaCrm/GenericFunctions.ts b/packages/nodes-base/nodes/MonicaCrm/GenericFunctions.ts new file mode 100644 index 0000000000..9adee57d2b --- /dev/null +++ b/packages/nodes-base/nodes/MonicaCrm/GenericFunctions.ts @@ -0,0 +1,103 @@ +import { + Credentials, + IExecuteFunctions, +} from 'n8n-core'; + +import { + IDataObject, + ILoadOptionsFunctions, + NodeApiError, + NodeOperationError, +} from 'n8n-workflow'; + +import { + OptionsWithUri, +} from 'request'; + +import { + LoaderGetResponse, +} from './types'; + +export async function monicaCrmApiRequest( + this: IExecuteFunctions | ILoadOptionsFunctions, + method: string, + endpoint: string, + body: IDataObject = {}, + qs: IDataObject = {}, +) { + const credentials = this.getCredentials('monicaCrmApi') as { apiToken: string, environment: string, domain: string }; + + if (credentials === undefined) { + throw new NodeOperationError(this.getNode(), 'No credentials got returned!'); + } + + let baseUrl = `https://app.monicahq.com`; + + if (credentials.environment === 'selfHosted') { + baseUrl = credentials.domain; + } + + const options: OptionsWithUri = { + headers: { + Authorization: `Bearer ${credentials.apiToken}`, + }, + method, + body, + qs, + uri: `${baseUrl}/api${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) { + throw new NodeApiError(this.getNode(), error); + } +} + +export async function monicaCrmApiRequestAllItems( + this: IExecuteFunctions | ILoadOptionsFunctions, + method: string, + endpoint: string, + body: IDataObject = {}, + qs: IDataObject = {}, + { forLoader }: { forLoader: boolean } = { forLoader: false }, +) { + const returnAll = this.getNodeParameter('returnAll', 0, false) as boolean; + const limit = this.getNodeParameter('limit', 0, 0) as number; + + let totalItems = 0; + + let responseData; + const returnData: IDataObject[] = []; + + do { + responseData = await monicaCrmApiRequest.call(this, method, endpoint, body, qs); + returnData.push(...responseData.data); + + if (!forLoader && !returnAll && returnData.length > limit) { + return returnData.slice(0, limit); + } + + totalItems = responseData.meta.total; + } while (totalItems > returnData.length); + + return returnData; +} + +/** + * Get day, month, and year from the n8n UI datepicker. + */ +export const getDateParts = (date: string) => + date.split('T')[0].split('-').map(Number).reverse(); + +export const toOptions = (response: LoaderGetResponse) => + response.data.map(({ id, name }) => ({ value: id, name })); diff --git a/packages/nodes-base/nodes/MonicaCrm/MonicaCrm.node.ts b/packages/nodes-base/nodes/MonicaCrm/MonicaCrm.node.ts new file mode 100644 index 0000000000..47e4d57e8a --- /dev/null +++ b/packages/nodes-base/nodes/MonicaCrm/MonicaCrm.node.ts @@ -0,0 +1,1268 @@ +import { + IExecuteFunctions, +} from 'n8n-core'; + +import { + IDataObject, + ILoadOptionsFunctions, + INodeExecutionData, + INodeType, + INodeTypeDescription, +} from 'n8n-workflow'; + +import { + getDateParts, + monicaCrmApiRequest, + monicaCrmApiRequestAllItems, + toOptions, +} from './GenericFunctions'; + +import { + activityFields, + activityOperations, + callFields, + callOperations, + contactFieldFields, + contactFieldOperations, + contactFields, + contactOperations, + contactTagFields, + contactTagOperations, + conversationFields, + conversationMessageFields, + conversationMessageOperations, + conversationOperations, + journalEntryFields, + journalEntryOperations, + noteFields, + noteOperations, + reminderFields, + reminderOperations, + tagFields, + tagOperations, + taskFields, + taskOperations, +} from './descriptions'; + +import { + LoaderGetResponse, + Option, +} from './types'; + +export class MonicaCrm implements INodeType { + description: INodeTypeDescription = { + displayName: 'Monica CRM', + name: 'monicaCrm', + icon: 'file:monicaCrm.png', + group: ['transform'], + version: 1, + subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', + description: 'Consume the Monica CRM API', + defaults: { + name: 'Monica CRM', + color: '#3cb371', + }, + inputs: ['main'], + outputs: ['main'], + credentials: [ + { + name: 'monicaCrmApi', + required: true, + }, + ], + properties: [ + { + displayName: 'Resource', + name: 'resource', + type: 'options', + options: [ + { + name: 'Activity', + value: 'activity', + }, + { + name: 'Call', + value: 'call', + }, + { + name: 'Contact', + value: 'contact', + }, + { + name: 'Contact Field', + value: 'contactField', + }, + { + name: 'Contact Tag', + value: 'contactTag', + }, + { + name: 'Conversation', + value: 'conversation', + }, + { + name: 'Conversation Message', + value: 'conversationMessage', + }, + { + name: 'Journal Entry', + value: 'journalEntry', + }, + { + name: 'Note', + value: 'note', + }, + { + name: 'Reminder', + value: 'reminder', + }, + { + name: 'Tag', + value: 'tag', + }, + { + name: 'Task', + value: 'task', + }, + ], + default: 'contact', + }, + ...activityOperations, + ...activityFields, + ...callOperations, + ...callFields, + ...contactOperations, + ...contactFields, + ...contactFieldOperations, + ...contactFieldFields, + ...contactTagOperations, + ...contactTagFields, + ...conversationOperations, + ...conversationFields, + ...conversationMessageOperations, + ...conversationMessageFields, + ...journalEntryOperations, + ...journalEntryFields, + ...noteOperations, + ...noteFields, + ...reminderOperations, + ...reminderFields, + ...tagOperations, + ...tagFields, + ...taskOperations, + ...taskFields, + ], + }; + + methods = { + loadOptions: { + async getActivityTypes(this: ILoadOptionsFunctions) { + const responseData = await monicaCrmApiRequest.call(this, 'GET', '/activitytypes') as LoaderGetResponse; + return toOptions(responseData); + }, + + async getTagsToAdd(this: ILoadOptionsFunctions) { + const responseData = await monicaCrmApiRequestAllItems.call( + this, 'GET', '/tags', {}, {}, { forLoader: true }, + ); + + // intentional, name required when adding + return responseData.map(({ name }) => ({ value: name, name })) as Option[]; + }, + + async getTagsToRemove(this: ILoadOptionsFunctions) { + const responseData = await monicaCrmApiRequestAllItems.call( + this, 'GET', '/tags', {}, {}, { forLoader: true }, + ); + return responseData.map(({ id, name }) => ({ value: id, name })) as Option[]; + }, + + async getContactFieldTypes(this: ILoadOptionsFunctions) { + const responseData = await monicaCrmApiRequest.call(this, 'GET', '/contactfieldtypes') as LoaderGetResponse; + return toOptions(responseData); + }, + + async getGenders(this: ILoadOptionsFunctions) { + const responseData = await monicaCrmApiRequest.call(this, 'GET', '/genders') as LoaderGetResponse; + return toOptions(responseData); + }, + }, + }; + + async execute(this: IExecuteFunctions): Promise { + const items = this.getInputData(); + const returnData: IDataObject[] = []; + + const resource = this.getNodeParameter('resource', 0) as string; + const operation = this.getNodeParameter('operation', 0) as string; + + let responseData; + + for (let i = 0; i < items.length; i++) { + + try { + + if (resource === 'activity') { + + // ********************************************************************** + // activity + // ********************************************************************** + + if (operation === 'create') { + + // ---------------------------------------- + // activity: create + // ---------------------------------------- + + // https://www.monicahq.com/api/activities#create-an-activity + + const contacts = this.getNodeParameter('contacts', i) as string; + const happenedAt = this.getNodeParameter('happenedAt', i) as string; + + const body = { + activity_type_id: this.getNodeParameter('activityTypeId', i), + contacts: contacts.split(','), + happened_at: happenedAt.split('T')[0], + summary: this.getNodeParameter('summary', i), + } as IDataObject; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + if (Object.keys(additionalFields).length) { + Object.assign(body, additionalFields); + } + + responseData = await monicaCrmApiRequest.call(this, 'POST', '/activities', body); + + } else if (operation === 'delete') { + + // ---------------------------------------- + // activity: delete + // ---------------------------------------- + + // https://www.monicahq.com/api/activities#delete-an-activity + + const activityId = this.getNodeParameter('activityId', i); + + const endpoint = `/activities/${activityId}`; + await monicaCrmApiRequest.call(this, 'DELETE', endpoint); + responseData = { success: true }; + + } else if (operation === 'get') { + + // ---------------------------------------- + // activity: get + // ---------------------------------------- + + // https://www.monicahq.com/api/activities#get-a-specific-activity + + const activityId = this.getNodeParameter('activityId', i); + + const endpoint = `/activities/${activityId}`; + responseData = await monicaCrmApiRequest.call(this, 'GET', endpoint); + + } else if (operation === 'getAll') { + + // ---------------------------------------- + // activity: getAll + // ---------------------------------------- + + // https://www.monicahq.com/api/activities#list-all-the-activities-in-your-account + + const endpoint = `/activities`; + responseData = await monicaCrmApiRequestAllItems.call(this, 'GET', endpoint); + + } else if (operation === 'update') { + + // ---------------------------------------- + // activity: update + // ---------------------------------------- + + // https://www.monicahq.com/api/activities#update-an-activity + + const activityId = this.getNodeParameter('activityId', i); + + const { data } = await monicaCrmApiRequest.call(this, 'GET', `/activities/${activityId}`); + + const body = { + activity_type_id: data.activity_type.id, + contacts: data.attendees.contacts.map((contact: IDataObject) => contact.id), + happened_at: data.happened_at, + summary: data.summary, + } as IDataObject; + + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + + if (Object.keys(updateFields).length) { + Object.assign(body, updateFields); + } + + body.happened_at = (body.happened_at as string).split('T')[0]; + + if (typeof body.contacts === 'string') { + body.contacts = (body.contacts as string).split(','); + } + + const endpoint = `/activities/${activityId}`; + responseData = await monicaCrmApiRequest.call(this, 'PUT', endpoint, body); + + } + + } else if (resource === 'call') { + + // ********************************************************************** + // call + // ********************************************************************** + + if (operation === 'create') { + + // ---------------------------------------- + // call: create + // ---------------------------------------- + + // https://www.monicahq.com/api/calls#create-a-call + + const body = { + called_at: this.getNodeParameter('calledAt', i), + contact_id: this.getNodeParameter('contactId', i), + content: this.getNodeParameter('content', i), + } as IDataObject; + + responseData = await monicaCrmApiRequest.call(this, 'POST', '/calls', body); + + } else if (operation === 'delete') { + + // ---------------------------------------- + // call: delete + // ---------------------------------------- + + // https://www.monicahq.com/api/contactfields#delete-a-call + + const callId = this.getNodeParameter('callId', i); + + responseData = await monicaCrmApiRequest.call(this, 'DELETE', `/calls/${callId}`); + responseData = { success: true }; + + } else if (operation === 'get') { + + // ---------------------------------------- + // call: get + // ---------------------------------------- + + // https://www.monicahq.com/api/calls#get-a-specific-call + + const callId = this.getNodeParameter('callId', i); + + responseData = await monicaCrmApiRequest.call(this, 'GET', `/calls/${callId}`); + + } else if (operation === 'getAll') { + + // ---------------------------------------- + // call: getAll + // ---------------------------------------- + + // https://www.monicahq.com/api/calls#list-all-the-calls-in-your-account + + const endpoint = `/calls`; + responseData = await monicaCrmApiRequestAllItems.call(this, 'GET', endpoint); + + } else if (operation === 'update') { + + // ---------------------------------------- + // call: update + // ---------------------------------------- + + // https://www.monicahq.com/api/calls#update-a-call + + const callId = this.getNodeParameter('callId', i); + + const { data } = await monicaCrmApiRequest.call(this, 'GET', `/calls/${callId}`); + + const body = { + called_at: data.called_at, + } as IDataObject; + + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + + if (Object.keys(updateFields).length) { + Object.assign(body, updateFields); + } + + responseData = await monicaCrmApiRequest.call(this, 'PUT', `/calls/${callId}`, body); + + } + + } else if (resource === 'contact') { + + // ********************************************************************** + // contact + // ********************************************************************** + + if (operation === 'create') { + + // ---------------------------------------- + // contact: create + // ---------------------------------------- + + // https://www.monicahq.com/api/contacts#create-a-contact + + const body = { + first_name: this.getNodeParameter('firstName', i), + gender_id: this.getNodeParameter('genderId', i), + } as IDataObject; + + const { + isDeceased = false, + deceasedDate, + birthdate, + ...rest + } = this.getNodeParameter('additionalFields', i) as { + isDeceased?: boolean; + deceasedDate?: string; + birthdate?: string; + } & IDataObject; + + body.is_birthdate_known = false; + body.is_deceased = isDeceased; + body.is_deceased_date_known = false; + + if (birthdate) { + body.is_birthdate_known = true; + + const [day, month, year] = getDateParts(birthdate); + body.birthdate_day = day; + body.birthdate_month = month; + body.birthdate_year = year; + } + + if (deceasedDate) { + body.is_deceased = true; + body.is_deceased_date_known = true; + + const [day, month, year] = getDateParts(deceasedDate); + body.deceased_date_day = day; + body.deceased_date_month = month; + body.deceased_date_year = year; + } + + if (Object.keys(rest).length) { + Object.assign(body, rest); + } + + responseData = await monicaCrmApiRequest.call(this, 'POST', '/contacts', body); + + } else if (operation === 'delete') { + + // ---------------------------------------- + // contact: delete + // ---------------------------------------- + + // https://www.monicahq.com/api/contacts#delete-a-contact + + const contactId = this.getNodeParameter('contactId', i); + + const endpoint = `/contacts/${contactId}`; + await monicaCrmApiRequest.call(this, 'DELETE', endpoint); + responseData = { success: true }; + + } else if (operation === 'get') { + + // ---------------------------------------- + // contact: get + // ---------------------------------------- + + // https://www.monicahq.com/api/contacts#get-a-specific-contact + + const contactId = this.getNodeParameter('contactId', i); + + const endpoint = `/contacts/${contactId}`; + responseData = await monicaCrmApiRequest.call(this, 'GET', endpoint); + + } else if (operation === 'getAll') { + + // ---------------------------------------- + // contact: getAll + // ---------------------------------------- + + // https://www.monicahq.com/api/contacts#list-all-your-contacts + + const qs = {} as IDataObject; + const filters = this.getNodeParameter('filters', i) as IDataObject; + + if (Object.keys(filters).length) { + Object.assign(qs, filters); + } + + responseData = await monicaCrmApiRequestAllItems.call(this, 'GET', '/contacts', {}, qs); + + } else if (operation === 'update') { + + // ---------------------------------------- + // contact: update + // ---------------------------------------- + + const contactId = this.getNodeParameter('contactId', i); + + const { data } = await monicaCrmApiRequest.call(this, 'GET', `/contacts/${contactId}`); + + const body = { + first_name: data.first_name, + } as IDataObject; + + const { + is_deaceased = false, + deceased_date, + birthdate, + ...rest + } = this.getNodeParameter('updateFields', i) as { + is_deaceased?: boolean; + deceased_date?: string; + birthdate?: string; + } & IDataObject; + + body.is_birthdate_known = false; + body.is_deceased = is_deaceased; + body.is_deceased_date_known = false; + + if (birthdate) { + body.is_birthdate_known = true; + + const [day, month, year] = getDateParts(birthdate); + body.birthdate_day = day; + body.birthdate_month = month; + body.birthdate_year = year; + } + + if (deceased_date) { + body.is_deceased = true; + body.is_deceased_date_known = true; + + const [day, month, year] = getDateParts(deceased_date); + body.deceased_date_day = day; + body.deceased_date_month = month; + body.deceased_date_year = year; + } + + if (Object.keys(rest).length) { + Object.assign(body, rest); + } + + const endpoint = `/contacts/${contactId}`; + responseData = await monicaCrmApiRequest.call(this, 'PUT', endpoint, body); + + } + + } else if (resource === 'contactField') { + + // ********************************************************************** + // contactField + // ********************************************************************** + + if (operation === 'create') { + + // ---------------------------------------- + // contactField: create + // ---------------------------------------- + + // https://www.monicahq.com/api/contactfields#create-a-contact-field + + const body = { + contact_field_type_id: this.getNodeParameter('contactFieldTypeId', i), + contact_id: this.getNodeParameter('contactId', i), + data: this.getNodeParameter('data', i), + } as IDataObject; + + responseData = await monicaCrmApiRequest.call(this, 'POST', '/contactfields', body); + + } else if (operation === 'delete') { + + // ---------------------------------------- + // contactField: delete + // ---------------------------------------- + + // https://www.monicahq.com/api/contactfields#delete-a-contact-field + + const contactFieldId = this.getNodeParameter('contactFieldId', i); + + const endpoint = `/contactfields/${contactFieldId}`; + await monicaCrmApiRequest.call(this, 'DELETE', endpoint); + responseData = { success: true }; + + } else if (operation === 'get') { + + // ---------------------------------------- + // contactField: get + // ---------------------------------------- + + // https://www.monicahq.com/api/contactfields#get-a-specific-contact-field + + const contactFieldId = this.getNodeParameter('contactFieldId', i); + + const endpoint = `/contactfields/${contactFieldId}`; + responseData = await monicaCrmApiRequest.call(this, 'GET', endpoint); + + } else if (operation === 'getAll') { + + // ---------------------------------------- + // contactField: getAll + // ---------------------------------------- + + // https://www.monicahq.com/api/contactfields#list-all-the-contact-fields-of-a-specific-contact + + const contactId = this.getNodeParameter('contactId', i); + + const endpoint = `/contact/${contactId}/contactfields`; + responseData = await monicaCrmApiRequestAllItems.call(this, 'GET', endpoint); + + } else if (operation === 'update') { + + // ---------------------------------------- + // contactField: update + // ---------------------------------------- + + // https://www.monicahq.com/api/contactfields#update-a-contact-field + + const body = { + contact_field_type_id: this.getNodeParameter('contactFieldTypeId', i), + contact_id: this.getNodeParameter('contactId', i), + data: this.getNodeParameter('data', i), + } as IDataObject; + + const contactFieldId = this.getNodeParameter('contactFieldId', i); + + const endpoint = `/contactfields/${contactFieldId}`; + responseData = await monicaCrmApiRequest.call(this, 'PUT', endpoint, body); + + } + + } else if (resource === 'contactTag') { + + // ********************************************************************** + // contactTag + // ********************************************************************** + + if (operation === 'add') { + + // ---------------------------------------- + // contactTag: add + // ---------------------------------------- + + // https://www.monicahq.com/api/tags#associate-a-tag-to-a-contact + + const body = { + tags: this.getNodeParameter('tagsToAdd', i), + } as IDataObject; + + const contactId = this.getNodeParameter('contactId', i); + + const endpoint = `/contacts/${contactId}/setTags`; + responseData = await monicaCrmApiRequest.call(this, 'POST', endpoint, body); + + } else if (operation === 'remove') { + + // ---------------------------------------- + // tag: remove + // ---------------------------------------- + + // https://www.monicahq.com/api/tags#remove-a-specific-tag-from-a-contact + + const body = { + tags: this.getNodeParameter('tagsToRemove', i), + } as IDataObject; + + const contactId = this.getNodeParameter('contactId', i); + + const endpoint = `/contacts/${contactId}/unsetTag`; + responseData = await monicaCrmApiRequest.call(this, 'POST', endpoint, body); + + } + + } else if (resource === 'conversation') { + + // ********************************************************************** + // conversation + // ********************************************************************** + + + + if (operation === 'create') { + + // ---------------------------------------- + // conversation: create + // ---------------------------------------- + + // https://www.monicahq.com/api/conversations#create-a-conversation + + const body = { + contact_field_type_id: this.getNodeParameter('contactFieldTypeId', i), + contact_id: this.getNodeParameter('contactId', i), + happened_at: this.getNodeParameter('happenedAt', i), + } as IDataObject; + + responseData = await monicaCrmApiRequest.call(this, 'POST', '/conversations', body); + + } else if (operation === 'delete') { + + // ---------------------------------------- + // conversation: delete + // ---------------------------------------- + + // https://www.monicahq.com/api/contactfields#delete-a-contact-field + + const conversationId = this.getNodeParameter('conversationId', i); + + const endpoint = `/conversations/${conversationId}`; + await monicaCrmApiRequest.call(this, 'DELETE', endpoint); + responseData = { success: true }; + + } else if (operation === 'get') { + + // ---------------------------------------- + // conversation: get + // ---------------------------------------- + + // https://www.monicahq.com/api/conversations#get-a-specific-conversation + + const conversationId = this.getNodeParameter('conversationId', i); + + const endpoint = `/conversations/${conversationId}`; + responseData = await monicaCrmApiRequest.call(this, 'GET', endpoint); + + } else if (operation === 'update') { + + // ---------------------------------------- + // conversation: update + // ---------------------------------------- + + // https://www.monicahq.com/api/conversations#update-a-conversation + + const body = { + contact_field_type_id: this.getNodeParameter('contactFieldTypeId', i), + happened_at: this.getNodeParameter('happenedAt', i), + } as IDataObject; + + const conversationId = this.getNodeParameter('conversationId', i); + + const endpoint = `/conversations/${conversationId}`; + responseData = await monicaCrmApiRequest.call(this, 'PUT', endpoint, body); + + } + } else if (resource === 'conversationMessage') { + if (operation === 'add') { + + // ---------------------------------------- + // conversationMessage: add + // ---------------------------------------- + + // https://www.monicahq.com/api/conversations#add-a-message-to-a-conversation + + const conversationId = this.getNodeParameter('conversationId', i); + + const endpoint = `/conversations/${conversationId}/messages`; + + const { data } = await monicaCrmApiRequest.call(this, 'GET', `/conversations/${conversationId}`); + + const body = { + contact_id: data.contact.id, + content: this.getNodeParameter('content', i), + written_at: this.getNodeParameter('writtenAt', i), + written_by_me: this.getNodeParameter('writtenByMe', i), + } as IDataObject; + + responseData = await monicaCrmApiRequest.call(this, 'POST', endpoint, body); + + } else if (operation === 'update') { + + // ---------------------------------------- + // conversationMessage: update + // ---------------------------------------- + + // https://www.monicahq.com/api/conversations#update-a-message-in-a-conversation + const conversationId = this.getNodeParameter('conversationId', i); + const messageId = this.getNodeParameter('messageId', i) as string; + const endpoint = `/conversations/${conversationId}/messages/${messageId}`; + + const updateFields = this.getNodeParameter('updateFields', i, {}) as IDataObject; + + const { data } = await monicaCrmApiRequest.call(this, 'GET', `/conversations/${conversationId}`); + + const message = data.messages.filter((message: IDataObject) => message.id === parseInt(messageId, 10))[0]; + + const body = { + contact_id: data.contact.id, + content: message.content, + written_at: message.written_at, + written_by_me: message.written_by_me, + } as IDataObject; + + if (Object.keys(updateFields).length) { + Object.assign(body, updateFields); + } + + responseData = await monicaCrmApiRequest.call(this, 'PUT', endpoint, body); + } + + } else if (resource === 'journalEntry') { + + // ********************************************************************** + // journalEntry + // ********************************************************************** + + if (operation === 'create') { + + // ---------------------------------------- + // journalEntry: create + // ---------------------------------------- + + // https://www.monicahq.com/api/notes#create-a-journal-entry + + const body = { + title: this.getNodeParameter('title', i), + post: this.getNodeParameter('post', i), + } as IDataObject; + + responseData = await monicaCrmApiRequest.call(this, 'POST', '/journal', body); + + } else if (operation === 'delete') { + + // ---------------------------------------- + // journalEntry: delete + // ---------------------------------------- + + // https://www.monicahq.com/api/journal#delete-a-journal-entry + + const journalId = this.getNodeParameter('journalId', i); + + await monicaCrmApiRequest.call(this, 'DELETE', `/journal/${journalId}`); + responseData = { success: true }; + + } else if (operation === 'get') { + + // ---------------------------------------- + // journalEntry: get + // ---------------------------------------- + + // https://www.monicahq.com/api/journal#get-a-specific-journal-entry + + const journalId = this.getNodeParameter('journalId', i); + + responseData = await monicaCrmApiRequest.call(this, 'GET', `/journal/${journalId}`); + + } else if (operation === 'getAll') { + + // ---------------------------------------- + // journalEntry: getAll + // ---------------------------------------- + + // https://www.monicahq.com/api/journal#list-all-the-entries-in-your-journal + + responseData = await monicaCrmApiRequestAllItems.call(this, 'GET', '/journal'); + + } else if (operation === 'update') { + + // ---------------------------------------- + // journalEntry: update + // ---------------------------------------- + + // https://www.monicahq.com/api/journal#update-a-journal-entry + + const journalId = this.getNodeParameter('journalId', i); + + const { data } = await monicaCrmApiRequest.call(this, 'GET', `/journal/${journalId}`); + + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + + const body = { + post: data.post, + title: data.title, + } as IDataObject; + + if (Object.keys(updateFields).length) { + Object.assign(body, updateFields); + } + + responseData = await monicaCrmApiRequest.call(this, 'PUT', `/journal/${journalId}`, body); + + } + + } else if (resource === 'note') { + + // ********************************************************************** + // note + // ********************************************************************** + + if (operation === 'create') { + + // ---------------------------------------- + // note: create + // ---------------------------------------- + + // https://www.monicahq.com/api/notes#create-a-note + + const body = { + body: this.getNodeParameter('body', i), + contact_id: this.getNodeParameter('contactId', i), + } as IDataObject; + + body.is_favorited = this.getNodeParameter('additionalFields.isFavorited', i, false); + + responseData = await monicaCrmApiRequest.call(this, 'POST', '/notes', body); + + } else if (operation === 'delete') { + + // ---------------------------------------- + // note: delete + // ---------------------------------------- + + // https://www.monicahq.com/api/notes#delete-a-note + + const noteId = this.getNodeParameter('noteId', i); + + await monicaCrmApiRequest.call(this, 'DELETE', `/notes/${noteId}`); + responseData = { success: true }; + + } else if (operation === 'get') { + + // ---------------------------------------- + // note: get + // ---------------------------------------- + + // https://www.monicahq.com/api/notes#get-a-specific-note + + const noteId = this.getNodeParameter('noteId', i); + + responseData = await monicaCrmApiRequest.call(this, 'GET', `/notes/${noteId}`); + + } else if (operation === 'getAll') { + + // ---------------------------------------- + // note: getAll + // ---------------------------------------- + + // https://www.monicahq.com/api/notes#list-all-the-notes-in-your-account + + const endpoint = `/notes`; + responseData = await monicaCrmApiRequestAllItems.call(this, 'GET', endpoint); + + } else if (operation === 'update') { + + // ---------------------------------------- + // note: update + // ---------------------------------------- + + // https://www.monicahq.com/api/notes#update-a-note + + const noteId = this.getNodeParameter('noteId', i); + + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + + const { data } = await monicaCrmApiRequest.call(this, 'GET', `/notes/${noteId}`); + + const body = { + body: data.body, + contact_id: data.contact.id, + } as IDataObject; + + + if (Object.keys(updateFields).length) { + Object.assign(body, updateFields); + } + + responseData = await monicaCrmApiRequest.call(this, 'PUT', `/notes/${noteId}`, body); + + } + + } else if (resource === 'reminder') { + + // ********************************************************************** + // reminder + // ********************************************************************** + + if (operation === 'create') { + + // ---------------------------------------- + // reminder: create + // ---------------------------------------- + + // https://www.monicahq.com/api/notes#create-a-reminder + + const initialDate = this.getNodeParameter('initialDate', i) as string; + + const body = { + contact_id: this.getNodeParameter('contactId', i), + frequency_type: this.getNodeParameter('frequencyType', i), + frequency_number: this.getNodeParameter('frequencyNumber', i, 1), + initial_date: initialDate.split('T')[0], + title: this.getNodeParameter('title', i), + } as IDataObject; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + if (Object.keys(additionalFields).length) { + Object.assign(body, additionalFields); + } + + responseData = await monicaCrmApiRequest.call(this, 'POST', '/reminders', body); + + } else if (operation === 'delete') { + + // ---------------------------------------- + // reminder: delete + // ---------------------------------------- + + // https://www.monicahq.com/api/reminder#delete-a-reminder + + const reminderId = this.getNodeParameter('reminderId', i); + + const endpoint = `/reminders/${reminderId}`; + await monicaCrmApiRequest.call(this, 'DELETE', endpoint); + responseData = { success: true }; + + } else if (operation === 'get') { + + // ---------------------------------------- + // reminder: get + // ---------------------------------------- + + // https://www.monicahq.com/api/reminder#get-a-specific-reminder + + const reminderId = this.getNodeParameter('reminderId', i); + + const endpoint = `/reminders/${reminderId}`; + responseData = await monicaCrmApiRequest.call(this, 'GET', endpoint); + + } else if (operation === 'getAll') { + + // ---------------------------------------- + // reminder: getAll + // ---------------------------------------- + + // https://www.monicahq.com/api/reminders#list-all-the-reminders-in-your-account + + responseData = await monicaCrmApiRequestAllItems.call(this, 'GET', '/reminders'); + + } else if (operation === 'update') { + + // ---------------------------------------- + // reminder: update + // ---------------------------------------- + + // https://www.monicahq.com/api/reminders#update-a-reminder + + const reminderId = this.getNodeParameter('reminderId', i); + + const { data } = await monicaCrmApiRequest.call(this, 'GET', `/reminders/${reminderId}`); + + const body = { + contact_id: data.contact.id, + frequency_type: data.frequency_type, + frequency_number: data.frequency_number, + initial_date: data.initial_date, + title: data.title, + } as IDataObject; + + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + + if (Object.keys(updateFields).length) { + Object.assign(body, updateFields); + } + + body.initial_date = (body.initial_date as string).split('T')[0]; + + const endpoint = `/reminders/${reminderId}`; + responseData = await monicaCrmApiRequest.call(this, 'PUT', endpoint, body); + + } + + } else if (resource === 'tag') { + + // ********************************************************************** + // tag + // ********************************************************************** + + if (operation === 'create') { + + // ---------------------------------------- + // tag: create + // ---------------------------------------- + + // https://www.monicahq.com/api/tags#create-a-tag + + const body = { + name: this.getNodeParameter('name', i), + } as IDataObject; + + responseData = await monicaCrmApiRequest.call(this, 'POST', '/tags', body); + + } else if (operation === 'delete') { + + // ---------------------------------------- + // tag: delete + // ---------------------------------------- + + // https://www.monicahq.com/api/tag#delete-a-tag + + const tagId = this.getNodeParameter('tagId', i); + + await monicaCrmApiRequest.call(this, 'DELETE', `/tags/${tagId}`); + responseData = { success: true }; + + } else if (operation === 'get') { + + // ---------------------------------------- + // tag: get + // ---------------------------------------- + + // https://www.monicahq.com/api/task#get-a-specific-tag + + const tagId = this.getNodeParameter('tagId', i); + + responseData = await monicaCrmApiRequest.call(this, 'GET', `/tags/${tagId}`); + + } else if (operation === 'getAll') { + + // ---------------------------------------- + // tag: getAll + // ---------------------------------------- + + // https://www.monicahq.com/api/tags#list-all-your-tags + + responseData = await monicaCrmApiRequestAllItems.call(this, 'GET', '/tags'); + + } else if (operation === 'update') { + + // ---------------------------------------- + // tag: update + // ---------------------------------------- + + // https://www.monicahq.com/api/tags#update-a-tag + + const body = { + name: this.getNodeParameter('name', i), + } as IDataObject; + + const tagId = this.getNodeParameter('tagId', i); + + responseData = await monicaCrmApiRequest.call(this, 'PUT', `/tags/${tagId}`, body); + + } + + } else if (resource === 'task') { + + // ********************************************************************** + // task + // ********************************************************************** + + if (operation === 'create') { + + // ---------------------------------------- + // task: create + // ---------------------------------------- + + // https://www.monicahq.com/api/notes#create-a-task + + const body = { + contact_id: this.getNodeParameter('contactId', i), + title: this.getNodeParameter('title', i), + } as IDataObject; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + if (Object.keys(additionalFields).length) { + Object.assign(body, additionalFields); + } + + responseData = await monicaCrmApiRequest.call(this, 'POST', '/tasks', body); + + } else if (operation === 'delete') { + + // ---------------------------------------- + // task: delete + // ---------------------------------------- + + // https://www.monicahq.com/api/task#delete-a-task + + const taskId = this.getNodeParameter('taskId', i); + + await monicaCrmApiRequest.call(this, 'DELETE', `/tasks/${taskId}`); + responseData = { success: true }; + + } else if (operation === 'get') { + + // ---------------------------------------- + // task: get + // ---------------------------------------- + + // https://www.monicahq.com/api/task#get-a-specific-task + + const taskId = this.getNodeParameter('taskId', i); + + responseData = await monicaCrmApiRequest.call(this, 'GET', `/tasks/${taskId}`); + + } else if (operation === 'getAll') { + + // ---------------------------------------- + // task: getAll + // ---------------------------------------- + + // https://www.monicahq.com/api/tasks#list-all-the-tasks-of-a-specific-contact + + const endpoint = `/tasks`; + responseData = await monicaCrmApiRequestAllItems.call(this, 'GET', endpoint); + + } else if (operation === 'update') { + + // ---------------------------------------- + // task: update + // ---------------------------------------- + + // https://www.monicahq.com/api/task#update-a-task + + const taskId = this.getNodeParameter('taskId', i); + + const { data } = await monicaCrmApiRequest.call(this, 'GET', `/tasks/${taskId}`); + + const body = { + contact_id: data.contact.id, + title: data.title, + completed: data.completed, + } as IDataObject; + + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + + if (Object.keys(updateFields).length) { + Object.assign(body, updateFields); + } + + responseData = await monicaCrmApiRequest.call(this, 'PUT', `/tasks/${taskId}`, body); + } + + } + + } catch (error) { + if (this.continueOnFail()) { + returnData.push({ json: { error: error.message } }); + continue; + } + + throw error; + } + + if ([ + 'create', + 'get', + 'update', + 'add', + ].includes(operation)) { + responseData = responseData.data; + } + + Array.isArray(responseData) + ? returnData.push(...responseData) + : returnData.push(responseData); + + } + + return [this.helpers.returnJsonArray(returnData)]; + } +} diff --git a/packages/nodes-base/nodes/MonicaCrm/descriptions/ActivityDescription.ts b/packages/nodes-base/nodes/MonicaCrm/descriptions/ActivityDescription.ts new file mode 100644 index 0000000000..fe326632bd --- /dev/null +++ b/packages/nodes-base/nodes/MonicaCrm/descriptions/ActivityDescription.ts @@ -0,0 +1,324 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const activityOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'activity', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create an activity', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete an activity', + }, + { + name: 'Get', + value: 'get', + description: 'Retrieve an activity', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Retrieve all activities', + }, + { + name: 'Update', + value: 'update', + description: 'Update an activity', + }, + ], + default: 'create', + }, +] as INodeProperties[]; + +export const activityFields = [ + // ---------------------------------------- + // activity: create + // ---------------------------------------- + { + displayName: 'Activity Type', + name: 'activityTypeId', + type: 'options', + required: true, + default: '', + typeOptions: { + loadOptionsMethod: 'getActivityTypes', + }, + displayOptions: { + show: { + resource: [ + 'activity', + ], + operation: [ + 'create', + ], + }, + }, + }, + { + displayName: 'Contacts', + name: 'contacts', + description: 'Comma-separated list of IDs of the contacts to associate the activity with', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'activity', + ], + operation: [ + 'create', + ], + }, + }, + }, + { + displayName: 'Happened At', + name: 'happenedAt', + description: 'Date when the activity happened', + type: 'dateTime', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'activity', + ], + operation: [ + 'create', + ], + }, + }, + }, + { + displayName: 'Summary', + name: 'summary', + description: 'Brief description of the activity - max 255 characters', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'activity', + ], + operation: [ + 'create', + ], + }, + }, + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'activity', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + { + displayName: 'Description', + name: 'description', + type: 'string', + default: '', + description: 'Description of the activity - max 100,000 characters', + typeOptions: { + alwaysOpenEditWindow: true, + }, + }, + ], + }, + + // ---------------------------------------- + // activity: delete + // ---------------------------------------- + { + displayName: 'Activity ID', + name: 'activityId', + description: 'ID of the activity to delete', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'activity', + ], + operation: [ + 'delete', + ], + }, + }, + }, + + // ---------------------------------------- + // activity: get + // ---------------------------------------- + { + displayName: 'Activity ID', + name: 'activityId', + description: 'ID of the activity to retrieve', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'activity', + ], + operation: [ + 'get', + ], + }, + }, + }, + + // ---------------------------------------- + // activity: getAll + // ---------------------------------------- + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + default: false, + description: 'Whether to return all results or only up to a given limit', + displayOptions: { + show: { + resource: [ + 'activity', + ], + operation: [ + 'getAll', + ], + }, + }, + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + default: 50, + description: 'How many results to return', + typeOptions: { + minValue: 1, + }, + displayOptions: { + show: { + resource: [ + 'activity', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + }, + + // ---------------------------------------- + // activity: update + // ---------------------------------------- + { + displayName: 'Activity ID', + name: 'activityId', + description: 'ID of the activity to update', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'activity', + ], + operation: [ + 'update', + ], + }, + }, + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'activity', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Activity Type', + name: 'activity_type_id', + type: 'options', + default: '', + typeOptions: { + loadOptionsMethod: 'getActivityTypes', + }, + }, + { + displayName: 'Contacts', + name: 'contacts', + description: 'IDs of the contacts to associate the activity with', + type: 'string', + default: '', + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + default: '', + description: 'Description to add more details on the activity - max 100,000 characters', + typeOptions: { + alwaysOpenEditWindow: true, + }, + }, + { + displayName: 'Happened At', + name: 'happened_at', + description: 'Date when the activity happened', + type: 'dateTime', + default: '', + }, + { + displayName: 'Summary', + name: 'summary', + description: 'Brief description of the activity - max 255 characters', + type: 'string', + default: '', + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/MonicaCrm/descriptions/CallDescription.ts b/packages/nodes-base/nodes/MonicaCrm/descriptions/CallDescription.ts new file mode 100644 index 0000000000..1336515f70 --- /dev/null +++ b/packages/nodes-base/nodes/MonicaCrm/descriptions/CallDescription.ts @@ -0,0 +1,259 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const callOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'call', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a call', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a call', + }, + { + name: 'Get', + value: 'get', + description: 'Retrieve a call', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Retrieve all calls', + }, + { + name: 'Update', + value: 'update', + description: 'Update a call', + }, + ], + default: 'create', + }, +] as INodeProperties[]; + +export const callFields = [ + // ---------------------------------------- + // call: create + // ---------------------------------------- + { + displayName: 'Contact ID', + name: 'contactId', + description: 'ID of the contact to associate the call with', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'call', + ], + operation: [ + 'create', + ], + }, + }, + }, + { + displayName: 'Called At', + name: 'calledAt', + description: 'Date when the call happened', + type: 'dateTime', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'call', + ], + operation: [ + 'create', + ], + }, + }, + }, + { + displayName: 'Description', + name: 'content', + description: 'Description of the call - max 100,000 characters', + typeOptions: { + alwaysOpenEditWindow: true, + }, + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'call', + ], + operation: [ + 'create', + ], + }, + }, + }, + + // ---------------------------------------- + // call: delete + // ---------------------------------------- + { + displayName: 'Call ID', + name: 'callId', + description: 'ID of the call to delete', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'call', + ], + operation: [ + 'delete', + ], + }, + }, + }, + + // ---------------------------------------- + // call: get + // ---------------------------------------- + { + displayName: 'Call ID', + name: 'callId', + description: 'ID of the call to retrieve', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'call', + ], + operation: [ + 'get', + ], + }, + }, + }, + + // ---------------------------------------- + // call: getAll + // ---------------------------------------- + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + default: false, + description: 'Whether to return all results or only up to a given limit', + displayOptions: { + show: { + resource: [ + 'call', + ], + operation: [ + 'getAll', + ], + }, + }, + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + default: 50, + description: 'How many results to return', + typeOptions: { + minValue: 1, + }, + displayOptions: { + show: { + resource: [ + 'call', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + }, + + // ---------------------------------------- + // call: update + // ---------------------------------------- + { + displayName: 'Call ID', + name: 'callId', + description: 'ID of the call to update', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'call', + ], + operation: [ + 'update', + ], + }, + }, + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'call', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Called At', + name: 'calledAt', + description: 'Date when the call happened', + type: 'dateTime', + default: '', + }, + { + displayName: 'Contact ID', + name: 'contactId', + description: 'ID of the contact to associate the call with', + type: 'string', + default: '', + }, + { + displayName: 'Description', + name: 'content', + description: 'Description of the call - max 100,000 characters', + type: 'string', + default: '', + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/MonicaCrm/descriptions/ContactDescription.ts b/packages/nodes-base/nodes/MonicaCrm/descriptions/ContactDescription.ts new file mode 100644 index 0000000000..63027d6ace --- /dev/null +++ b/packages/nodes-base/nodes/MonicaCrm/descriptions/ContactDescription.ts @@ -0,0 +1,400 @@ +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 contact', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a contact', + }, + { + name: 'Get', + value: 'get', + description: 'Retrieve a contact', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Retrieve all contacts', + }, + { + name: 'Update', + value: 'update', + description: 'Update a contact', + }, + ], + default: 'create', + }, +] as INodeProperties[]; + +export const contactFields = [ + // ---------------------------------------- + // contact: create + // ---------------------------------------- + { + displayName: 'First Name', + name: 'firstName', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'create', + ], + }, + }, + }, + { + displayName: 'Gender', + name: 'genderId', + type: 'options', + required: true, + default: '', + typeOptions: { + loadOptionsMethod: 'getGenders', + }, + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'create', + ], + }, + }, + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + { + displayName: 'Birthdate', + name: 'birthdate', + type: 'dateTime', + default: '', + }, + { + displayName: 'Deceased Date', + name: 'deceasedDate', + type: 'dateTime', + default: '', + }, + { + displayName: 'Is Deceased', + name: 'isDeceased', + description: 'Whether the contact has passed away', + type: 'boolean', + default: false, + }, + { + displayName: 'Last Name', + name: 'last_name', + type: 'string', + default: '', + }, + { + displayName: 'Nickname', + name: 'nickname', + type: 'string', + default: '', + }, + { + displayName: 'Type', + name: 'is_partial', + type: 'options', + default: false, + options: [ + { + name: 'Real', + value: false, + description: 'Contact with their own contact sheet', + }, + { + name: 'Partial', + value: true, + description: 'Contact without their own contact sheet', + }, + ], + }, + ], + }, + + // ---------------------------------------- + // contact: delete + // ---------------------------------------- + { + displayName: 'Contact ID', + name: 'contactId', + description: 'ID of the contact to delete', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'delete', + ], + }, + }, + }, + + // ---------------------------------------- + // contact: get + // ---------------------------------------- + { + displayName: 'Contact ID', + name: 'contactId', + description: 'ID of the contact to retrieve', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'get', + ], + }, + }, + }, + + // ---------------------------------------- + // contact: getAll + // ---------------------------------------- + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + default: false, + description: 'Whether to return all results or only up to a given limit', + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'getAll', + ], + }, + }, + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + default: 50, + description: 'How many results to return', + typeOptions: { + minValue: 1, + }, + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + }, + { + displayName: 'Filters', + name: 'filters', + type: 'collection', + placeholder: 'Add Field', + default: {}, + options: [ + { + displayName: 'Search Term', + name: 'query', + type: 'string', + default: '', + description: 'Search term to filter results by', + }, + { + displayName: 'Sort', + name: 'sort', + type: 'options', + options: [ + { + name: 'Ascended Created At', + value: 'created_at', + }, + { + name: 'Descended Created At', + value: '-created_at', + }, + { + name: 'Ascended Updated At', + value: 'updated_at', + }, + { + name: 'Descended Updated At', + value: '-updated_at', + }, + ], + default: '', + }, + ], + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'getAll', + ], + }, + }, + }, + + // ---------------------------------------- + // contact: update + // ---------------------------------------- + { + displayName: 'Contact ID', + name: 'contactId', + description: 'ID of the contact to update', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'update', + ], + }, + }, + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Birthdate', + name: 'birthdate', + type: 'dateTime', + default: '', + }, + { + displayName: 'Deceased Date', + name: 'deceased_date', + type: 'dateTime', + default: '', + }, + { + displayName: 'First Name', + name: 'first_name', + type: 'string', + default: '', + }, + { + displayName: 'Gender', + name: 'gender_id', + type: 'options', + default: '', + typeOptions: { + loadOptionsMethod: 'getGenders', + }, + }, + { + displayName: 'Is Deceased', + name: 'is_deceased', + description: 'Whether the contact has passed away', + type: 'boolean', + default: false, + }, + { + displayName: 'Last Name', + name: 'last_name', + type: 'string', + default: '', + }, + { + displayName: 'Nickname', + name: 'nickname', + type: 'string', + default: '', + }, + { + displayName: 'Type', + name: 'is_partial', + type: 'options', + default: false, + options: [ + { + name: 'Real', + value: false, + description: 'Contact with their own contact sheet', + }, + { + name: 'Partial', + value: true, + description: 'Contact without their own contact sheet', + }, + ], + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/MonicaCrm/descriptions/ContactFieldDescription.ts b/packages/nodes-base/nodes/MonicaCrm/descriptions/ContactFieldDescription.ts new file mode 100644 index 0000000000..cca52a7e63 --- /dev/null +++ b/packages/nodes-base/nodes/MonicaCrm/descriptions/ContactFieldDescription.ts @@ -0,0 +1,292 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const contactFieldOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'contactField', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a contact field', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a contact field', + }, + { + name: 'Get', + value: 'get', + description: 'Retrieve a contact field', + }, + // { + // name: 'Get All', + // value: 'getAll', + // description: 'Retrieve all contact fields', + // }, + { + name: 'Update', + value: 'update', + description: 'Update a contact field', + }, + ], + default: 'create', + }, +] as INodeProperties[]; + +export const contactFieldFields = [ + // ---------------------------------------- + // contactField: create + // ---------------------------------------- + { + displayName: 'Contact ID', + name: 'contactId', + description: 'ID of the contact to associate the contact field with', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'contactField', + ], + operation: [ + 'create', + ], + }, + }, + }, + { + displayName: 'Contact Field Type', + name: 'contactFieldTypeId', + type: 'options', + required: true, + typeOptions: { + loadOptionsMethod: 'getContactFieldTypes', + }, + default: '', + displayOptions: { + show: { + resource: [ + 'contactField', + ], + operation: [ + 'create', + ], + }, + }, + }, + { + displayName: 'Content', + name: 'data', + description: 'Content of the contact field - max 255 characters', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'contactField', + ], + operation: [ + 'create', + ], + }, + }, + }, + + // ---------------------------------------- + // contactField: delete + // ---------------------------------------- + { + displayName: 'Contact Field ID', + name: 'contactFieldId', + description: 'ID of the contactField to delete', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'contactField', + ], + operation: [ + 'delete', + ], + }, + }, + }, + + // ---------------------------------------- + // contactField: get + // ---------------------------------------- + { + displayName: 'Contact Field ID', + name: 'contactFieldId', + description: 'ID of the contact field to retrieve', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'contactField', + ], + operation: [ + 'get', + ], + }, + }, + }, + + // ---------------------------------------- + // contactField: getAll + // ---------------------------------------- + { + displayName: 'Contact ID', + name: 'contactId', + description: 'ID of the contact whose fields to retrieve', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'contactField', + ], + operation: [ + 'getAll', + ], + }, + }, + }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + default: false, + description: 'Whether to return all results or only up to a given limit', + displayOptions: { + show: { + resource: [ + 'contactField', + ], + operation: [ + 'getAll', + ], + }, + }, + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + default: 50, + description: 'How many results to return', + typeOptions: { + minValue: 1, + }, + displayOptions: { + show: { + resource: [ + 'contactField', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + }, + + // ---------------------------------------- + // contactField: update + // ---------------------------------------- + { + displayName: 'Contact ID', + name: 'contactId', + description: 'ID of the contact to associate the contact field with', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'contactField', + ], + operation: [ + 'update', + ], + }, + }, + }, + { + displayName: 'Contact Field ID', + name: 'contactFieldId', + description: 'ID of the contact field to update', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'contactField', + ], + operation: [ + 'update', + ], + }, + }, + }, + { + displayName: 'Contact Field Type ID', + name: 'contactFieldTypeId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getContactFieldTypes', + }, + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'contactField', + ], + operation: [ + 'update', + ], + }, + }, + }, + { + displayName: 'Content', + name: 'data', + description: 'Content of the contact field - max 255 characters', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'contactField', + ], + operation: [ + 'update', + ], + }, + }, + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/MonicaCrm/descriptions/ContactTagDescription.ts b/packages/nodes-base/nodes/MonicaCrm/descriptions/ContactTagDescription.ts new file mode 100644 index 0000000000..d786e832c7 --- /dev/null +++ b/packages/nodes-base/nodes/MonicaCrm/descriptions/ContactTagDescription.ts @@ -0,0 +1,117 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const contactTagOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'contactTag', + ], + }, + }, + options: [ + { + name: 'Add', + value: 'add', + }, + { + name: 'Remove', + value: 'remove', + }, + ], + default: 'add', + }, +] as INodeProperties[]; + +export const contactTagFields = [ + // ---------------------------------------- + // tag: add + // ---------------------------------------- + { + displayName: 'Contact ID', + name: 'contactId', + description: 'ID of the contact to add a tag to', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'contactTag', + ], + operation: [ + 'add', + ], + }, + }, + }, + { + displayName: 'Tags', + name: 'tagsToAdd', + description: 'Tags to add to the contact', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getTagsToAdd', + }, + required: true, + default: [], + displayOptions: { + show: { + resource: [ + 'contactTag', + ], + operation: [ + 'add', + ], + }, + }, + }, + + // ---------------------------------------- + // tag: remove + // ---------------------------------------- + { + displayName: 'Contact ID', + name: 'contactId', + description: 'ID of the contact to remove the tag from', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'contactTag', + ], + operation: [ + 'remove', + ], + }, + }, + }, + { + displayName: 'Tags', + name: 'tagsToRemove', + description: 'Tags to remove from the contact', + type: 'multiOptions', + required: true, + typeOptions: { + loadOptionsMethod: 'getTagsToRemove', + }, + default: [], + displayOptions: { + show: { + resource: [ + 'contactTag', + ], + operation: [ + 'remove', + ], + }, + }, + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/MonicaCrm/descriptions/ConversationDescription.ts b/packages/nodes-base/nodes/MonicaCrm/descriptions/ConversationDescription.ts new file mode 100644 index 0000000000..9c9f97a392 --- /dev/null +++ b/packages/nodes-base/nodes/MonicaCrm/descriptions/ConversationDescription.ts @@ -0,0 +1,207 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const conversationOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'conversation', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a conversation', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a conversation', + }, + { + name: 'Get', + value: 'get', + description: 'Retrieve a conversation', + }, + { + name: 'Update', + value: 'update', + description: 'Update a conversation', + }, + ], + default: 'create', + }, +] as INodeProperties[]; + +export const conversationFields = [ + // ---------------------------------------- + // conversation: create + // ---------------------------------------- + { + displayName: 'Contact ID', + name: 'contactId', + description: 'ID of the contact to associate the conversation with', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'conversation', + ], + operation: [ + 'create', + ], + }, + }, + }, + { + displayName: 'Contact Field Type', + name: 'contactFieldTypeId', + type: 'options', + required: true, + default: '', + typeOptions: { + loadOptionsMethod: 'getContactFieldTypes', + }, + displayOptions: { + show: { + resource: [ + 'conversation', + ], + operation: [ + 'create', + ], + }, + }, + }, + { + displayName: 'Happened At', + name: 'happenedAt', + description: 'Date when the conversation happened', + type: 'dateTime', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'conversation', + ], + operation: [ + 'create', + ], + }, + }, + }, + + // ---------------------------------------- + // conversation: delete + // ---------------------------------------- + { + displayName: 'Conversation ID', + name: 'conversationId', + description: 'ID of the conversation to delete', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'conversation', + ], + operation: [ + 'delete', + ], + }, + }, + }, + + // ---------------------------------------- + // conversation: get + // ---------------------------------------- + { + displayName: 'Conversation ID', + name: 'conversationId', + description: 'ID of the conversation to retrieve', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'conversation', + ], + operation: [ + 'get', + ], + }, + }, + }, + + // ---------------------------------------- + // conversation: update + // ---------------------------------------- + { + displayName: 'Conversation ID', + name: 'conversationId', + description: 'ID of the conversation to update', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'conversation', + ], + operation: [ + 'update', + ], + }, + }, + }, + { + displayName: 'Contact Field Type', + name: 'contactFieldTypeId', + type: 'options', + required: true, + default: '', + typeOptions: { + loadOptionsMethod: 'getContactFieldTypes', + }, + displayOptions: { + show: { + resource: [ + 'conversation', + ], + operation: [ + 'update', + ], + }, + }, + }, + { + displayName: 'Happened At', + name: 'happenedAt', + description: 'Date when the conversation happened', + type: 'dateTime', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'conversation', + ], + operation: [ + 'update', + ], + }, + }, + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/MonicaCrm/descriptions/ConversationMessageDescription.ts b/packages/nodes-base/nodes/MonicaCrm/descriptions/ConversationMessageDescription.ts new file mode 100644 index 0000000000..3fc42640f8 --- /dev/null +++ b/packages/nodes-base/nodes/MonicaCrm/descriptions/ConversationMessageDescription.ts @@ -0,0 +1,217 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const conversationMessageOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'conversationMessage', + ], + }, + }, + options: [ + { + name: 'Add', + value: 'add', + description: 'Add a message to a conversation', + }, + { + name: 'Update', + value: 'update', + description: 'Update a message in a conversation', + }, + ], + default: 'add', + }, +] as INodeProperties[]; + +export const conversationMessageFields = [ + // ---------------------------------------- + // conversationMessage: add + // ---------------------------------------- + { + displayName: 'Conversation ID', + name: 'conversationId', + description: 'ID of the contact whose conversation', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'conversationMessage', + ], + operation: [ + 'add', + ], + }, + }, + }, + { + displayName: 'Content', + name: 'content', + description: 'Content of the message', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'conversationMessage', + ], + operation: [ + 'add', + ], + }, + }, + }, + { + displayName: 'Written At', + name: 'writtenAt', + description: 'Date when the message was written', + type: 'dateTime', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'conversationMessage', + ], + operation: [ + 'add', + ], + }, + }, + }, + { + displayName: 'Written By', + name: 'writtenByMe', + description: 'Author of the message', + type: 'options', + required: true, + default: true, + options: [ + { + name: 'User', + value: true, + }, + { + name: 'Contact', + value: false, + }, + ], + displayOptions: { + show: { + resource: [ + 'conversationMessage', + ], + operation: [ + 'add', + ], + }, + }, + }, + + // ---------------------------------------- + // conversationMessage: update + // ---------------------------------------- + { + displayName: 'Message ID', + name: 'messageId', + description: 'ID of the message to update', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'conversationMessage', + ], + operation: [ + 'update', + ], + }, + }, + }, + { + displayName: 'Conversation ID', + name: 'conversationId', + description: 'ID of the conversation whose message to update', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'conversationMessage', + ], + operation: [ + 'update', + ], + }, + }, + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'conversationMessage', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Contact ID', + name: 'contact_id', + description: 'ID of the contact to associate the conversationMessage with', + type: 'string', + default: '', + }, + { + displayName: 'Content', + name: 'content', + description: 'Content of the message', + type: 'string', + default: '', + }, + { + displayName: 'Written At', + name: 'written_at', + description: 'Date when the message was written', + type: 'dateTime', + default: '', + }, + { + displayName: 'Written By', + name: 'written_by_me', + description: 'Author of the message', + type: 'options', + required: true, + default: true, + options: [ + { + name: 'User', + value: true, + }, + { + name: 'Contact', + value: false, + }, + ], + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/MonicaCrm/descriptions/JournalEntryDescription.ts b/packages/nodes-base/nodes/MonicaCrm/descriptions/JournalEntryDescription.ts new file mode 100644 index 0000000000..8610e7c592 --- /dev/null +++ b/packages/nodes-base/nodes/MonicaCrm/descriptions/JournalEntryDescription.ts @@ -0,0 +1,237 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const journalEntryOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'journalEntry', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a journal entry', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a journal entry', + }, + { + name: 'Get', + value: 'get', + description: 'Retrieve a journal entry', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Retrieve all journal entries', + }, + { + name: 'Update', + value: 'update', + description: 'Update a journal entry', + }, + ], + default: 'create', + }, +] as INodeProperties[]; + +export const journalEntryFields = [ + // ---------------------------------------- + // journalEntry: create + // ---------------------------------------- + { + displayName: 'Title', + name: 'title', + description: 'Title of the journal entry - max 250 characters', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'journalEntry', + ], + operation: [ + 'create', + ], + }, + }, + }, + { + displayName: 'Content', + name: 'post', + description: 'Content of the journal entry - max 100,000 characters', + type: 'string', + required: true, + default: '', + typeOptions: { + alwaysOpenEditWindow: true, + }, + displayOptions: { + show: { + resource: [ + 'journalEntry', + ], + operation: [ + 'create', + ], + }, + }, + }, + + // ---------------------------------------- + // journalEntry: delete + // ---------------------------------------- + { + displayName: 'Journal Entry ID', + name: 'journalId', + description: 'ID of the journal entry to delete', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'journalEntry', + ], + operation: [ + 'delete', + ], + }, + }, + }, + + // ---------------------------------------- + // journalEntry: get + // ---------------------------------------- + { + displayName: 'Journal Entry ID', + name: 'journalId', + description: 'ID of the journal entry to retrieve', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'journalEntry', + ], + operation: [ + 'get', + ], + }, + }, + }, + + // ---------------------------------------- + // journalEntry: getAll + // ---------------------------------------- + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + default: false, + description: 'Whether to return all results or only up to a given limit', + displayOptions: { + show: { + resource: [ + 'journalEntry', + ], + operation: [ + 'getAll', + ], + }, + }, + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + default: 50, + description: 'How many results to return', + typeOptions: { + minValue: 1, + }, + displayOptions: { + show: { + resource: [ + 'journalEntry', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + }, + + // ---------------------------------------- + // journalEntry: update + // ---------------------------------------- + { + displayName: 'Journal Entry ID', + name: 'journalId', + description: 'ID of the journal entry to update', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'journalEntry', + ], + operation: [ + 'update', + ], + }, + }, + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'journalEntry', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Content', + name: 'post', + description: 'Content of the journal entry - max 100,000 characters', + type: 'string', + default: '', + typeOptions: { + alwaysOpenEditWindow: true, + }, + }, + { + displayName: 'Title', + name: 'title', + description: 'Title of the journal entry - max 250 characters', + type: 'string', + default: '', + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/MonicaCrm/descriptions/NoteDescription.ts b/packages/nodes-base/nodes/MonicaCrm/descriptions/NoteDescription.ts new file mode 100644 index 0000000000..d4c51cb83a --- /dev/null +++ b/packages/nodes-base/nodes/MonicaCrm/descriptions/NoteDescription.ts @@ -0,0 +1,269 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const noteOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'note', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a note', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a note', + }, + { + name: 'Get', + value: 'get', + description: 'Retrieve a note', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Retrieve all notes', + }, + { + name: 'Update', + value: 'update', + description: 'Update a note', + }, + ], + default: 'create', + }, +] as INodeProperties[]; + +export const noteFields = [ + // ---------------------------------------- + // note: create + // ---------------------------------------- + { + displayName: 'Contact ID', + name: 'contactId', + description: 'ID of the contact to associate the note with', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'note', + ], + operation: [ + 'create', + ], + }, + }, + }, + { + displayName: 'Body', + name: 'body', + description: 'Body of the note - max 100,000 characters', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'note', + ], + operation: [ + 'create', + ], + }, + }, + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'note', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + { + displayName: 'Is Favorited', + name: 'isFavorited', + description: 'Whether the note has been favorited', + type: 'boolean', + default: false, + }, + ], + }, + + // ---------------------------------------- + // note: delete + // ---------------------------------------- + { + displayName: 'Note ID', + name: 'noteId', + description: 'ID of the note to delete', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'note', + ], + operation: [ + 'delete', + ], + }, + }, + }, + + // ---------------------------------------- + // note: get + // ---------------------------------------- + { + displayName: 'Note ID', + name: 'noteId', + description: 'ID of the note to retrieve', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'note', + ], + operation: [ + 'get', + ], + }, + }, + }, + + // ---------------------------------------- + // note: getAll + // ---------------------------------------- + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + default: false, + description: 'Whether to return all results or only up to a given limit', + displayOptions: { + show: { + resource: [ + 'note', + ], + operation: [ + 'getAll', + ], + }, + }, + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + default: 50, + description: 'How many results to return', + typeOptions: { + minValue: 1, + }, + displayOptions: { + show: { + resource: [ + 'note', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + }, + + // ---------------------------------------- + // note: update + // ---------------------------------------- + { + displayName: 'Note ID', + name: 'noteId', + description: 'ID of the note to update', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'note', + ], + operation: [ + 'update', + ], + }, + }, + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'note', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Body', + name: 'body', + description: 'Body of the note - max 100,000 characters', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + }, + { + displayName: 'Contact ID', + name: 'contact_id', + description: 'ID of the contact to associate the note with', + type: 'string', + default: '', + }, + { + displayName: 'Is Favorited', + name: 'is_favorited', + description: 'Whether the note has been favorited', + type: 'boolean', + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/MonicaCrm/descriptions/ReminderDescription.ts b/packages/nodes-base/nodes/MonicaCrm/descriptions/ReminderDescription.ts new file mode 100644 index 0000000000..fe51030d0a --- /dev/null +++ b/packages/nodes-base/nodes/MonicaCrm/descriptions/ReminderDescription.ts @@ -0,0 +1,394 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const reminderOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'reminder', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a reminder', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a reminder', + }, + { + name: 'Get', + value: 'get', + description: 'Retrieve a reminder', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Retrieve all reminders', + }, + { + name: 'Update', + value: 'update', + description: 'Update a reminder', + }, + ], + default: 'create', + }, +] as INodeProperties[]; + +export const reminderFields = [ + // ---------------------------------------- + // reminder: create + // ---------------------------------------- + { + displayName: 'Contact ID', + name: 'contactId', + type: 'string', + default: '', + description: 'ID of the contact to associate the reminder with', + displayOptions: { + show: { + resource: [ + 'reminder', + ], + operation: [ + 'create', + ], + }, + }, + }, + { + displayName: 'Frequency Type', + name: 'frequencyType', + description: 'Type of frequency of the reminder', + type: 'options', + required: true, + default: 'one_time', + options: [ + { + name: 'Once', + value: 'one_time', + }, + { + name: 'Weekly', + value: 'week', + }, + { + name: 'Monthly', + value: 'month', + }, + { + name: 'Yearly', + value: 'year', + }, + ], + displayOptions: { + show: { + resource: [ + 'reminder', + ], + operation: [ + 'create', + ], + }, + }, + }, + { + displayName: 'Recurring Interval', + name: 'frequencyNumber', + type: 'number', + default: 0, + description: 'Interval for the reminder', + displayOptions: { + show: { + resource: [ + 'reminder', + ], + operation: [ + 'create', + ], + frequencyType: [ + 'week', + 'month', + 'year', + ], + }, + }, + }, + { + displayName: 'Initial Date', + name: 'initialDate', + description: 'Date of the reminder', + type: 'dateTime', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'reminder', + ], + operation: [ + 'create', + ], + }, + }, + }, + { + displayName: 'Title', + name: 'title', + description: 'Title of the reminder - max 100,000 characters', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'reminder', + ], + operation: [ + 'create', + ], + }, + }, + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'reminder', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + { + displayName: 'Description', + name: 'description', + type: 'string', + default: '', + description: 'Description about the reminder - Max 100,000 characters', + typeOptions: { + alwaysOpenEditWindow: true, + }, + }, + ], + }, + + // ---------------------------------------- + // reminder: delete + // ---------------------------------------- + { + displayName: 'Reminder ID', + name: 'reminderId', + description: 'ID of the reminder to delete', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'reminder', + ], + operation: [ + 'delete', + ], + }, + }, + }, + + // ---------------------------------------- + // reminder: get + // ---------------------------------------- + { + displayName: 'Reminder ID', + name: 'reminderId', + description: 'ID of the reminder to retrieve', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'reminder', + ], + operation: [ + 'get', + ], + }, + }, + }, + + // ---------------------------------------- + // reminder: getAll + // ---------------------------------------- + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + default: false, + description: 'Whether to return all results or only up to a given limit', + displayOptions: { + show: { + resource: [ + 'reminder', + ], + operation: [ + 'getAll', + ], + }, + }, + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + default: 50, + description: 'How many results to return', + typeOptions: { + minValue: 1, + }, + displayOptions: { + show: { + resource: [ + 'reminder', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + }, + + // ---------------------------------------- + // reminder: update + // ---------------------------------------- + { + displayName: 'Reminder ID', + name: 'reminderId', + description: 'ID of the reminder to update', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'reminder', + ], + operation: [ + 'update', + ], + }, + }, + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'reminder', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Contact ID', + name: 'contact_id', + type: 'string', + default: '', + description: 'ID of the contact to associate the reminder with', + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + default: '', + description: 'Description about the reminder - Max 100,000 characters', + typeOptions: { + alwaysOpenEditWindow: true, + }, + }, + { + displayName: 'Frequency Type', + name: 'frequency_type', + description: 'Frequency of the reminder', + type: 'options', + default: 'one_time', + options: [ + { + name: 'One Time', + value: 'one_time', + }, + { + name: 'Week', + value: 'week', + }, + { + name: 'Month', + value: 'month', + }, + { + name: 'Year', + value: 'year', + }, + ], + }, + { + displayName: 'Initial Date', + name: 'initial_data', + description: 'Date of the reminder', + type: 'dateTime', + default: '', + }, + { + displayName: 'Recurring Interval', + name: 'frequency_number', + type: 'number', + default: 0, + description: 'Interval for the reminder', + displayOptions: { + show: { + frequency_type: [ + 'week', + 'month', + 'year', + ], + }, + }, + }, + + { + displayName: 'Title', + name: 'title', + description: 'Title of the reminder - max 100,000 characters', + type: 'string', + default: '', + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/MonicaCrm/descriptions/TagDescription.ts b/packages/nodes-base/nodes/MonicaCrm/descriptions/TagDescription.ts new file mode 100644 index 0000000000..ed293abecf --- /dev/null +++ b/packages/nodes-base/nodes/MonicaCrm/descriptions/TagDescription.ts @@ -0,0 +1,198 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const tagOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'tag', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a tag', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a tag', + }, + { + name: 'Get', + value: 'get', + description: 'Retrieve a tag', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Retrieve all tags', + }, + { + name: 'Update', + value: 'update', + description: 'Update a tag', + }, + ], + default: 'create', + }, +] as INodeProperties[]; + +export const tagFields = [ + // ---------------------------------------- + // tag: create + // ---------------------------------------- + { + displayName: 'Name', + name: 'name', + description: 'Name of the tag - max 250 characters', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'tag', + ], + operation: [ + 'create', + ], + }, + }, + }, + + // ---------------------------------------- + // tag: delete + // ---------------------------------------- + { + displayName: 'Tag ID', + name: 'tagId', + description: 'ID of the tag to delete', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'tag', + ], + operation: [ + 'delete', + ], + }, + }, + }, + + // ---------------------------------------- + // tag: get + // ---------------------------------------- + { + displayName: 'Tag ID', + name: 'tagId', + description: 'ID of the tag to retrieve', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'tag', + ], + operation: [ + 'get', + ], + }, + }, + }, + + // ---------------------------------------- + // tag: getAll + // ---------------------------------------- + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + default: false, + description: 'Whether to return all results or only up to a given limit', + displayOptions: { + show: { + resource: [ + 'tag', + ], + operation: [ + 'getAll', + ], + }, + }, + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + default: 50, + description: 'How many results to return', + typeOptions: { + minValue: 1, + }, + displayOptions: { + show: { + resource: [ + 'tag', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + }, + + // ---------------------------------------- + // tag: update + // ---------------------------------------- + { + displayName: 'Tag ID', + name: 'tagId', + description: 'ID of the tag to update', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'tag', + ], + operation: [ + 'update', + ], + }, + }, + }, + { + displayName: 'Name', + name: 'name', + description: 'Name of the tag - max 250 characters', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'tag', + ], + operation: [ + 'update', + ], + }, + }, + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/MonicaCrm/descriptions/TaskDescription.ts b/packages/nodes-base/nodes/MonicaCrm/descriptions/TaskDescription.ts new file mode 100644 index 0000000000..dd3a603682 --- /dev/null +++ b/packages/nodes-base/nodes/MonicaCrm/descriptions/TaskDescription.ts @@ -0,0 +1,277 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const taskOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'task', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a task', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a task', + }, + { + name: 'Get', + value: 'get', + description: 'Retrieve a task', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Retrieve all tasks', + }, + { + name: 'Update', + value: 'update', + description: 'Update a task', + }, + ], + default: 'create', + }, +] as INodeProperties[]; + +export const taskFields = [ + // ---------------------------------------- + // task: create + // ---------------------------------------- + { + displayName: 'Contact ID', + name: 'contactId', + description: 'ID of the contact to associate the task with', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'create', + ], + }, + }, + }, + { + displayName: 'Title', + name: 'title', + description: 'Title of the task entry - max 250 characters', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'create', + ], + }, + }, + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + { + displayName: 'Description', + name: 'description', + type: 'string', + default: '', + description: 'Description of the task - max 100,000 characters', + typeOptions: { + alwaysOpenEditWindow: true, + }, + }, + ], + }, + + // ---------------------------------------- + // task: delete + // ---------------------------------------- + { + displayName: 'Task ID', + name: 'taskId', + description: 'ID of the task to delete', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'delete', + ], + }, + }, + }, + + // ---------------------------------------- + // task: get + // ---------------------------------------- + { + displayName: 'Task ID', + name: 'taskId', + description: 'ID of the task to retrieve', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'get', + ], + }, + }, + }, + + // ---------------------------------------- + // task: getAll + // ---------------------------------------- + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + default: false, + description: 'Whether to return all results or only up to a given limit', + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'getAll', + ], + }, + }, + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + default: 50, + description: 'How many results to return', + typeOptions: { + minValue: 1, + }, + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + }, + + // ---------------------------------------- + // task: update + // ---------------------------------------- + { + displayName: 'Task ID', + name: 'taskId', + description: 'ID of the task to update', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'update', + ], + }, + }, + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Contact ID', + name: 'contactId', + description: 'ID of the contact to associate the task with', + type: 'string', + default: '', + }, + { + displayName: 'Completed', + name: 'completed', + description: 'Whether the task has been completed', + type: 'boolean', + default: false, + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + default: '', + description: 'Description of the task - max 100,000 characters', + typeOptions: { + alwaysOpenEditWindow: true, + }, + }, + { + displayName: 'Title', + name: 'title', + description: 'Title of the task entry - max 250 characters', + type: 'string', + default: '', + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/MonicaCrm/descriptions/index.ts b/packages/nodes-base/nodes/MonicaCrm/descriptions/index.ts new file mode 100644 index 0000000000..973a631c28 --- /dev/null +++ b/packages/nodes-base/nodes/MonicaCrm/descriptions/index.ts @@ -0,0 +1,12 @@ +export * from './ActivityDescription'; +export * from './CallDescription'; +export * from './ContactDescription'; +export * from './ContactFieldDescription'; +export * from './ContactTagDescription'; +export * from './ConversationDescription'; +export * from './ConversationMessageDescription'; +export * from './JournalEntryDescription'; +export * from './NoteDescription'; +export * from './ReminderDescription'; +export * from './TagDescription'; +export * from './TaskDescription'; diff --git a/packages/nodes-base/nodes/MonicaCrm/monicaCrm.png b/packages/nodes-base/nodes/MonicaCrm/monicaCrm.png new file mode 100644 index 0000000000000000000000000000000000000000..1600e626fed48c8f0c73f0eaeea3c97b56a34f8b GIT binary patch literal 4007 zcmZ{nWmFW}*T!e48A>{&L8Os^p%tXN1*9(BB`FO<4e0h^vckQ+Jv(9g=XRrO`oOO1Rfu1@!2@?qb03g@YP%*mKXa6Ob=)TJ0=Kb!4 zz(rA45ddgNBgNSf-k&)fG>misfKVO)02T)T+}tDBF90AA0s!pV-n()E00y7@cEkS~ z5&LME`vU+E2L4N6w%`Nidq`xjt*!#N`!9;Ssxt2hC_qzJ6|@D$2R)*E@V!a?9z70F zH4AXG3viNk@N>EsfEYwfR2U*H41pR$q-3EIvJj{s1R@K8fZs5o|Bu1zm7|+;$p3E$ z!IXyF&y%C6qG;@@d|&|zFi~Z?CFYLHDKwzwDA8!wtd5BZ*s11N)rx^}7sF_ydkY`U zQU0TWr(sS{IE+Uk5Z9`%c+qM^%`sgZprIto^;m&qb?ma|smSSJnC6PMLeR`ne%MNv z|Ir98;x=k~=1LWIt581`c?Y@5T*E%Sb-cPnJZI@Q71555j}JKCH4zXNE_pBEVR*36 z^uc?%J$&Yq`HMfhU+_<{ogpdS*lyj&kI?}~DjNfM z8NXa33bt~n%a}oAWVb;t`4^uLj6d0AWPfnl*x2l@bUi&kTuxY8T5c-b=6ky~Icao* zI~6{=U1$o}F{;c^(Bv-Ma&mD|9~&E+{xJ1O+#456LPE0rsyH4`(aO}6-pR>Dw4@%n z*y2t6WqB--L#QWVEzMU0;b;z{#17x%+jJ5(TzeQj22ZZUi>^aGHjw_x^mh+S1kVZRX_-j4V~?NnK}Yxx9KuWx(}{QUi^+Rw?f=a-)beK>aJB$b9jGhfD~>@e&kV@PsaW?0X9 z896yiP&Y>&-_YpzMIA1APDHI6WAyw^f)`&5==E*{01o(jkp;;8=bXjY?~)JQ!i!|cZHC% ziN#rl@lIJROLxxF0oPL<0cO&awJ{!gx(-h*IhQi$2PghT4Y=&rpjg9 z8s#}3CLS@U<395-uql_YUOwn>>D{9P+%;(&z!LJi8SiY1Vbepuz1RuUbe_%w<&=ff zcXuVmC@Z0)E-7roF{_-k3`GiQazCh#}q#?@#tK2)0~BPVYfq7~&NI9u1wbla>poRPHg0LG6L{CT-M zQC4NLO)kqU?y8^|t!R-lR^IV?kB7^1Lkko;t(MB7TBasvS@Fx9wY9BnLa@=u z5S!GdeW{H-&n{Wccb6AS&o((|O3TbAdmgt3gk-@VL`-t^yEeA8R5dt!P13-sdPwx9 z+D*4MG@MpP-2t87!zpjhb_A(U5EjF)K(Ur@%AH5wzvBaq*l#5!y;I33b)P!5op^1S zCl9rc1M48vh}UQTA$Vig5BG;P6435mUx(zgnlShj6h+;>^Kf;v&B1iGiJa3U8vYSa0Di|x2(?&l=e}Z-QeI}xK=-!MwKUjR*?71TJv2^%TAcmB)#0OTl zXXxJzd;N;wqRzC|LQ!UZUJc$rrkgLFWYO%79;fEj%P*djY23+s)m@2~F`+QbHwsxR zT<(xbyMGy*E&|cLh`|#lZMW}8bXHD|+RJ~fN<3cH8U3ixMg<+Q4Gj-()pBz`q6S-i zO=EXh)37iMk?`>=JzNgUt>vQEz3GcTwQ8$g@cQvdCLx%v+K;}g%K%a^=!`hBGnyoS zLafB_Bs|3knR(sDRXl!kz8CzKkcUi3RAhmCW2MZ?vL)H#FFTKX%Ib8RHUo{6GM1e{ z*N0e6NG$&~PWn}i#*Xb|{V~M)$|U@Hi=opF_WhU~m81jS^Z#>kFrRR8dg40ze#TRR z4#@QPCog;Bku34S+2K^eS0EIu^qwsKI#NwNjZNa2E12$aabqLt6aaRZpU*3NMi%0BK4SDV{7F(4GrW zW*L;N%khwL|>$g8Zs77TDj2wBt_nb&g#kPs0N)g*-v7(8un;AvdZnzT*@2{ z_?8Q}*jNgZUXbbKNU&GK#9(YI?}|r8x5?nsJ6%>iXR8b7Mt8T6Fi@e4;n7MZty&M^ zC=)2Gd^Jr#A>44nD>8vpKKOVQcu)(p7580~6fOCZx|U+5Bm{J(h_)BX_uHrRXeSu~ z^<)P``*N!a?6dB-^$;BT(&9T+LmXN)gl>(H)J;N^A0wj`8zK)E&V_l2r9q*mqf%G% zV`KG>oQxk#Qb_hahlW57wH8gtSgeI+MR_e;od#pbaB}kA z#BUDJhu?}gNFkX9>a2rj>;94QeVDq%5mV6B<#ypY2q7}k#b<43tHRsf+2Kc0 zs+Ru3hpJidx0nKRIrSLXBhEx4n+ZO3MW~c=>ascf?u_x>MBA!V5Iab@FPVjjaR}}q z%~O5ovx{!Ur&8^Lf`S@dK?fhG=x^z1S?rz3uS~6}Th*!7e*Ua#G8^3BI`W$Rw}5!! zrU3xP8!qnY>G@#~`8idk@1ak$+A*kvF`G2b6cvtkiN%N(AiavcJg%c0h3#E5dC69&%2N2o{3g$CJ3wi~zZd@#Uqec_< zgY{`?6d}oeI%27c=_hLt3)MVHuV9+-nHgPDMkke05wM3KPV)8Pvu37zDiO2ekH^EJ z;?6qUZ<46rUZGE8uIoN)a^=03dzv$>c=lEtK#8{y6Ytc;y`4==o{mKJeNPN{bQViT z0Ae2dV3M1i!1yayV@$tD-i-x7!|8LrKQmljtyarS&7@Qjq!o{)XNt+AOi!@&`GzSG z_xQo0cMU6e8sxK+(0)F6&TJ*+(1nI->6HH)UHSuViWeIr*@M&|P)MFRwl^kxot`rUr6dt$&(K^`Fg^m^Jv(6|HD<019OTlc%g4eT6NXH{Eq8 zaN29YR+U_=310*e&sI-_CA4 zq#H{8O-P|Lcr?4kGaEF0@S;j*+g^R<$uG;U&^Ht9#<|xJKvCGujvdcCDxzm0C)|1Z zCKsPDQTwgG&o(pleX|nOFhnWQJf2SU^Z(?V)tEQ~!PGS|kDY-jOhl_TP#2xD<&K~{ z%2;3mMHJk_`0p)J0`?*D&w<{PtG}wBqm{5fd^wJLy8b-KYb0s44Gxw@lM137Zx!0e z@cDo);L39<%FF7&HC{fUNL>!9B+JjaQrE_lc0MUVP$*Jve`K^3~Kv7IWg_x`?e9+t%_kzD4ApZr< zS}LTY^_=s`X0u$_+2)z)Q3E@|K{l4v;QPF-EUd?F)*N3r>t z8M#EcG{YJB6q4`+R?OoT5$gbuvt@mk&qt1G+o!G9Ng!XiyC{u{Xo} zqnbd$POq)Pb+Fj+*29jHhq&%RIi=4GRVpD^mrMF$clqnSLNC2>iXE{lKNK2ULk~ej zb#t|r!~;@$>k2sgM^G*l%uY!*j_WL`vH`IzUBNzFV}k-%O|$4tWos_J`UA=aa1e!ePkX0w(cIN+z~Df|B75}NvgYFX#tw5 LdMf`a*~a`2IVXBa literal 0 HcmV?d00001 diff --git a/packages/nodes-base/nodes/MonicaCrm/types.d.ts b/packages/nodes-base/nodes/MonicaCrm/types.d.ts new file mode 100644 index 0000000000..96361ec2f0 --- /dev/null +++ b/packages/nodes-base/nodes/MonicaCrm/types.d.ts @@ -0,0 +1,13 @@ +import { IDataObject } from "n8n-workflow"; + +export type LoaderGetResponse = { + data: Array<{ + id: string; + name: string; + }> +} & IDataObject; + +export type Option = { + value: string; + name: string; +}; diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index a1dd7ea7b6..006a851d8f 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -172,6 +172,7 @@ "dist/credentials/MicrosoftToDoOAuth2Api.credentials.js", "dist/credentials/MindeeReceiptApi.credentials.js", "dist/credentials/MindeeInvoiceApi.credentials.js", + "dist/credentials/MonicaCrmApi.credentials.js", "dist/credentials/MoceanApi.credentials.js", "dist/credentials/MondayComApi.credentials.js", "dist/credentials/MondayComOAuth2Api.credentials.js", @@ -469,6 +470,7 @@ "dist/nodes/Microsoft/Teams/MicrosoftTeams.node.js", "dist/nodes/Microsoft/ToDo/MicrosoftToDo.node.js", "dist/nodes/Mindee/Mindee.node.js", + "dist/nodes/MonicaCrm/MonicaCrm.node.js", "dist/nodes/MoveBinaryData.node.js", "dist/nodes/Mocean/Mocean.node.js", "dist/nodes/MondayCom/MondayCom.node.js", From 2e309bef7fed9d8f2f633a243d0ab0111550e8e0 Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Sun, 8 Aug 2021 03:58:04 -0400 Subject: [PATCH 005/111] :bug: Fix Spotify pagination bug (#2061) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fixed missing value Fixed infinite amount of interval triggered nodes executions * Fixed inifite loop on Spotify Node * Hide Limit parameter * Logic operator fix * Spotify getFollowingArtists returnAll fix * :zap: Small improvements to #2054 * :shirt: Fix lint issue Co-authored-by: Лебедев Иван <11215636+X-pech@users.noreply.github.com> Co-authored-by: X-pech --- .../nodes/Spotify/GenericFunctions.ts | 2 +- .../nodes-base/nodes/Spotify/Spotify.node.ts | 31 +++++++++++++++---- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/packages/nodes-base/nodes/Spotify/GenericFunctions.ts b/packages/nodes-base/nodes/Spotify/GenericFunctions.ts index e07a6d71df..e4f437275a 100644 --- a/packages/nodes-base/nodes/Spotify/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Spotify/GenericFunctions.ts @@ -71,7 +71,7 @@ export async function spotifyApiRequestAllItems(this: IHookFunctions | IExecuteF } } while ( (responseData['next'] !== null && responseData['next'] !== undefined) || - responseData[propertyName.split('.')[0]].next !== null + (responseData[propertyName.split('.')[0]].next !== null && responseData[propertyName.split('.')[0]].next !== undefined) ); return returnData; diff --git a/packages/nodes-base/nodes/Spotify/Spotify.node.ts b/packages/nodes-base/nodes/Spotify/Spotify.node.ts index e896ca83ea..daa913284c 100644 --- a/packages/nodes-base/nodes/Spotify/Spotify.node.ts +++ b/packages/nodes-base/nodes/Spotify/Spotify.node.ts @@ -715,6 +715,7 @@ export class Spotify implements INodeType { 'myData', 'playlist', 'track', + 'player', ], operation: [ 'getTracks', @@ -724,6 +725,7 @@ export class Spotify implements INodeType { 'getLikedTracks', 'getFollowingArtists', 'search', + 'recentlyPlayed', ], }, }, @@ -779,6 +781,9 @@ export class Spotify implements INodeType { 'getFollowingArtists', 'recentlyPlayed', ], + returnAll: [ + false, + ], }, }, typeOptions: { @@ -908,6 +913,7 @@ export class Spotify implements INodeType { endpoint = `/me/player/pause`; + responseData = await spotifyApiRequest.call(this, requestMethod, endpoint, body, qs); responseData = { success: true }; @@ -917,15 +923,22 @@ export class Spotify implements INodeType { endpoint = `/me/player/recently-played`; - const limit = this.getNodeParameter('limit', i) as number; + returnAll = this.getNodeParameter('returnAll', i) as boolean; - qs = { - limit, - }; + propertyName = 'items'; - responseData = await spotifyApiRequest.call(this, requestMethod, endpoint, body, qs); + if (!returnAll) { - responseData = responseData.items; + const limit = this.getNodeParameter('limit', i) as number; + + qs = { + limit, + }; + + responseData = await spotifyApiRequest.call(this, requestMethod, endpoint, body, qs); + + responseData = responseData.items; + } } else if (operation === 'currentlyPlaying') { requestMethod = 'GET'; @@ -1384,8 +1397,14 @@ export class Spotify implements INodeType { endpoint = `/me/following`; + returnAll = this.getNodeParameter('returnAll', i) as boolean; + propertyName = 'artists.items'; + qs = { + type: 'artist', + }; + if (!returnAll) { const limit = this.getNodeParameter('limit', i) as number; qs = { From 5bdcadf93de815ca2155aac54f0900ef0d7c75d2 Mon Sep 17 00:00:00 2001 From: Mutasem Aldmour <4711238+mutdmour@users.noreply.github.com> Date: Sun, 8 Aug 2021 10:43:34 +0200 Subject: [PATCH 006/111] :bug: Fix ipad drag issue (#2016) --- packages/editor-ui/src/components/Node.vue | 2 +- packages/editor-ui/src/components/mixins/nodeBase.ts | 7 ------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/packages/editor-ui/src/components/Node.vue b/packages/editor-ui/src/components/Node.vue index 22bb4ed800..00e21b1085 100644 --- a/packages/editor-ui/src/components/Node.vue +++ b/packages/editor-ui/src/components/Node.vue @@ -1,6 +1,6 @@