diff --git a/packages/nodes-base/credentials/FreshworksCrmApi.credentials.ts b/packages/nodes-base/credentials/FreshworksCrmApi.credentials.ts
index 115fb998f4..f81f46e7dc 100644
--- a/packages/nodes-base/credentials/FreshworksCrmApi.credentials.ts
+++ b/packages/nodes-base/credentials/FreshworksCrmApi.credentials.ts
@@ -1,4 +1,6 @@
import {
+ IAuthenticateGeneric,
+ ICredentialTestRequest,
ICredentialType,
INodeProperties,
} from 'n8n-workflow';
@@ -24,4 +26,19 @@ export class FreshworksCrmApi implements ICredentialType {
description: 'Domain in the Freshworks CRM org URL. For example, in https://n8n-org.myfreshworks.com
, the domain is n8n-org
.',
},
];
+ authenticate: IAuthenticateGeneric = {
+ type: 'generic',
+ properties: {
+ headers: {
+ 'Authorization': '=Token token={{$credentials?.apiKey}}',
+ },
+ },
+ };
+ test: ICredentialTestRequest = {
+ request: {
+ baseURL: '=https://{{$credentials?.domain}}.myfreshworks.com/crm/sales/api',
+ url: '/tasks',
+ method: 'GET',
+ },
+ };
}
diff --git a/packages/nodes-base/nodes/FreshworksCrm/FreshworksCrm.node.ts b/packages/nodes-base/nodes/FreshworksCrm/FreshworksCrm.node.ts
index 9dbfd1fc68..2ad3fd4e37 100644
--- a/packages/nodes-base/nodes/FreshworksCrm/FreshworksCrm.node.ts
+++ b/packages/nodes-base/nodes/FreshworksCrm/FreshworksCrm.node.ts
@@ -34,6 +34,8 @@ import {
noteOperations,
salesActivityFields,
salesActivityOperations,
+ searchFields,
+ searchOperations,
taskFields,
taskOperations,
} from './descriptions';
@@ -100,6 +102,10 @@ export class FreshworksCrm implements INodeType {
name: 'Sales Activity',
value: 'salesActivity',
},
+ {
+ name: 'Search',
+ value: 'search',
+ },
{
name: 'Task',
value: 'task',
@@ -119,6 +125,8 @@ export class FreshworksCrm implements INodeType {
...noteFields,
...salesActivityOperations,
...salesActivityFields,
+ ...searchOperations,
+ ...searchFields,
...taskOperations,
...taskFields,
],
@@ -855,6 +863,58 @@ export class FreshworksCrm implements INodeType {
}
+ } else if (resource === 'search') {
+
+ // **********************************************************************
+ // search
+ // **********************************************************************
+
+ if (operation === 'query') {
+ // https://developers.freshworks.com/crm/api/#search
+ const query = this.getNodeParameter('query', i) as string;
+ let entities = this.getNodeParameter('entities', i);
+ const returnAll = this.getNodeParameter('returnAll', 0, false);
+
+ if (Array.isArray(entities)) {
+ entities = entities.join(',');
+ }
+
+ const qs: IDataObject = {
+ q: query,
+ include: entities,
+ per_page: 100,
+ };
+
+ responseData = await freshworksCrmApiRequest.call(this, 'GET', '/search', {}, qs);
+
+ if (!returnAll) {
+ const limit = this.getNodeParameter('limit', 0);
+ responseData = responseData.slice(0, limit);
+ }
+ }
+
+ if (operation === 'lookup') {
+ // https://developers.freshworks.com/crm/api/#lookup_search
+ let searchField = this.getNodeParameter('searchField', i) as string;
+ let fieldValue = this.getNodeParameter('fieldValue', i, '') as string;
+ let entities = this.getNodeParameter('options.entities', i) as string;
+ if (Array.isArray(entities)) {
+ entities = entities.join(',');
+ }
+
+ if (searchField === 'customField') {
+ searchField = this.getNodeParameter('customFieldName', i) as string;
+ fieldValue = this.getNodeParameter('customFieldValue', i) as string;
+ }
+
+ const qs: IDataObject = {
+ q: fieldValue,
+ f: searchField,
+ entities,
+ };
+
+ responseData = await freshworksCrmApiRequest.call(this, 'GET', '/lookup', {}, qs);
+ }
} else if (resource === 'task') {
// **********************************************************************
diff --git a/packages/nodes-base/nodes/FreshworksCrm/GenericFunctions.ts b/packages/nodes-base/nodes/FreshworksCrm/GenericFunctions.ts
index 25ec4f0d87..e1d41870fd 100644
--- a/packages/nodes-base/nodes/FreshworksCrm/GenericFunctions.ts
+++ b/packages/nodes-base/nodes/FreshworksCrm/GenericFunctions.ts
@@ -31,12 +31,9 @@ export async function freshworksCrmApiRequest(
body: IDataObject = {},
qs: IDataObject = {},
) {
- const { apiKey, domain } = await this.getCredentials('freshworksCrmApi') as FreshworksCrmApiCredentials;
+ const { domain } = await this.getCredentials('freshworksCrmApi') as FreshworksCrmApiCredentials;
const options: OptionsWithUri = {
- headers: {
- Authorization: `Token token=${apiKey}`,
- },
method,
body,
qs,
@@ -52,7 +49,8 @@ export async function freshworksCrmApiRequest(
delete options.qs;
}
try {
- return await this.helpers.request!(options);
+ const credentialsType = 'freshworksCrmApi';
+ return await this.helpers.requestWithAuthentication.call(this, credentialsType, options);
} catch (error) {
throw new NodeApiError(this.getNode(), error);
}
diff --git a/packages/nodes-base/nodes/FreshworksCrm/descriptions/SearchDescription.ts b/packages/nodes-base/nodes/FreshworksCrm/descriptions/SearchDescription.ts
new file mode 100644
index 0000000000..fed7104aa9
--- /dev/null
+++ b/packages/nodes-base/nodes/FreshworksCrm/descriptions/SearchDescription.ts
@@ -0,0 +1,267 @@
+import {
+ INodeProperties,
+} from 'n8n-workflow';
+
+export const searchOperations: INodeProperties[] = [
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ noDataExpression: true,
+ displayOptions: {
+ show: {
+ resource: [
+ 'search',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Query',
+ value: 'query',
+ description: 'Search for records by entering search queries of your choice',
+ },
+ {
+ name: 'Lookup',
+ value: 'lookup',
+ description: 'Search for the name or email address of records',
+ },
+ ],
+ default: 'query',
+ },
+];
+
+export const searchFields: INodeProperties[] = [
+ // ----------------------------------------
+ // Search: query
+ // ----------------------------------------
+ {
+ displayName: 'Search Term',
+ name: 'query',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'search',
+ ],
+ operation: [
+ 'query',
+ ],
+ },
+ },
+ description: 'Enter a term that will be used for searching entities',
+ },
+ {
+ displayName: 'Search on Entities',
+ name: 'entities',
+ type: 'multiOptions',
+ options: [
+ {
+ name: 'Contact',
+ value: 'contact',
+ },
+ {
+ name: 'Deal',
+ value: 'deal',
+ },
+ {
+ name: 'Sales Account',
+ value: 'sales_account',
+ },
+ {
+ name: 'User',
+ value: 'user',
+ },
+ ],
+ required: true,
+ default: [],
+ displayOptions: {
+ show: {
+ resource: [
+ 'search',
+ ],
+ operation: [
+ 'query',
+ ],
+ },
+ },
+ description: 'Enter a term that will be used for searching entities',
+ },
+ {
+ displayName: 'Return All',
+ name: 'returnAll',
+ type: 'boolean',
+ default: false,
+ displayOptions: {
+ show: {
+ resource: [
+ 'search',
+ ],
+ operation: [
+ 'query',
+ ],
+ },
+ },
+ description: 'Whether to return all results or only up to a given limit',
+ },
+ {
+ displayName: 'Limit',
+ name: 'limit',
+ type: 'number',
+ default: 25,
+ displayOptions: {
+ show: {
+ resource: [
+ 'search',
+ ],
+ operation: [
+ 'query',
+ ],
+ returnAll: [
+ false,
+ ],
+ },
+ },
+ description: 'Max number of results to return',
+ },
+ // ----------------------------------------
+ // Search: lookup
+ // ----------------------------------------
+ {
+ displayName: 'Search Field',
+ name: 'searchField',
+ type: 'options',
+ options: [
+ {
+ name: 'Email',
+ value: 'email',
+ },
+ {
+ name: 'Name',
+ value: 'name',
+ },
+ {
+ name: 'Custom Field',
+ value: 'customField',
+ description: 'Only allowed custom fields of type "Text field", "Number", "Dropdown" or "Radio button"',
+ },
+ ],
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'search',
+ ],
+ operation: [
+ 'lookup',
+ ],
+ },
+ },
+ description: 'Field against which the entities have to be searched',
+ },
+ {
+ displayName: 'Custom Field Name',
+ name: 'customFieldName',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'search',
+ ],
+ operation: [
+ 'lookup',
+ ],
+ searchField: [
+ 'customField',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Custom Field Value',
+ name: 'customFieldValue',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'search',
+ ],
+ operation: [
+ 'lookup',
+ ],
+ searchField: [
+ 'customField',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Field Value',
+ name: 'fieldValue',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'search',
+ ],
+ operation: [
+ 'lookup',
+ ],
+ searchField: [
+ 'email',
+ 'name',
+ ],
+ },
+ },
+ },
+ {
+ displayName: 'Options',
+ name: 'options',
+ type: 'collection',
+ placeholder: 'Add Option',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: [
+ 'search',
+ ],
+ operation: [
+ 'lookup',
+ ],
+ },
+ },
+ options: [
+ {
+ displayName: 'Entities',
+ name: 'entities',
+ type: 'multiOptions',
+ default: [],
+ options: [
+ {
+ name: 'Contact',
+ value: 'contact',
+ },
+ {
+ name: 'Deal',
+ value: 'deal',
+ },
+ {
+ name: 'Sales Account',
+ value: 'sales_account',
+ },
+ ],
+ // eslint-disable-next-line n8n-nodes-base/node-param-description-unneeded-backticks
+ description: `Use 'entities' to query against related entities. You can include multiple entities at once, provided the field is available in both entities or else you'd receive an error response.`,
+ },
+ ],
+ },
+];
diff --git a/packages/nodes-base/nodes/FreshworksCrm/descriptions/index.ts b/packages/nodes-base/nodes/FreshworksCrm/descriptions/index.ts
index 70957a2c38..06a000c960 100644
--- a/packages/nodes-base/nodes/FreshworksCrm/descriptions/index.ts
+++ b/packages/nodes-base/nodes/FreshworksCrm/descriptions/index.ts
@@ -4,4 +4,5 @@ export * from './ContactDescription';
export * from './DealDescription';
export * from './NoteDescription';
export * from './SalesActivityDescription';
+export * from './SearchDescription';
export * from './TaskDescription';