Add filters to all getAll operations

This commit is contained in:
Iván Ovejero 2021-06-01 10:43:57 +02:00
parent 2863b04fff
commit 497ffc9679
5 changed files with 286 additions and 144 deletions

View file

@ -21,13 +21,16 @@ import {
import {
AllFields,
CamelCaseResource,
DateType,
GetAllFilterOptions,
IdType,
LoadedFields,
LocationType,
NameType,
ProductDetails,
ResourceItems,
SnakeCaseResource,
ZohoOAuth2ApiCredentials,
} from './types';
@ -62,6 +65,7 @@ export async function zohoApiRequest(
}
try {
console.log(options);
const responseData = await this.helpers.requestOAuth2?.call(this, 'zohoOAuth2Api', options);
if (responseData === undefined) return [];
@ -127,7 +131,7 @@ export async function handleListing(
return responseData.slice(0, limit);
}
export function throwOnEmptyUpdate(this: IExecuteFunctions, resource: string) {
export function throwOnEmptyUpdate(this: IExecuteFunctions, resource: CamelCaseResource) {
throw new NodeOperationError(
this.getNode(),
`Please enter at least one field to update for the ${resource}.`,
@ -291,7 +295,7 @@ export const toLoadOptions = (items: ResourceItems, nameProperty: NameType) =>
/**
* Retrieve all fields for a resource, sorted alphabetically.
*/
export async function getFields(this: ILoadOptionsFunctions, resource: string) {
export async function getFields(this: ILoadOptionsFunctions, resource: SnakeCaseResource) {
const { fields } = await zohoApiRequest.call(
this, 'GET', '/settings/fields', {}, { module: `${resource}s` },
) as LoadedFields;
@ -299,3 +303,13 @@ export async function getFields(this: ILoadOptionsFunctions, resource: string) {
return sortBy(options, o => o.name);
}
/**
* Add filter options to a query string object.
*/
export const addGetAllFilterOptions = (qs: IDataObject, options: GetAllFilterOptions) => {
if (Object.keys(options).length) {
const { fields, ...rest } = options;
Object.assign(qs, fields && { fields: fields.join(',') }, rest);
}
};

View file

@ -11,6 +11,7 @@ import {
} from 'n8n-workflow';
import {
addGetAllFilterOptions,
adjustAccountPayload,
adjustContactPayload,
adjustDealPayload,
@ -30,10 +31,11 @@ import {
} from './GenericFunctions';
import {
CamelCaseResource,
GetAllFilterOptions,
LoadedAccounts,
LoadedContacts,
LoadedDeals,
LoadedFields,
LoadedProducts,
LoadedVendors,
ProductDetails,
@ -158,6 +160,10 @@ export class ZohoCrm implements INodeType {
methods = {
loadOptions: {
// ----------------------------------------
// resources
// ----------------------------------------
async getAccounts(this: ILoadOptionsFunctions) {
const accounts = await zohoApiRequestAllItems.call(this, 'GET', '/accounts') as LoadedAccounts;
return toLoadOptions(accounts, 'Account_Name');
@ -173,10 +179,6 @@ export class ZohoCrm implements INodeType {
return toLoadOptions(deals, 'Deal_Name');
},
async getLeadFields(this: ILoadOptionsFunctions) {
return getFields.call(this, 'lead');
},
async getProducts(this: ILoadOptionsFunctions) {
const products = await zohoApiRequestAllItems.call(this, 'GET', '/products') as LoadedProducts;
return toLoadOptions(products, 'Product_Name');
@ -186,6 +188,54 @@ export class ZohoCrm implements INodeType {
const vendors = await zohoApiRequestAllItems.call(this, 'GET', '/vendors') as LoadedVendors;
return toLoadOptions(vendors, 'Vendor_Name');
},
// ----------------------------------------
// resource fields
// ----------------------------------------
async getAccountFields(this: ILoadOptionsFunctions) {
return getFields.call(this, 'account');
},
async getContactFields(this: ILoadOptionsFunctions) {
return getFields.call(this, 'contact');
},
async getDealFields(this: ILoadOptionsFunctions) {
return getFields.call(this, 'deal');
},
async getInvoiceFields(this: ILoadOptionsFunctions) {
return getFields.call(this, 'invoice');
},
async getLeadFields(this: ILoadOptionsFunctions) {
return getFields.call(this, 'lead');
},
async getProductFields(this: ILoadOptionsFunctions) {
return getFields.call(this, 'product');
},
async getPurchaseOrderFields(this: ILoadOptionsFunctions) {
return getFields.call(this, 'purchase_order');
},
async getVendorOrderFields(this: ILoadOptionsFunctions) {
return getFields.call(this, 'vendor');
},
async getQuoteFields(this: ILoadOptionsFunctions) {
return getFields.call(this, 'quote');
},
async getSalesOrderFields(this: ILoadOptionsFunctions) {
return getFields.call(this, 'sales_order');
},
async getVendorFields(this: ILoadOptionsFunctions) {
return getFields.call(this, 'vendor');
},
},
};
@ -193,7 +243,7 @@ export class ZohoCrm implements INodeType {
const items = this.getInputData();
const returnData: IDataObject[] = [];
const resource = this.getNodeParameter('resource', 0) as string;
const resource = this.getNodeParameter('resource', 0) as CamelCaseResource;
const operation = this.getNodeParameter('operation', 0) as string;
let responseData;
@ -205,7 +255,7 @@ export class ZohoCrm implements INodeType {
// https://www.zoho.com/crm/developer/docs/api/update-specific-record.html
// https://www.zoho.com/crm/developer/docs/api/delete-specific-record.html
//try {
// try {
if (resource === 'account') {
@ -264,7 +314,12 @@ export class ZohoCrm implements INodeType {
// account: getAll
// ----------------------------------------
responseData = await handleListing.call(this, 'GET', '/accounts');
const qs: IDataObject = {};
const options = this.getNodeParameter('options', i) as GetAllFilterOptions;
addGetAllFilterOptions(qs, options);
responseData = await handleListing.call(this, 'GET', '/accounts', {}, qs);
} else if (operation === 'update') {
@ -365,7 +420,12 @@ export class ZohoCrm implements INodeType {
// contact: getAll
// ----------------------------------------
responseData = await handleListing.call(this, 'GET', '/contacts');
const qs: IDataObject = {};
const options = this.getNodeParameter('options', i) as GetAllFilterOptions;
addGetAllFilterOptions(qs, options);
responseData = await handleListing.call(this, 'GET', '/contacts', {}, qs);
} else if (operation === 'update') {
@ -465,7 +525,12 @@ export class ZohoCrm implements INodeType {
// deal: getAll
// ----------------------------------------
responseData = await handleListing.call(this, 'GET', '/deals');
const qs: IDataObject = {};
const options = this.getNodeParameter('options', i) as GetAllFilterOptions;
addGetAllFilterOptions(qs, options);
responseData = await handleListing.call(this, 'GET', '/deals', {}, qs);
} else if (operation === 'update') {
@ -569,7 +634,12 @@ export class ZohoCrm implements INodeType {
// invoice: getAll
// ----------------------------------------
responseData = await handleListing.call(this, 'GET', '/invoices');
const qs: IDataObject = {};
const options = this.getNodeParameter('options', i) as GetAllFilterOptions;
addGetAllFilterOptions(qs, options);
responseData = await handleListing.call(this, 'GET', '/invoices', {}, qs);
} else if (operation === 'update') {
@ -672,11 +742,9 @@ export class ZohoCrm implements INodeType {
// ----------------------------------------
const qs: IDataObject = {};
const options = this.getNodeParameter('options', i) as IDataObject;
const options = this.getNodeParameter('options', i) as GetAllFilterOptions;
if (Object.keys(options).length) {
Object.assign(qs, options);
}
addGetAllFilterOptions(qs, options);
responseData = await handleListing.call(this, 'GET', '/leads', {}, qs);
@ -779,7 +847,12 @@ export class ZohoCrm implements INodeType {
// product: getAll
// ----------------------------------------
responseData = await handleListing.call(this, 'GET', '/products');
const qs: IDataObject = {};
const options = this.getNodeParameter('options', i) as GetAllFilterOptions;
addGetAllFilterOptions(qs, options);
responseData = await handleListing.call(this, 'GET', '/products', {}, qs);
} else if (operation === 'update') {
@ -884,7 +957,12 @@ export class ZohoCrm implements INodeType {
// purchaseOrder: getAll
// ----------------------------------------
responseData = await handleListing.call(this, 'GET', '/purchase_orders');
const qs: IDataObject = {};
const options = this.getNodeParameter('options', i) as GetAllFilterOptions;
addGetAllFilterOptions(qs, options);
responseData = await handleListing.call(this, 'GET', '/purchase_orders', {}, qs);
} else if (operation === 'update') {
@ -990,7 +1068,12 @@ export class ZohoCrm implements INodeType {
// quote: getAll
// ----------------------------------------
responseData = await handleListing.call(this, 'GET', '/quotes');
const qs: IDataObject = {};
const options = this.getNodeParameter('options', i) as GetAllFilterOptions;
addGetAllFilterOptions(qs, options);
responseData = await handleListing.call(this, 'GET', '/quotes', {}, qs);
} else if (operation === 'update') {
@ -1097,7 +1180,12 @@ export class ZohoCrm implements INodeType {
// salesOrder: getAll
// ----------------------------------------
responseData = await handleListing.call(this, 'GET', '/sales_orders');
const qs: IDataObject = {};
const options = this.getNodeParameter('options', i) as GetAllFilterOptions;
addGetAllFilterOptions(qs, options);
responseData = await handleListing.call(this, 'GET', '/sales_orders', {}, qs);
} else if (operation === 'update') {
@ -1202,7 +1290,12 @@ export class ZohoCrm implements INodeType {
// vendor: getAll
// ----------------------------------------
responseData = await handleListing.call(this, 'GET', '/vendors');
const qs: IDataObject = {};
const options = this.getNodeParameter('options', i) as GetAllFilterOptions;
addGetAllFilterOptions(qs, options);
responseData = await handleListing.call(this, 'GET', '/sales_orders');
} else if (operation === 'update') {

View file

@ -294,89 +294,6 @@ export const leadFields = [
// lead: getAll
// ----------------------------------------
...makeGetAllFields('lead'),
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Option',
default: {},
displayOptions: {
show: {
resource: [
'lead',
],
operation: [
'getAll',
],
},
},
options: [
{
displayName: 'Approved',
name: 'approved',
type: 'boolean',
default: true,
description: 'Retrieve only approved leads. Defaults to true.',
},
{
displayName: 'Converted',
name: 'converted',
type: 'boolean',
default: false,
description: 'Retrieve only converted leads. Defaults to false.',
},
{
displayName: 'Fields',
name: 'fields',
type: 'multiOptions',
typeOptions: {
loadOptionsMethod: 'getLeadFields',
},
default: [],
},
{
displayName: 'Include Child',
name: 'include_child',
type: 'boolean',
default: false,
description: 'Retrieve only leads from child territories.',
},
{
displayName: 'Sort By',
name: 'sort_by',
type: 'multiOptions',
typeOptions: {
loadOptionsMethod: 'getLeadFields',
},
default: [],
description: 'Field to sort leads by.',
},
{
displayName: 'Sort Order',
name: 'sort_order',
type: 'options',
options: [
{
name: 'Ascending',
value: 'asc',
},
{
name: 'Descending',
value: 'desc',
},
],
default: 'desc',
description: 'Ascending or descending order sort order.',
},
{
displayName: 'Territory ID',
name: 'territory_id',
type: 'string',
default: '',
description: 'Retrieve only leads from this territory.',
},
],
},
// ----------------------------------------
// lead: update

View file

@ -1,3 +1,5 @@
import { CamelCaseResource } from '../types';
export const billingAddress = {
displayName: 'Billing Address',
name: 'Billing_Address',
@ -310,46 +312,144 @@ export const makeProductDetails = (resource: string, operation: string, { hasUps
],
});
export const makeGetAllFields = (resource: string) => [
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
default: false,
description: 'Return all results.',
displayOptions: {
show: {
resource: [
resource,
],
operation: [
'getAll',
],
export const makeGetAllFields = (resource: CamelCaseResource) => {
const loadOptionsMethod = {
account: 'getAccountFields',
contact: 'getContactFields',
deal: 'getDealFields',
invoice: 'getInvoiceFields',
lead: 'getLeadFields',
product: 'getProductFields',
purchaseOrder: 'getPurchaseOrderFields',
quote: 'getQuoteFields',
salesOrder: 'getSalesOrderFields',
vendor: 'getVendorFields',
}[resource];
return [
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
default: false,
description: 'Return all results.',
displayOptions: {
show: {
resource: [
resource,
],
operation: [
'getAll',
],
},
},
},
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
default: 5,
description: 'The number of results to return.',
typeOptions: {
minValue: 1,
maxValue: 1000,
},
displayOptions: {
show: {
resource: [
resource,
],
operation: [
'getAll',
],
returnAll: [
false,
],
{
displayName: 'Limit',
name: 'limit',
type: 'number',
default: 5,
description: 'The number of results to return.',
typeOptions: {
minValue: 1,
maxValue: 1000,
},
displayOptions: {
show: {
resource: [
resource,
],
operation: [
'getAll',
],
returnAll: [
false,
],
},
},
},
},
];
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Option',
default: {},
displayOptions: {
show: {
resource: [
resource,,
],
operation: [
'getAll',
],
},
},
options: [
{
displayName: 'Approved',
name: 'approved',
type: 'boolean',
default: true,
description: 'Retrieve only approved leads. Defaults to true.',
},
{
displayName: 'Converted',
name: 'converted',
type: 'boolean',
default: false,
description: 'Retrieve only converted leads. Defaults to false.',
},
{
displayName: 'Fields',
name: 'fields',
type: 'multiOptions',
typeOptions: {
loadOptionsMethod,
},
default: [],
},
{
displayName: 'Include Child',
name: 'include_child',
type: 'boolean',
default: false,
description: 'Retrieve only leads from child territories.',
},
{
displayName: 'Sort By',
name: 'sort_by',
type: 'multiOptions',
typeOptions: {
loadOptionsMethod,
},
default: [],
description: 'Field to sort leads by.',
},
{
displayName: 'Sort Order',
name: 'sort_order',
type: 'options',
options: [
{
name: 'Ascending',
value: 'asc',
},
{
name: 'Descending',
value: 'desc',
},
],
default: 'desc',
description: 'Ascending or descending order sort order.',
},
{
displayName: 'Territory ID',
name: 'territory_id',
type: 'string',
default: '',
description: 'Retrieve only leads from this territory.',
},
],
},
];
};

View file

@ -1,5 +1,23 @@
import { IDataObject } from "n8n-workflow";
// ----------------------------------------
// for generic functions
// ----------------------------------------
export type CamelCaseResource = 'account' | 'contact' | 'deal' | 'invoice' | 'lead' | 'product' | 'purchaseOrder' | 'quote' | 'salesOrder' | 'vendor';
export type SnakeCaseResource = CamelToSnakeCase<CamelCaseResource>
type CamelToSnakeCase<S extends string> =
S extends `${infer S1}${infer S2}`
? `${S1 extends Capitalize<S1> ? "_" : ""}${Lowercase<S1>}${CamelToSnakeCase<S2>}`
: S
export type GetAllFilterOptions = {
fields: string[],
[otherOptions: string]: unknown;
};
// ----------------------------------------
// for auth
// ----------------------------------------