diff --git a/packages/cli/package.json b/packages/cli/package.json index 833123d8a7..d7059d1b2d 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "n8n", - "version": "0.132.0", + "version": "0.133.0", "description": "n8n Workflow Automation Tool", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", @@ -109,8 +109,8 @@ "lodash.get": "^4.4.2", "mysql2": "~2.2.0", "n8n-core": "~0.78.0", - "n8n-editor-ui": "~0.100.0", - "n8n-nodes-base": "~0.129.0", + "n8n-editor-ui": "~0.101.0", + "n8n-nodes-base": "~0.130.0", "n8n-workflow": "~0.64.0", "oauth-1.0a": "^2.2.6", "open": "^7.0.0", diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index 0abd3f60f1..d4341ed711 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -466,16 +466,18 @@ class App { // Does very basic health check this.app.get('/healthz', async (req: express.Request, res: express.Response) => { - const connectionManager = getConnectionManager(); + const connection = getConnectionManager().get(); - if (connectionManager.connections.length === 0) { - const error = new ResponseHelper.ResponseError('No Database connection found!', undefined, 503); - return ResponseHelper.sendErrorResponse(res, error); - } - - if (connectionManager.connections[0].isConnected === false) { - // Connection is not active - const error = new ResponseHelper.ResponseError('Database connection not active!', undefined, 503); + try { + if (connection.isConnected === false) { + // Connection is not active + throw new Error('No active database connection!'); + } + // DB ping + await connection.query('SELECT 1'); + } catch (err) { + LoggerProxy.error('No Database connection!', err); + const error = new ResponseHelper.ResponseError('No Database connection!', undefined, 503); return ResponseHelper.sendErrorResponse(res, error); } diff --git a/packages/cli/src/WebhookServer.ts b/packages/cli/src/WebhookServer.ts index becfb71af7..245e3a2504 100644 --- a/packages/cli/src/WebhookServer.ts +++ b/packages/cli/src/WebhookServer.ts @@ -111,7 +111,7 @@ export function registerProductionWebhooks() { } class App { - + app: express.Application; activeWorkflowRunner: ActiveWorkflowRunner.ActiveWorkflowRunner; endpointWebhook: string; @@ -129,12 +129,12 @@ class App { protocol: string; sslKey: string; sslCert: string; - + presetCredentialsLoaded: boolean; - + constructor() { this.app = express(); - + this.endpointWebhook = config.get('endpoints.webhook') as string; this.saveDataErrorExecution = config.get('executions.saveDataOnError') as string; this.saveDataSuccessExecution = config.get('executions.saveDataOnSuccess') as string; @@ -143,22 +143,22 @@ class App { this.maxExecutionTimeout = config.get('executions.maxTimeout') as number; this.timezone = config.get('generic.timezone') as string; this.restEndpoint = config.get('endpoints.rest') as string; - + this.activeWorkflowRunner = ActiveWorkflowRunner.getInstance(); - + this.activeExecutionsInstance = ActiveExecutions.getInstance(); - + this.protocol = config.get('protocol'); this.sslKey = config.get('ssl_key'); this.sslCert = config.get('ssl_cert'); - + this.externalHooks = ExternalHooks(); - + this.presetCredentialsLoaded = false; this.endpointPresetCredentials = config.get('credentials.overwrite.endpoint') as string; } - - + + /** * Returns the current epoch time * @@ -168,15 +168,15 @@ class App { getCurrentDate(): Date { return new Date(); } - - + + async config(): Promise { - + this.versions = await GenericHelpers.getVersions(); - + // Compress the response data this.app.use(compression()); - + // Make sure that each request has the "parsedUrl" parameter this.app.use((req: express.Request, res: express.Response, next: express.NextFunction) => { (req as ICustomRequest).parsedUrl = parseUrl(req); @@ -184,7 +184,7 @@ class App { req.rawBody = Buffer.from('', 'base64'); next(); }); - + // Support application/json type post data this.app.use(bodyParser.json({ limit: '16mb', verify: (req, res, buf) => { @@ -192,7 +192,7 @@ class App { req.rawBody = buf; }, })); - + // Support application/xml type post data // @ts-ignore this.app.use(bodyParser.xml({ @@ -202,14 +202,14 @@ class App { explicitArray: false, // Only put properties in array if length > 1 }, })); - + this.app.use(bodyParser.text({ limit: '16mb', verify: (req, res, buf) => { // @ts-ignore req.rawBody = buf; }, })); - + //support application/x-www-form-urlencoded post data this.app.use(bodyParser.urlencoded({ extended: false, verify: (req, res, buf) => { @@ -217,7 +217,7 @@ class App { req.rawBody = buf; }, })); - + if (process.env['NODE_ENV'] !== 'production') { this.app.use((req: express.Request, res: express.Response, next: express.NextFunction) => { // Allow access also from frontend when developing @@ -227,64 +227,65 @@ class App { next(); }); } - - + + this.app.use((req: express.Request, res: express.Response, next: express.NextFunction) => { if (Db.collections.Workflow === null) { const error = new ResponseHelper.ResponseError('Database is not ready!', undefined, 503); return ResponseHelper.sendErrorResponse(res, error); } - + next(); }); - - - + + + // ---------------------------------------- // Healthcheck // ---------------------------------------- - - + + // Does very basic health check this.app.get('/healthz', async (req: express.Request, res: express.Response) => { - - const connectionManager = getConnectionManager(); - - if (connectionManager.connections.length === 0) { - const error = new ResponseHelper.ResponseError('No Database connection found!', undefined, 503); + + const connection = getConnectionManager().get(); + + try { + if (connection.isConnected === false) { + // Connection is not active + throw new Error('No active database connection!'); + } + // DB ping + await connection.query('SELECT 1'); + } catch (err) { + const error = new ResponseHelper.ResponseError('No Database connection!', undefined, 503); return ResponseHelper.sendErrorResponse(res, error); } - - if (connectionManager.connections[0].isConnected === false) { - // Connection is not active - const error = new ResponseHelper.ResponseError('Database connection not active!', undefined, 503); - return ResponseHelper.sendErrorResponse(res, error); - } - + // Everything fine const responseData = { status: 'ok', }; - + ResponseHelper.sendSuccessResponse(res, responseData, true, 200); }); - + registerProductionWebhooks.apply(this); - + } - + } export async function start(): Promise { const PORT = config.get('port'); const ADDRESS = config.get('listen_address'); - + const app = new App(); - + await app.config(); - + let server; - + if (app.protocol === 'https' && app.sslKey && app.sslCert) { const https = require('https'); const privateKey = readFileSync(app.sslKey, 'utf8'); @@ -295,12 +296,12 @@ export async function start(): Promise { const http = require('http'); server = http.createServer(app.app); } - + server.listen(PORT, ADDRESS, async () => { const versions = await GenericHelpers.getVersions(); console.log(`n8n ready on ${ADDRESS}, port ${PORT}`); console.log(`Version: ${versions.cli}`); - + await app.externalHooks.run('n8n.ready', [app]); }); } diff --git a/packages/editor-ui/package.json b/packages/editor-ui/package.json index 12a4174b8d..4aae991580 100644 --- a/packages/editor-ui/package.json +++ b/packages/editor-ui/package.json @@ -1,6 +1,6 @@ { "name": "n8n-editor-ui", - "version": "0.100.0", + "version": "0.101.0", "description": "Workflow Editor UI for n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", diff --git a/packages/editor-ui/src/components/Node.vue b/packages/editor-ui/src/components/Node.vue index b3c9c96744..318ea69e8e 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 2777d31d24..e6462724dc 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" > 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/Cisco/Webex/CiscoWebex.node.ts b/packages/nodes-base/nodes/Cisco/Webex/CiscoWebex.node.ts index c75a261353..430f69cfd6 100644 --- a/packages/nodes-base/nodes/Cisco/Webex/CiscoWebex.node.ts +++ b/packages/nodes-base/nodes/Cisco/Webex/CiscoWebex.node.ts @@ -33,15 +33,15 @@ 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.svg', + icon: 'file:ciscoWebex.png', group: ['transform'], version: 1, 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 bff18e37ee..7a33970f1c 100644 --- a/packages/nodes-base/nodes/Cisco/Webex/CiscoWebexTrigger.node.ts +++ b/packages/nodes-base/nodes/Cisco/Webex/CiscoWebexTrigger.node.ts @@ -24,15 +24,15 @@ import { export class CiscoWebexTrigger implements INodeType { description: INodeTypeDescription = { - displayName: 'Cisco Webex Trigger', + displayName: 'Webex by Cisco Trigger', name: 'ciscoWebexTrigger', - icon: 'file:ciscoWebex.svg', + icon: 'file:ciscoWebex.png', group: ['trigger'], version: 1, 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) { diff --git a/packages/nodes-base/nodes/Cisco/Webex/ciscoWebex.png b/packages/nodes-base/nodes/Cisco/Webex/ciscoWebex.png new file mode 100644 index 0000000000..f36061c12e Binary files /dev/null and b/packages/nodes-base/nodes/Cisco/Webex/ciscoWebex.png differ diff --git a/packages/nodes-base/nodes/Cisco/Webex/ciscoWebex.svg b/packages/nodes-base/nodes/Cisco/Webex/ciscoWebex.svg deleted file mode 100644 index f8253bbf23..0000000000 --- a/packages/nodes-base/nodes/Cisco/Webex/ciscoWebex.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/nodes-base/nodes/HttpRequest.node.ts b/packages/nodes-base/nodes/HttpRequest.node.ts index 7e4292e6cf..a7aa3db464 100644 --- a/packages/nodes-base/nodes/HttpRequest.node.ts +++ b/packages/nodes-base/nodes/HttpRequest.node.ts @@ -308,11 +308,18 @@ export class HttpRequest implements INodeType { description: 'Returns the full reponse data instead of only the body.', }, { - displayName: 'Follow Redirect', + displayName: 'Follow All Redirects', + name: 'followAllRedirects', + type: 'boolean', + default: false, + description: 'Follow non-GET HTTP 3xx redirects.', + }, + { + displayName: 'Follow GET Redirect', name: 'followRedirect', type: 'boolean', default: true, - description: 'Follow HTTP 3xx redirects.', + description: 'Follow GET HTTP 3xx redirects.', }, { displayName: 'Ignore Response Code', @@ -695,6 +702,11 @@ export class HttpRequest implements INodeType { if (options.followRedirect !== undefined) { requestOptions.followRedirect = options.followRedirect as boolean; } + + if (options.followAllRedirects !== undefined) { + requestOptions.followAllRedirects = options.followAllRedirects as boolean; + } + if (options.ignoreResponseCode === true) { // @ts-ignore requestOptions.simple = false; diff --git a/packages/nodes-base/nodes/Interval.node.ts b/packages/nodes-base/nodes/Interval.node.ts index 55592236e9..c5ee0631be 100644 --- a/packages/nodes-base/nodes/Interval.node.ts +++ b/packages/nodes-base/nodes/Interval.node.ts @@ -84,7 +84,7 @@ export class Interval implements INodeType { throw new Error('The interval value is too large.'); } - const intervalObj = setInterval(executeTrigger, ); + const intervalObj = setInterval(executeTrigger, intervalValue); async function closeFunction() { clearInterval(intervalObj); 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 0000000000..1600e626fe Binary files /dev/null and b/packages/nodes-base/nodes/MonicaCrm/monicaCrm.png differ 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/nodes/NextCloud/NextCloud.node.ts b/packages/nodes-base/nodes/NextCloud/NextCloud.node.ts index 011ca2e27d..feb38b0d65 100644 --- a/packages/nodes-base/nodes/NextCloud/NextCloud.node.ts +++ b/packages/nodes-base/nodes/NextCloud/NextCloud.node.ts @@ -1104,34 +1104,38 @@ export class NextCloud implements INodeType { let skippedFirst = false; // @ts-ignore - for (const item of jsonResponseData['d:multistatus']['d:response']) { - if (skippedFirst === false) { - skippedFirst = true; - continue; - } - const newItem: IDataObject = {}; - - newItem.path = item['d:href'].slice(19); - - const props = item['d:propstat'][0]['d:prop']; - - // Get the props and save them under a proper name - for (const propName of Object.keys(propNames)) { - if (props[propName] !== undefined) { - newItem[propNames[propName]] = props[propName]; + if (Array.isArray(jsonResponseData['d:multistatus']['d:response'])) { + // @ts-ignore + for (const item of jsonResponseData['d:multistatus']['d:response']) { + if (skippedFirst === false) { + skippedFirst = true; + continue; } - } + const newItem: IDataObject = {}; - if (props['d:resourcetype'] === '') { - newItem.type = 'file'; - } else { - newItem.type = 'folder'; - } - newItem.eTag = props['d:getetag'].slice(1, -1); + newItem.path = item['d:href'].slice(19); - returnData.push(newItem as IDataObject); + const props = item['d:propstat'][0]['d:prop']; + + // Get the props and save them under a proper name + for (const propName of Object.keys(propNames)) { + if (props[propName] !== undefined) { + newItem[propNames[propName]] = props[propName]; + } + } + + if (props['d:resourcetype'] === '') { + newItem.type = 'file'; + } else { + newItem.type = 'folder'; + } + newItem.eTag = props['d:getetag'].slice(1, -1); + + returnData.push(newItem as IDataObject); + } } } + } else { returnData.push(responseData as IDataObject); } 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; } 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 = { diff --git a/packages/nodes-base/nodes/WooCommerce/GenericFunctions.ts b/packages/nodes-base/nodes/WooCommerce/GenericFunctions.ts index 7be0c77221..4416961a07 100644 --- a/packages/nodes-base/nodes/WooCommerce/GenericFunctions.ts +++ b/packages/nodes-base/nodes/WooCommerce/GenericFunctions.ts @@ -149,6 +149,17 @@ export function toSnakeCase(data: } } +export function setFields(fieldsToSet: IDataObject, body: IDataObject) { + for(const fields in fieldsToSet) { + if (fields === 'tags') { + body['tags'] = (fieldsToSet[fields] as string[]).map(tag => ({id: parseInt(tag, 10)})); + } else { + body[snakeCase(fields.toString())] = fieldsToSet[fields]; + } + + } +} + export function adjustMetadata(fields: IDataObject & Metadata) { if (!fields.meta_data) return fields; diff --git a/packages/nodes-base/nodes/WooCommerce/OrderInterface.ts b/packages/nodes-base/nodes/WooCommerce/OrderInterface.ts index 74e5ce7376..5a59c4c02a 100644 --- a/packages/nodes-base/nodes/WooCommerce/OrderInterface.ts +++ b/packages/nodes-base/nodes/WooCommerce/OrderInterface.ts @@ -46,6 +46,7 @@ export interface ICouponLine { } export interface IOrder { + [index: string]: any; // tslint:disable-line:no-any billing?: IAddress; coupon_lines?: ICouponLine[]; currency?: string; diff --git a/packages/nodes-base/nodes/WooCommerce/ProductInterface.ts b/packages/nodes-base/nodes/WooCommerce/ProductInterface.ts index 2c4072146d..b4382677cb 100644 --- a/packages/nodes-base/nodes/WooCommerce/ProductInterface.ts +++ b/packages/nodes-base/nodes/WooCommerce/ProductInterface.ts @@ -13,42 +13,5 @@ export interface IImage { } export interface IProduct { - backorders?: string; - button_text?: string; - catalog_visibility?: string; - categories?: IDataObject[]; - cross_sell_ids?: string[]; - date_on_sale_from?: string; - date_on_sale_to?: string; - description?: string; - dimensions?: IDimension; - downloadable?: boolean; - external_url?: string; - featured?: boolean; - images?: IImage[]; - manage_stock?: boolean; - menu_order?: number; - meta_data?: IDataObject[]; - name?: string; - parent_id?: string; - price?: string; - purchase_note?: string; - regular_price?: string; - reviews_allowed?: boolean; - sale_price?: string; - shipping_class?: string; - short_description?: string; - sku?: string; - slug?: string; - sold_individually?: boolean; - status?: string; - stock_quantity?: number; - stock_status?: string; - tags?: IDataObject[]; - tax_class?: string; - tax_status?: string; - type?: string; - upsell_ids?: string[]; - virtual?: boolean; - weight?: string; + [index: string]: string | number | string[] | number[] | IDataObject | IDataObject[] | IImage[] | IDimension; } diff --git a/packages/nodes-base/nodes/WooCommerce/WooCommerce.node.ts b/packages/nodes-base/nodes/WooCommerce/WooCommerce.node.ts index 9b7361402b..c8264dda24 100644 --- a/packages/nodes-base/nodes/WooCommerce/WooCommerce.node.ts +++ b/packages/nodes-base/nodes/WooCommerce/WooCommerce.node.ts @@ -11,6 +11,7 @@ import { } from 'n8n-workflow'; import { adjustMetadata, + setFields, setMetadata, toSnakeCase, woocommerceApiRequest, @@ -252,102 +253,13 @@ export class WooCommerce implements INodeType { const body: IProduct = { name, }; - if (additionalFields.backorders) { - body.backorders = additionalFields.backorders as string; - } - if (additionalFields.buttonText) { - body.button_text = additionalFields.buttonText as string; - } - if (additionalFields.catalogVisibility) { - body.catalog_visibility = additionalFields.catalogVisibility as string; - } + + setFields(additionalFields, body); + if (additionalFields.categories) { body.categories = (additionalFields.categories as string[]).map(category => ({ id: parseInt(category, 10) })) as unknown as IDataObject[]; } - if (additionalFields.crossSellIds) { - body.cross_sell_ids = (additionalFields.crossSellIds as string).split(',') as string[]; - } - if (additionalFields.dateOnSaleFrom) { - body.date_on_sale_from = additionalFields.dateOnSaleFrom as string; - } - if (additionalFields.dateOnSaleTo) { - body.date_on_sale_to = additionalFields.dateOnSaleTo as string; - } - if (additionalFields.description) { - body.description = additionalFields.description as string; - } - if (additionalFields.downloadable) { - body.downloadable = additionalFields.downloadable as boolean; - } - if (additionalFields.externalUrl) { - body.external_url = additionalFields.externalUrl as string; - } - if (additionalFields.featured) { - body.featured = additionalFields.featured as boolean; - } - if (additionalFields.manageStock) { - body.manage_stock = additionalFields.manageStock as boolean; - } - if (additionalFields.parentId) { - body.parent_id = additionalFields.parentId as string; - } - if (additionalFields.purchaseNote) { - body.purchase_note = additionalFields.purchaseNote as string; - } - if (additionalFields.regularPrice) { - body.regular_price = additionalFields.regularPrice as string; - } - if (additionalFields.reviewsAllowed) { - body.reviews_allowed = additionalFields.reviewsAllowed as boolean; - } - if (additionalFields.salePrice) { - body.sale_price = additionalFields.salePrice as string; - } - if (additionalFields.shippingClass) { - body.shipping_class = additionalFields.shippingClass as string; - } - if (additionalFields.shortDescription) { - body.short_description = additionalFields.shortDescription as string; - } - if (additionalFields.sku) { - body.sku = additionalFields.sku as string; - } - if (additionalFields.slug) { - body.slug = additionalFields.slug as string; - } - if (additionalFields.soldIndividually) { - body.sold_individually = additionalFields.soldIndividually as boolean; - } - if (additionalFields.status) { - body.status = additionalFields.status as string; - } - if (additionalFields.stockQuantity) { - body.stock_quantity = additionalFields.stockQuantity as number; - } - if (additionalFields.stockStatus) { - body.stock_status = additionalFields.stockStatus as string; - } - if (additionalFields.tags) { - body.tags = (additionalFields.tags as string[]).map(tag => ({ 'id': parseInt(tag, 10) })) as unknown as IDataObject[]; - } - if (additionalFields.taxClass) { - body.tax_class = additionalFields.taxClass as string; - } - if (additionalFields.taxStatus) { - body.tax_status = additionalFields.taxStatus as string; - } - if (additionalFields.type) { - body.type = additionalFields.type as string; - } - if (additionalFields.upsellIds) { - body.upsell_ids = (additionalFields.upsellIds as string).split(',') as string[]; - } - if (additionalFields.virtual) { - body.virtual = additionalFields.virtual as boolean; - } - if (additionalFields.weight) { - body.weight = additionalFields.weight as string; - } + const images = (this.getNodeParameter('imagesUi', i) as IDataObject).imagesValues as IImage[]; if (images) { body.images = images; @@ -367,105 +279,9 @@ export class WooCommerce implements INodeType { const productId = this.getNodeParameter('productId', i) as string; const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; const body: IProduct = {}; - if (updateFields.name) { - body.name = updateFields.name as string; - } - if (updateFields.backorders) { - body.backorders = updateFields.backorders as string; - } - if (updateFields.buttonText) { - body.button_text = updateFields.buttonText as string; - } - if (updateFields.catalogVisibility) { - body.catalog_visibility = updateFields.catalogVisibility as string; - } - if (updateFields.categories) { - body.categories = (updateFields.categories as string[]).map(category => ({ id: parseInt(category, 10) })) as unknown as IDataObject[]; - } - if (updateFields.crossSellIds) { - body.cross_sell_ids = (updateFields.crossSellIds as string).split(',') as string[]; - } - if (updateFields.dateOnSaleFrom) { - body.date_on_sale_from = updateFields.dateOnSaleFrom as string; - } - if (updateFields.dateOnSaleTo) { - body.date_on_sale_to = updateFields.dateOnSaleTo as string; - } - if (updateFields.description) { - body.description = updateFields.description as string; - } - if (updateFields.downloadable) { - body.downloadable = updateFields.downloadable as boolean; - } - if (updateFields.externalUrl) { - body.external_url = updateFields.externalUrl as string; - } - if (updateFields.featured) { - body.featured = updateFields.featured as boolean; - } - if (updateFields.manageStock) { - body.manage_stock = updateFields.manageStock as boolean; - } - if (updateFields.parentId) { - body.parent_id = updateFields.parentId as string; - } - if (updateFields.purchaseNote) { - body.purchase_note = updateFields.purchaseNote as string; - } - if (updateFields.regularPrice) { - body.regular_price = updateFields.regularPrice as string; - } - if (updateFields.reviewsAllowed) { - body.reviews_allowed = updateFields.reviewsAllowed as boolean; - } - if (updateFields.salePrice) { - body.sale_price = updateFields.salePrice as string; - } - if (updateFields.shippingClass) { - body.shipping_class = updateFields.shippingClass as string; - } - if (updateFields.shortDescription) { - body.short_description = updateFields.shortDescription as string; - } - if (updateFields.sku) { - body.sku = updateFields.sku as string; - } - if (updateFields.slug) { - body.slug = updateFields.slug as string; - } - if (updateFields.soldIndividually) { - body.sold_individually = updateFields.soldIndividually as boolean; - } - if (updateFields.status) { - body.status = updateFields.status as string; - } - if (updateFields.stockQuantity) { - body.stock_quantity = updateFields.stockQuantity as number; - } - if (updateFields.stockStatus) { - body.stock_status = updateFields.stockStatus as string; - } - if (updateFields.tags) { - body.tags = (updateFields.tags as string[]).map(tag => ({ id: parseInt(tag, 10) })) as unknown as IDataObject[]; - } - if (updateFields.taxClass) { - body.tax_class = updateFields.taxClass as string; - } - if (updateFields.taxStatus) { - body.tax_status = updateFields.taxStatus as string; - } - if (updateFields.type) { - body.type = updateFields.type as string; - } - if (updateFields.upsellIds) { - body.upsell_ids = (updateFields.upsellIds as string).split(',') as string[]; - } - if (updateFields.virtual) { - body.virtual = updateFields.virtual as boolean; - } - if (updateFields.weight) { - body.weight = updateFields.weight as string; - } + + setFields(updateFields, body); + const images = (this.getNodeParameter('imagesUi', i) as IDataObject).imagesValues as IImage[]; if (images) { body.images = images; @@ -558,33 +374,9 @@ export class WooCommerce implements INodeType { if (operation === 'create') { const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; const body: IOrder = {}; - if (additionalFields.currency) { - body.currency = additionalFields.currency as string; - } - if (additionalFields.customerId) { - body.customer_id = parseInt(additionalFields.customerId as string, 10); - } - if (additionalFields.customerNote) { - body.customer_note = additionalFields.customerNote as string; - } - if (additionalFields.parentId) { - body.parent_id = parseInt(additionalFields.parentId as string, 10); - } - if (additionalFields.paymentMethodId) { - body.payment_method = additionalFields.paymentMethodId as string; - } - if (additionalFields.paymentMethodTitle) { - body.payment_method_title = additionalFields.paymentMethodTitle as string; - } - if (additionalFields.setPaid) { - body.set_paid = additionalFields.setPaid as boolean; - } - if (additionalFields.status) { - body.status = additionalFields.status as string; - } - if (additionalFields.transactionID) { - body.transaction_id = additionalFields.transactionID as string; - } + + setFields(additionalFields, body); + const billing = (this.getNodeParameter('billingUi', i) as IDataObject).billingValues as IAddress; if (billing !== undefined) { body.billing = billing; @@ -631,6 +423,7 @@ export class WooCommerce implements INodeType { const orderId = this.getNodeParameter('orderId', i) as string; const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; const body: IOrder = {}; + if (updateFields.currency) { body.currency = updateFields.currency as string; } @@ -694,6 +487,7 @@ export class WooCommerce implements INodeType { setMetadata(shippingLines); toSnakeCase(shippingLines); } + responseData = await woocommerceApiRequest.call(this, 'PUT', `/orders/${orderId}`, body); } //https://woocommerce.github.io/woocommerce-rest-api-docs/#retrieve-an-order diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index c5a34fb047..96c0c96734 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -1,6 +1,6 @@ { "name": "n8n-nodes-base", - "version": "0.129.0", + "version": "0.130.0", "description": "Base nodes of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", @@ -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",