diff --git a/packages/nodes-base/nodes/WooCommerce/GenericFunctions.ts b/packages/nodes-base/nodes/WooCommerce/GenericFunctions.ts index 3e42c88505..7be0c77221 100644 --- a/packages/nodes-base/nodes/WooCommerce/GenericFunctions.ts +++ b/packages/nodes-base/nodes/WooCommerce/GenericFunctions.ts @@ -32,6 +32,10 @@ import { snakeCase, } from 'change-case'; +import { + omit +} from 'lodash'; + export async function woocommerceApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IWebhookFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise { // tslint:disable-line:no-any const credentials = this.getCredentials('wooCommerceApi'); if (credentials === undefined) { @@ -144,3 +148,18 @@ export function toSnakeCase(data: } } } + +export function adjustMetadata(fields: IDataObject & Metadata) { + if (!fields.meta_data) return fields; + + return { + ...omit(fields, ['meta_data']), + meta_data: fields.meta_data.meta_data_fields, + }; +} + +type Metadata = { + meta_data?: { + meta_data_fields: Array<{ key: string; value: string }>; + } +}; diff --git a/packages/nodes-base/nodes/WooCommerce/WooCommerce.node.ts b/packages/nodes-base/nodes/WooCommerce/WooCommerce.node.ts index 025318bdcf..9b7361402b 100644 --- a/packages/nodes-base/nodes/WooCommerce/WooCommerce.node.ts +++ b/packages/nodes-base/nodes/WooCommerce/WooCommerce.node.ts @@ -10,6 +10,7 @@ import { INodeTypeDescription, } from 'n8n-workflow'; import { + adjustMetadata, setMetadata, toSnakeCase, woocommerceApiRequest, @@ -37,11 +38,16 @@ import { IShoppingLine, } from './OrderInterface'; +import { + customerFields, + customerOperations, +} from './descriptions'; + export class WooCommerce implements INodeType { description: INodeTypeDescription = { displayName: 'WooCommerce', name: 'wooCommerce', - icon: 'file:wooCommerce.png', + icon: 'file:wooCommerce.svg', group: ['output'], version: 1, subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', @@ -64,6 +70,10 @@ export class WooCommerce implements INodeType { name: 'resource', type: 'options', options: [ + { + name: 'Customer', + value: 'customer', + }, { name: 'Order', value: 'order', @@ -76,6 +86,8 @@ export class WooCommerce implements INodeType { default: 'product', description: 'Resource to consume.', }, + ...customerOperations, + ...customerFields, ...productOperations, ...productFields, ...orderOperations, @@ -128,7 +140,111 @@ export class WooCommerce implements INodeType { const operation = this.getNodeParameter('operation', 0) as string; for (let i = 0; i < length; i++) { - if (resource === 'product') { + + if (resource === 'customer') { + + // ********************************************************************** + // customer + // ********************************************************************** + + // https://woocommerce.github.io/woocommerce-rest-api-docs/?shell#customer-properties + + if (operation === 'create') { + + // ---------------------------------------- + // customer: create + // ---------------------------------------- + + // https://woocommerce.github.io/woocommerce-rest-api-docs/?javascript#create-a-customer + + const body = { + email: this.getNodeParameter('email', i), + } as IDataObject; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + if (Object.keys(additionalFields).length) { + Object.assign(body, adjustMetadata(additionalFields)); + } + + responseData = await woocommerceApiRequest.call(this, 'POST', '/customers', body); + + } else if (operation === 'delete') { + + // ---------------------------------------- + // customer: delete + // ---------------------------------------- + + // https://woocommerce.github.io/woocommerce-rest-api-docs/?javascript#delete-a-customer + + const customerId = this.getNodeParameter('customerId', i); + + const qs: IDataObject = { + force: true, // required, customers do not support trashing + }; + + const endpoint = `/customers/${customerId}`; + responseData = await woocommerceApiRequest.call(this, 'DELETE', endpoint, {}, qs); + + } else if (operation === 'get') { + + // ---------------------------------------- + // customer: get + // ---------------------------------------- + + // https://woocommerce.github.io/woocommerce-rest-api-docs/?javascript#retrieve-a-customer + + const customerId = this.getNodeParameter('customerId', i); + + const endpoint = `/customers/${customerId}`; + responseData = await woocommerceApiRequest.call(this, 'GET', endpoint); + + } else if (operation === 'getAll') { + + // ---------------------------------------- + // customer: getAll + // ---------------------------------------- + + // https://woocommerce.github.io/woocommerce-rest-api-docs/?javascript#list-all-customers + + const qs = {} as IDataObject; + const filters = this.getNodeParameter('filters', i) as IDataObject; + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + + if (Object.keys(filters).length) { + Object.assign(qs, filters); + } + + if (returnAll) { + responseData = await woocommerceApiRequestAllItems.call(this, 'GET', '/customers', {}, qs); + } else { + qs.per_page = this.getNodeParameter('limit', i) as number; + responseData = await woocommerceApiRequest.call(this, 'GET', '/customers', {}, qs); + } + + } else if (operation === 'update') { + + // ---------------------------------------- + // customer: update + // ---------------------------------------- + + // https://woocommerce.github.io/woocommerce-rest-api-docs/?javascript#update-a-customer + + const body = {} as IDataObject; + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + + if (Object.keys(updateFields).length) { + Object.assign(body, adjustMetadata(updateFields)); + } + + const customerId = this.getNodeParameter('customerId', i); + + const endpoint = `/customers/${customerId}`; + responseData = await woocommerceApiRequest.call(this, 'PUT', endpoint, body); + + } + + } else if (resource === 'product') { //https://woocommerce.github.io/woocommerce-rest-api-docs/#create-a-product if (operation === 'create') { const name = this.getNodeParameter('name', i) as string; diff --git a/packages/nodes-base/nodes/WooCommerce/WooCommerceTrigger.node.ts b/packages/nodes-base/nodes/WooCommerce/WooCommerceTrigger.node.ts index 1bea61ff6c..6c4deb2e80 100644 --- a/packages/nodes-base/nodes/WooCommerce/WooCommerceTrigger.node.ts +++ b/packages/nodes-base/nodes/WooCommerce/WooCommerceTrigger.node.ts @@ -23,7 +23,7 @@ export class WooCommerceTrigger implements INodeType { description: INodeTypeDescription = { displayName: 'WooCommerce Trigger', name: 'wooCommerceTrigger', - icon: 'file:wooCommerce.png', + icon: 'file:wooCommerce.svg', group: ['trigger'], version: 1, description: 'Handle WooCommerce events via webhooks', @@ -118,7 +118,7 @@ export class WooCommerceTrigger implements INodeType { const webhookData = this.getWorkflowStaticData('node'); const currentEvent = this.getNodeParameter('event') as string; const endpoint = `/webhooks`; - + const webhooks = await woocommerceApiRequest.call(this, 'GET', endpoint, {}, { status: 'active', per_page: 100 }); for (const webhook of webhooks) { @@ -185,4 +185,4 @@ export class WooCommerceTrigger implements INodeType { ], }; } -} \ No newline at end of file +} diff --git a/packages/nodes-base/nodes/WooCommerce/descriptions/CustomerDescription.ts b/packages/nodes-base/nodes/WooCommerce/descriptions/CustomerDescription.ts new file mode 100644 index 0000000000..4a52553fea --- /dev/null +++ b/packages/nodes-base/nodes/WooCommerce/descriptions/CustomerDescription.ts @@ -0,0 +1,254 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +import { + customerCreateFields, + customerUpdateFields, +} from './shared'; + +export const customerOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'customer', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a customer', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a customer', + }, + { + name: 'Get', + value: 'get', + description: 'Retrieve a customer', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Retrieve all customers', + }, + { + name: 'Update', + value: 'update', + description: 'Update a customer', + }, + ], + default: 'create', + }, +] as INodeProperties[]; + +export const customerFields = [ + // ---------------------------------------- + // customer: create + // ---------------------------------------- + { + displayName: 'Email', + name: 'email', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'customer', + ], + operation: [ + 'create', + ], + }, + }, + }, + customerCreateFields, + + // ---------------------------------------- + // customer: delete + // ---------------------------------------- + { + displayName: 'Customer ID', + name: 'customerId', + description: 'ID of the customer to delete', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'customer', + ], + operation: [ + 'delete', + ], + }, + }, + }, + + // ---------------------------------------- + // customer: get + // ---------------------------------------- + { + displayName: 'Customer ID', + name: 'customerId', + description: 'ID of the customer to retrieve', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'customer', + ], + operation: [ + 'get', + ], + }, + }, + }, + + // ---------------------------------------- + // customer: 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: [ + 'customer', + ], + operation: [ + 'getAll', + ], + }, + }, + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + default: 50, + description: 'How many results to return', + typeOptions: { + minValue: 1, + }, + displayOptions: { + show: { + resource: [ + 'customer', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + }, + { + displayName: 'Filters', + name: 'filters', + type: 'collection', + placeholder: 'Add Filter', + default: {}, + displayOptions: { + show: { + resource: [ + 'customer', + ], + operation: [ + 'getAll', + ], + }, + }, + options: [ + { + displayName: 'Email', + name: 'email', + type: 'string', + default: '', + description: 'Email address to filter customers by', + }, + { + displayName: 'Sort Order', + name: 'order', + description: 'Order to sort customers in', + type: 'options', + options: [ + { + name: 'Ascending', + value: 'asc', + }, + { + name: 'Descending', + value: 'desc', + }, + ], + default: 'asc', + }, + { + displayName: 'Order By', + name: 'orderby', + description: 'Field to sort customers by', + type: 'options', + options: [ + { + name: 'ID', + value: 'id', + }, + { + name: 'Include', + value: 'include', + }, + { + name: 'Name', + value: 'name', + }, + { + name: 'Registered Date', + value: 'registered_date', + }, + ], + default: 'id', + }, + ], + }, + + // ---------------------------------------- + // customer: update + // ---------------------------------------- + { + displayName: 'Customer ID', + name: 'customerId', + description: 'ID of the customer to update', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'customer', + ], + operation: [ + 'update', + ], + }, + }, + }, + customerUpdateFields, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/WooCommerce/descriptions/index.ts b/packages/nodes-base/nodes/WooCommerce/descriptions/index.ts new file mode 100644 index 0000000000..184b55e69e --- /dev/null +++ b/packages/nodes-base/nodes/WooCommerce/descriptions/index.ts @@ -0,0 +1 @@ +export * from './CustomerDescription'; diff --git a/packages/nodes-base/nodes/WooCommerce/descriptions/shared.ts b/packages/nodes-base/nodes/WooCommerce/descriptions/shared.ts new file mode 100644 index 0000000000..14e3fa1f94 --- /dev/null +++ b/packages/nodes-base/nodes/WooCommerce/descriptions/shared.ts @@ -0,0 +1,177 @@ +const customerAddressOptions = [ + { + displayName: 'First Name', + name: 'first_name', + type: 'string', + default: '', + }, + { + displayName: 'Last Name', + name: 'last_name', + type: 'string', + default: '', + }, + { + displayName: 'Company', + name: 'company', + type: 'string', + default: '', + }, + { + displayName: 'Address 1', + name: 'address_1', + type: 'string', + default: '', + }, + { + displayName: 'Address 2', + name: 'address_2', + type: 'string', + default: '', + }, + { + displayName: 'City', + name: 'city', + type: 'string', + default: '', + }, + { + displayName: 'State', + name: 'state', + type: 'string', + default: '', + }, + { + displayName: 'Postcode', + name: 'postcode', + type: 'string', + default: '', + }, + { + displayName: 'Country', + name: 'country', + type: 'string', + default: '', + }, + { + displayName: 'Email', + name: 'email', + type: 'string', + default: '', + }, + { + displayName: 'Phone', + name: 'phone', + type: 'string', + default: '', + }, +]; + +const customerUpdateOptions = [ + { + displayName: 'Billing Address', + name: 'billing', + type: 'collection', + default: {}, + placeholder: 'Add Field', + options: customerAddressOptions, + }, + { + displayName: 'First Name', + name: 'first_name', + type: 'string', + default: '', + }, + { + displayName: 'Last Name', + name: 'last_name', + type: 'string', + default: '', + }, + { + displayName: 'Metadata', + name: 'meta_data', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + default: {}, + placeholder: 'Add Metadata Field', + options: [ + { + displayName: 'Metadata Fields', + name: 'meta_data_fields', + values: [ + { + displayName: 'Key', + name: 'key', + type: 'string', + default: '', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + }, + ], + }, + ], + }, + { + displayName: 'Shipping Address', + name: 'shipping', + type: 'collection', + default: {}, + placeholder: 'Add Field', + options: customerAddressOptions, + }, +]; + +const customerCreateOptions = [ + ...customerUpdateOptions, + { + displayName: 'Username', + name: 'username', + type: 'string', + default: '', + }, +]; + +export const customerCreateFields = { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'customer', + ], + operation: [ + 'create', + ], + }, + }, + options: customerCreateOptions, +}; + +export const customerUpdateFields = { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'customer', + ], + operation: [ + 'update', + ], + }, + }, + options: customerUpdateOptions, +}; diff --git a/packages/nodes-base/nodes/WooCommerce/wooCommerce.png b/packages/nodes-base/nodes/WooCommerce/wooCommerce.png deleted file mode 100644 index 187de2a2f0..0000000000 Binary files a/packages/nodes-base/nodes/WooCommerce/wooCommerce.png and /dev/null differ diff --git a/packages/nodes-base/nodes/WooCommerce/wooCommerce.svg b/packages/nodes-base/nodes/WooCommerce/wooCommerce.svg new file mode 100644 index 0000000000..9cde2a9d41 --- /dev/null +++ b/packages/nodes-base/nodes/WooCommerce/wooCommerce.svg @@ -0,0 +1,14 @@ + +WooCommerce Logo + + + +image/svg+xml + + + + + + + +