From fa23ee94d8a7f16f19547f0590463237d3e88ca0 Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Sun, 16 Feb 2020 13:43:51 -0500 Subject: [PATCH] :sparkles: woocommerce node --- .../nodes/WooCommerce/GenericFunctions.ts | 19 + .../nodes/WooCommerce/ProductDescription.ts | 1456 +++++++++++++++++ .../nodes/WooCommerce/ProductInterface.ts | 54 + .../nodes/WooCommerce/WooCommerce.node.ts | 435 +++++ packages/nodes-base/package.json | 1 + 5 files changed, 1965 insertions(+) create mode 100644 packages/nodes-base/nodes/WooCommerce/ProductDescription.ts create mode 100644 packages/nodes-base/nodes/WooCommerce/ProductInterface.ts create mode 100644 packages/nodes-base/nodes/WooCommerce/WooCommerce.node.ts diff --git a/packages/nodes-base/nodes/WooCommerce/GenericFunctions.ts b/packages/nodes-base/nodes/WooCommerce/GenericFunctions.ts index dcdd79aaab..776cb6a4f9 100644 --- a/packages/nodes-base/nodes/WooCommerce/GenericFunctions.ts +++ b/packages/nodes-base/nodes/WooCommerce/GenericFunctions.ts @@ -47,6 +47,25 @@ export async function woocommerceApiRequest(this: IHookFunctions | IExecuteFunct } } +export async function woocommerceApiRequestAllItems(this: IExecuteFunctions | ILoadOptionsFunctions, method: string, endpoint: string, body: any = {}, query: IDataObject = {}): Promise { // tslint:disable-line:no-any + + const returnData: IDataObject[] = []; + + let responseData; + let uri: string | undefined; + query.per_page = 100; + do { + responseData = await woocommerceApiRequest.call(this, method, endpoint, body, query, uri, { resolveWithFullResponse: true }); + uri = responseData.headers['link'].split(';')[0].replace('<', '').replace('>',''); + returnData.push.apply(returnData, responseData.body); + } while ( + responseData.headers['link'] !== undefined && + responseData.headers['link'].includes('rel="next"') + ); + + return returnData; +} + /** * Creates a secret from the credentials * diff --git a/packages/nodes-base/nodes/WooCommerce/ProductDescription.ts b/packages/nodes-base/nodes/WooCommerce/ProductDescription.ts new file mode 100644 index 0000000000..8780922b2e --- /dev/null +++ b/packages/nodes-base/nodes/WooCommerce/ProductDescription.ts @@ -0,0 +1,1456 @@ +import { INodeProperties } from 'n8n-workflow'; + +export const productOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'product', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a product', + }, + { + name: 'Update', + value: 'update', + description: 'Update a product', + }, + { + name: 'Get', + value: 'get', + description: 'Get a product', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all products', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a product', + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const productFields = [ + +/* -------------------------------------------------------------------------- */ +/* product:create */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Name', + name: 'name', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'product', + ], + operation: [ + 'create', + ] + }, + }, + description: 'Product name.', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'product', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + { + displayName: 'Backorders', + name: 'backorders', + type: 'options', + options: [ + { + name: 'No', + value: 'no', + }, + { + name: 'Notify', + value: 'notify', + }, + { + name: 'Yes', + value: 'yes', + }, + ], + default: 'no', + description: 'If managing stock, this controls if backorders are allowed', + }, + { + displayName: 'Button Text', + name: 'buttonText', + type: 'string', + default: '', + description: 'Product external button text. Only for external products.', + }, + { + displayName: 'Catalog Visibility', + name: 'catalogVisibility', + type: 'options', + options: [ + { + name: 'Visible', + value: 'visible', + }, + { + name: 'Catalog', + value: 'catalog', + }, + { + name: 'Search', + value: 'search', + }, + { + name: 'Hidden', + value: 'hidden', + }, + ], + default: 'visible', + description: 'Catalog visibility.', + }, + { + displayName: 'Categories', + name: 'categories', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getCategories', + }, + default: [], + description: 'List of categories', + }, + { + displayName: 'Cross Sell IDs', + name: 'crossSellIds', + type: 'string', + default: '', + description: 'List of cross-sell products IDs. Multiple can be added separated by ,', + }, + { + displayName: 'Date On Sale From', + name: 'dateOnSaleFrom', + type: 'dateTime', + default: '', + description: `Start date of sale price, in the site's timezone.`, + }, + { + displayName: 'Date On Sale To', + name: 'dateOnSaleTo', + type: 'dateTime', + default: '', + description: `Ennd date of sale price, in the site's timezone.`, + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + description: 'Product description.', + }, + { + displayName: 'Downloadable', + name: 'downloadable', + type: 'boolean', + default: false, + description: 'if the product is downloadable.', + }, + { + displayName: 'External URL', + name: 'externalUrl', + type: 'string', + default: '', + description: 'Product external URL. Only for external products.', + }, + { + displayName: 'Featured', + name: 'featured', + type: 'boolean', + default: false, + description: 'Featured product', + }, + { + displayName: 'Manage Stock', + name: 'manageStock', + type: 'boolean', + default: false, + description: 'Stock management at product level', + }, + { + displayName: 'Menu Order', + name: 'menuOrder', + type: 'number', + default: 1, + description: 'Menu order, used to custom sort products.', + }, + { + displayName: 'Parent ID', + name: 'parentId', + type: 'string', + default: '', + description: 'Product parent ID.', + }, + { + displayName: 'Price', + name: 'price', + type: 'string', + default: '', + description: 'Current product price.', + }, + { + displayName: 'Purchase Note', + name: 'purchaseNote', + type: 'string', + default: '', + description: 'Optional note to send the customer after purchase.', + }, + { + displayName: 'Regular Price', + name: 'regularPrice', + type: 'string', + default: '', + description: 'Product regular price.', + }, + { + displayName: 'Reviews Allowed', + name: 'reviewsAllowed', + type: 'boolean', + default: true, + description: 'Allow reviews.', + }, + { + displayName: 'Sale Price', + name: 'salePrice', + type: 'string', + default: '', + description: 'Product sale price.', + }, + { + displayName: 'Shipping Class', + name: 'shippingClass', + type: 'string', + default: '', + description: 'Shipping class slug.', + }, + { + displayName: 'Short Description', + name: 'shortDescription', + type: 'string', + default: '', + description: 'Product short description.', + }, + { + displayName: 'SKU', + name: 'sku', + type: 'string', + default: '', + description: 'Unique identifier.', + }, + { + displayName: 'Slug', + name: 'slug', + type: 'string', + default: '', + description: 'Product slug.', + }, + { + displayName: 'Sold Individually', + name: 'soldIndividually', + type: 'boolean', + default: false, + description: 'Allow one item to be bought in a single order.', + }, + { + displayName: 'Status', + name: 'status', + type: 'options', + options: [ + { + name: 'Draft', + value: 'draft', + }, + { + name: 'Pending', + value: 'pending', + }, + { + name: 'Private', + value: 'private', + }, + { + name: 'Publish', + value: 'publish', + }, + ], + default: 'publish', + description: 'A named status for the product.', + }, + { + displayName: 'Stock Quantity', + name: 'stockQuantity', + type: 'number', + default: 1, + description: 'Stock quantity.', + }, + { + displayName: 'Stock Status', + name: 'stockStatus', + type: 'options', + options: [ + { + name: 'In Stock', + value: 'instock', + }, + { + name: 'Out Of Stock', + value: 'outofstock', + }, + { + name: 'On Back Order', + value: 'onbackorder', + }, + ], + default: 'instock', + description: 'Controls the stock status of the product', + }, + { + displayName: 'Tags', + name: 'tags', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getTags', + }, + default: [], + description: 'List of tags', + }, + { + displayName: 'Tax Class', + name: 'taxClass', + type: 'string', + default: '', + description: 'Tax class.', + }, + { + displayName: 'Tax Status', + name: 'taxStatus', + type: 'options', + options: [ + { + name: 'Taxable', + value: 'taxable', + }, + { + name: 'Shipping', + value: 'shipping', + }, + { + name: 'None', + value: 'none', + }, + ], + default: 'taxable', + description: 'Tax status.', + }, + { + displayName: 'Type', + name: 'type', + type: 'options', + options: [ + { + name: 'Simple', + value: 'simple', + }, + { + name: 'Grouped', + value: 'grouped', + }, + { + name: 'External', + value: 'external', + }, + { + name: 'Variable', + value: 'variable', + }, + ], + default: 'simple', + description: 'Product type', + }, + { + displayName: 'Upsell IDs', + name: 'upsellIds', + type: 'string', + default: '', + description: 'List of up-sell products IDs. Multiple can be added separated by ,', + }, + { + displayName: 'Virtual', + name: 'virtual', + type: 'boolean', + default: false, + description: 'If the product is virtual.', + }, + { + displayName: 'Weight', + name: 'weight', + type: 'string', + default: '', + description: 'Product weight.', + }, + ] + }, + { + displayName: 'Dimensions', + name: 'dimensionsUi', + placeholder: 'Add Dimension', + type: 'fixedCollection', + default: '', + typeOptions: { + multipleValues: false, + }, + displayOptions: { + show: { + resource: [ + 'product', + ], + operation: [ + 'create', + ] + }, + }, + description: 'Product dimensions', + options: [ + { + name: 'dimensionsValues', + displayName: 'Dimension', + values: [ + { + displayName: 'Height', + name: 'height', + type: 'string', + default: '', + description: 'Product height.', + }, + { + displayName: 'Length', + name: 'length', + type: 'string', + default: '', + description: 'Product length.', + }, + { + displayName: 'Width', + name: 'width', + type: 'string', + default: '', + description: 'Product width.', + }, + ], + }, + ], + }, + { + displayName: 'Images', + name: 'imagesUi', + placeholder: 'Add Image', + type: 'fixedCollection', + default: '', + typeOptions: { + multipleValues: true, + }, + displayOptions: { + show: { + resource: [ + 'product', + ], + operation: [ + 'create', + ] + }, + }, + description: 'Product Image', + options: [ + { + name: 'imagesValues', + displayName: 'Image', + values: [ + { + displayName: 'Alt', + name: 'alt', + type: 'string', + default: '', + description: 'Image alternative text.', + }, + { + displayName: 'Src', + name: 'src', + type: 'string', + default: '', + description: 'Image URL.', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + description: 'Image name.', + }, + ], + }, + ], + }, + { + displayName: 'Metadata', + name: 'metadataUi', + placeholder: 'Add Metadata', + type: 'fixedCollection', + default: '', + typeOptions: { + multipleValues: true, + }, + displayOptions: { + show: { + resource: [ + 'product', + ], + operation: [ + 'create', + ] + }, + }, + description: 'Meta data', + options: [ + { + name: 'metadataValues', + displayName: 'Metadata', + values: [ + { + displayName: 'Key', + name: 'key', + type: 'string', + default: '', + description: 'Name of the metadata key to add.', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + description: 'Value to set for the metadata key.', + }, + ], + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* product:update */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Product ID', + name: 'productId', + type: 'string', + displayOptions: { + show: { + resource: [ + 'product', + ], + operation: [ + 'update', + ], + }, + }, + default: '', + description: 'Product ID.', + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'product', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Backorders', + name: 'backorders', + type: 'options', + options: [ + { + name: 'No', + value: 'no', + }, + { + name: 'Notify', + value: 'notify', + }, + { + name: 'Yes', + value: 'yes', + }, + ], + default: 'no', + description: 'If managing stock, this controls if backorders are allowed', + }, + { + displayName: 'Button Text', + name: 'buttonText', + type: 'string', + default: '', + description: 'Product external button text. Only for external products.', + }, + { + displayName: 'Catalog Visibility', + name: 'catalogVisibility', + type: 'options', + options: [ + { + name: 'Visible', + value: 'visible', + }, + { + name: 'Catalog', + value: 'catalog', + }, + { + name: 'Search', + value: 'search', + }, + { + name: 'Hidden', + value: 'hidden', + }, + ], + default: 'visible', + description: 'Catalog visibility.', + }, + { + displayName: 'Categories', + name: 'categories', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getCategories', + }, + default: [], + description: 'List of categories', + }, + { + displayName: 'Cross Sell IDs', + name: 'crossSellIds', + type: 'string', + default: '', + description: 'List of cross-sell products IDs. Multiple can be added separated by ,', + }, + { + displayName: 'Date On Sale From', + name: 'dateOnSaleFrom', + type: 'dateTime', + default: '', + description: `Start date of sale price, in the site's timezone.`, + }, + { + displayName: 'Date On Sale To', + name: 'dateOnSaleTo', + type: 'dateTime', + default: '', + description: `Ennd date of sale price, in the site's timezone.`, + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + description: 'Product description.', + }, + { + displayName: 'Downloadable', + name: 'downloadable', + type: 'boolean', + default: false, + description: 'if the product is downloadable.', + }, + { + displayName: 'External URL', + name: 'externalUrl', + type: 'string', + default: '', + description: 'Product external URL. Only for external products.', + }, + { + displayName: 'Featured', + name: 'featured', + type: 'boolean', + default: false, + description: 'Featured product', + }, + { + displayName: 'Manage Stock', + name: 'manageStock', + type: 'boolean', + default: false, + description: 'Stock management at product level', + }, + { + displayName: 'Menu Order', + name: 'menuOrder', + type: 'number', + default: 1, + description: 'Menu order, used to custom sort products.', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + description: 'Product name.', + }, + { + displayName: 'Parent ID', + name: 'parentId', + type: 'string', + default: '', + description: 'Product parent ID.', + }, + { + displayName: 'Price', + name: 'price', + type: 'string', + default: '', + description: 'Current product price.', + }, + { + displayName: 'Purchase Note', + name: 'purchaseNote', + type: 'string', + default: '', + description: 'Optional note to send the customer after purchase.', + }, + { + displayName: 'Regular Price', + name: 'regularPrice', + type: 'string', + default: '', + description: 'Product regular price.', + }, + { + displayName: 'Reviews Allowed', + name: 'reviewsAllowed', + type: 'boolean', + default: true, + description: 'Allow reviews.', + }, + { + displayName: 'Sale Price', + name: 'salePrice', + type: 'string', + default: '', + description: 'Product sale price.', + }, + { + displayName: 'Shipping Class', + name: 'shippingClass', + type: 'string', + default: '', + description: 'Shipping class slug.', + }, + { + displayName: 'Short Description', + name: 'shortDescription', + type: 'string', + default: '', + description: 'Product short description.', + }, + { + displayName: 'SKU', + name: 'sku', + type: 'string', + default: '', + description: 'Unique identifier.', + }, + { + displayName: 'Slug', + name: 'slug', + type: 'string', + default: '', + description: 'Product slug.', + }, + { + displayName: 'Sold Individually', + name: 'soldIndividually', + type: 'boolean', + default: false, + description: 'Allow one item to be bought in a single order.', + }, + { + displayName: 'Status', + name: 'status', + type: 'options', + options: [ + { + name: 'Draft', + value: 'draft', + }, + { + name: 'Pending', + value: 'pending', + }, + { + name: 'Private', + value: 'private', + }, + { + name: 'Publish', + value: 'publish', + }, + ], + default: 'publish', + description: 'A named status for the product.', + }, + { + displayName: 'Stock Quantity', + name: 'stockQuantity', + type: 'number', + default: 1, + description: 'Stock quantity.', + }, + { + displayName: 'Stock Status', + name: 'stockStatus', + type: 'options', + options: [ + { + name: 'In Stock', + value: 'instock', + }, + { + name: 'Out Of Stock', + value: 'outofstock', + }, + { + name: 'On Back Order', + value: 'onbackorder', + }, + ], + default: 'instock', + description: 'Controls the stock status of the product', + }, + { + displayName: 'Tags', + name: 'tags', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getTags', + }, + default: [], + description: 'List of tags', + }, + { + displayName: 'Tax Class', + name: 'taxClass', + type: 'string', + default: '', + description: 'Tax class.', + }, + { + displayName: 'Tax Status', + name: 'taxStatus', + type: 'options', + options: [ + { + name: 'Taxable', + value: 'taxable', + }, + { + name: 'Shipping', + value: 'shipping', + }, + { + name: 'None', + value: 'none', + }, + ], + default: 'taxable', + description: 'Tax status.', + }, + { + displayName: 'Type', + name: 'type', + type: 'options', + options: [ + { + name: 'Simple', + value: 'simple', + }, + { + name: 'Grouped', + value: 'grouped', + }, + { + name: 'External', + value: 'external', + }, + { + name: 'Variable', + value: 'variable', + }, + ], + default: 'simple', + description: 'Product type', + }, + { + displayName: 'Upsell IDs', + name: 'upsellIds', + type: 'string', + default: '', + description: 'List of up-sell products IDs. Multiple can be added separated by ,', + }, + { + displayName: 'Virtual', + name: 'virtual', + type: 'boolean', + default: false, + description: 'If the product is virtual.', + }, + { + displayName: 'Weight', + name: 'weight', + type: 'string', + default: '', + description: 'Product weight.', + }, + ] + }, + { + displayName: 'Dimensions', + name: 'dimensionsUi', + placeholder: 'Add Dimension', + type: 'fixedCollection', + default: '', + typeOptions: { + multipleValues: false, + }, + displayOptions: { + show: { + resource: [ + 'product', + ], + operation: [ + 'update', + ] + }, + }, + description: 'Product dimensions', + options: [ + { + name: 'dimensionsValues', + displayName: 'Dimension', + values: [ + { + displayName: 'Height', + name: 'height', + type: 'string', + default: '', + description: 'Product height.', + }, + { + displayName: 'Length', + name: 'length', + type: 'string', + default: '', + description: 'Product length.', + }, + { + displayName: 'Width', + name: 'width', + type: 'string', + default: '', + description: 'Product width.', + }, + ], + }, + ], + }, + { + displayName: 'Images', + name: 'imagesUi', + placeholder: 'Add Image', + type: 'fixedCollection', + default: '', + typeOptions: { + multipleValues: true, + }, + displayOptions: { + show: { + resource: [ + 'product', + ], + operation: [ + 'update', + ] + }, + }, + description: 'Product Image', + options: [ + { + name: 'imagesValues', + displayName: 'Image', + values: [ + { + displayName: 'Alt', + name: 'alt', + type: 'string', + default: '', + description: 'Image alternative text.', + }, + { + displayName: 'Src', + name: 'src', + type: 'string', + default: '', + description: 'Image URL.', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + description: 'Image name.', + }, + ], + }, + ], + }, + { + displayName: 'Metadata', + name: 'metadataUi', + placeholder: 'Add Metadata', + type: 'fixedCollection', + default: '', + typeOptions: { + multipleValues: true, + }, + displayOptions: { + show: { + resource: [ + 'product', + ], + operation: [ + 'update', + ] + }, + }, + description: 'Meta data', + options: [ + { + name: 'metadataValues', + displayName: 'Metadata', + values: [ + { + displayName: 'Key', + name: 'key', + type: 'string', + default: '', + description: 'Name of the metadata key to add.', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + description: 'Value to set for the metadata key.', + }, + ], + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* product:get */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Product ID', + name: 'productId', + type: 'string', + displayOptions: { + show: { + resource: [ + 'product', + ], + operation: [ + 'get', + ], + }, + }, + default: '', + description: 'Product ID.', + }, +/* -------------------------------------------------------------------------- */ +/* product:getAll */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'product', + ], + operation: [ + 'getAll', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: [ + 'product', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 100, + }, + default: 50, + description: 'How many results to return.', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + resource: [ + 'product', + ], + operation: [ + 'getAll', + ], + }, + }, + options: [ + { + displayName: 'After', + name: 'after', + type: 'dateTime', + default: '', + description: 'Limit response to resources published after a given ISO8601 compliant date.', + }, + { + displayName: 'Before', + name: 'before', + type: 'dateTime', + default: '', + description: 'Limit response to resources published before a given ISO8601 compliant date', + }, + { + displayName: 'Category', + name: 'category', + type: 'options', + default: '', + typeOptions: { + loadOptionsMethod: 'getCategories', + }, + description: 'Limit result set to products assigned a specific category ID.', + }, + { + displayName: 'Context', + name: 'context', + type: 'options', + options: [ + { + name: 'View', + value: 'view', + }, + { + name: 'Embed', + value: 'embed', + }, + { + name: 'Edit', + value: 'edit', + }, + ], + default: 'view', + description: 'Scope under which the request is made; determines fields present in response.', + }, + { + displayName: 'Featured', + name: 'featured', + type: 'boolean', + default: false, + description: 'Limit result set to featured products.', + }, + { + displayName: 'Max Price', + name: 'maxPrice', + type: 'string', + default: '', + description: 'Limit result set to products based on a maximun price.', + }, + { + displayName: 'Min Price', + name: 'minPrice', + type: 'string', + default: '', + description: 'Limit result set to products based on a minimum price.', + }, + { + displayName: 'On Sale', + name: 'onSale', + type: 'boolean', + default: false, + description: 'Limit result set to products on sale.', + }, + { + displayName: 'Order', + name: 'order', + type: 'options', + options: [ + { + name: 'ASC', + value: 'asc', + }, + { + name: 'DESC', + value: 'desc', + }, + ], + default: 'desc', + description: 'Order sort attribute ascending or descending.', + }, + { + displayName: 'Order By', + name: 'orderBy', + type: 'options', + options: [ + { + name: 'Date', + value: 'date', + }, + { + name: 'ID', + value: 'id', + }, + { + name: 'Include', + value: 'include', + }, + { + name: 'Slug', + value: 'slug', + }, + { + name: 'Title', + value: 'title', + }, + ], + default: 'id', + description: 'Sort collection by object attribute.', + }, + { + displayName: 'Search', + name: 'search', + type: 'string', + default: '', + description: 'Limit results to those matching a string.', + }, + { + displayName: 'SKU', + name: 'sku', + type: 'string', + default: '', + description: 'Limit result set to products with a specific SKU.', + }, + { + displayName: 'Slug', + name: 'slug', + type: 'string', + default: '', + description: 'Limit result set to products with a specific slug.', + }, + { + displayName: 'Status', + name: 'status', + type: 'options', + options: [ + { + name: 'Draft', + value: 'draft', + }, + { + name: 'Pending', + value: 'pending', + }, + { + name: 'Private', + value: 'private', + }, + { + name: 'Publish', + value: 'publish', + }, + { + name: 'Any', + value: 'any', + }, + ], + default: 'any', + description: 'Limit result set to products assigned a specific status.', + }, + { + displayName: 'Stock Status', + name: 'stockStatus', + type: 'options', + options: [ + { + name: 'In Stock', + value: 'instock', + }, + { + name: 'Out Of Stock', + value: 'outofstock', + }, + { + name: 'On Back Order', + value: 'onbackorder', + }, + ], + default: '', + description: 'Controls the stock status of the product', + }, + { + displayName: 'Tag', + name: 'tag', + type: 'options', + default: [], + typeOptions: { + loadOptionsMethod: 'getTags', + }, + description: 'Limit result set to products assigned a specific tag ID.', + }, + { + displayName: 'Tax Class', + name: 'taxClass', + type: 'options', + options: [ + { + name: 'Standar', + value: 'standard', + }, + { + name: 'Reduced Rate', + value: 'reduced-rate', + }, + { + name: 'Zero Rate', + value: 'zero-rate.', + }, + ], + default: '', + description: 'Limit result set to products with a specific tax class', + }, + { + displayName: 'Type', + name: 'type', + type: 'options', + options: [ + { + name: 'Simple', + value: 'simple', + }, + { + name: 'Grouped', + value: 'grouped', + }, + { + name: 'External', + value: 'external', + }, + { + name: 'Variable', + value: 'variable', + }, + ], + default: 'simple', + description: 'Product type', + }, + ] + }, +/* -------------------------------------------------------------------------- */ +/* product:delete */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Product ID', + name: 'productId', + type: 'string', + displayOptions: { + show: { + resource: [ + 'product', + ], + operation: [ + 'delete', + ], + }, + }, + default: '', + description: 'Product ID.', + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/WooCommerce/ProductInterface.ts b/packages/nodes-base/nodes/WooCommerce/ProductInterface.ts new file mode 100644 index 0000000000..c6a40a1af8 --- /dev/null +++ b/packages/nodes-base/nodes/WooCommerce/ProductInterface.ts @@ -0,0 +1,54 @@ +import { IDataObject } from "n8n-workflow"; + +export interface IDimension { + height?: string; + length?: string; + width?: string; +} + +export interface IImage { + alt?: string; + name?: string; + src?: string; +} + +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; +} diff --git a/packages/nodes-base/nodes/WooCommerce/WooCommerce.node.ts b/packages/nodes-base/nodes/WooCommerce/WooCommerce.node.ts new file mode 100644 index 0000000000..45104a51a2 --- /dev/null +++ b/packages/nodes-base/nodes/WooCommerce/WooCommerce.node.ts @@ -0,0 +1,435 @@ +import { + IExecuteFunctions, +} from 'n8n-core'; +import { + IDataObject, + ILoadOptionsFunctions, + INodeTypeDescription, + INodeExecutionData, + INodeType, + INodePropertyOptions, +} from 'n8n-workflow'; +import { + woocommerceApiRequest, + woocommerceApiRequestAllItems, +} from './GenericFunctions'; +import { + productFields, + productOperations, +} from './ProductDescription'; +import { + IProduct, IImage, IDimension, +} from './ProductInterface'; + +export class WooCommerce implements INodeType { + description: INodeTypeDescription = { + displayName: 'WooCommerce', + name: 'wooCommerce', + icon: 'file:woocommerce.png', + group: ['output'], + version: 1, + subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', + description: 'Consume WooCommerce API', + defaults: { + name: 'WooCommerce', + color: '#96588a', + }, + inputs: ['main'], + outputs: ['main'], + credentials: [ + { + name: 'wooCommerceApi', + required: true, + } + ], + properties: [ + { + displayName: 'Resource', + name: 'resource', + type: 'options', + options: [ + { + name: 'Product', + value: 'product', + }, + ], + default: 'product', + description: 'Resource to consume.', + }, + ...productOperations, + ...productFields, + ], + }; + + methods = { + loadOptions: { + // Get all the available categories to display them to user so that he can + // select them easily + async getCategories(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const categories = await woocommerceApiRequestAllItems.call(this, 'GET', '/products/categories', {}); + for (const category of categories) { + const categoryName = category.name; + const categoryId = category.id; + returnData.push({ + name: categoryName, + value: categoryId, + }); + } + return returnData; + }, + // Get all the available tags to display them to user so that he can + // select them easily + async getTags(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const tags = await woocommerceApiRequestAllItems.call(this, 'GET', '/products/tags', {}); + for (const tag of tags) { + const tagName = tag.name; + const tagId = tag.id; + returnData.push({ + name: tagName, + value: tagId, + }); + } + return returnData; + }, + }, + }; + + async execute(this: IExecuteFunctions): Promise { + const items = this.getInputData(); + const returnData: IDataObject[] = []; + const length = items.length as unknown as number; + let responseData; + const qs: IDataObject = {}; + const resource = this.getNodeParameter('resource', 0) as string; + const operation = this.getNodeParameter('operation', 0) as string; + + for (let i = 0; i < length; i++) { + 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; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + 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; + } + 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.price) { + body.price = additionalFields.price 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; + } + const dimension = (this.getNodeParameter('dimensionsUi', i) as IDataObject).dimensionsValues as IDimension; + if (dimension) { + body.dimensions = dimension; + } + const metadata = (this.getNodeParameter('metadataUi', i) as IDataObject).metadataValues as IDataObject[]; + if (metadata) { + body.meta_data = metadata; + } + responseData = await woocommerceApiRequest.call(this, 'POST', '/products', body); + } + //https://woocommerce.github.io/woocommerce-rest-api-docs/#update-a-product + if (operation === 'update') { + 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.price) { + body.price = updateFields.price 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; + } + const images = (this.getNodeParameter('imagesUi', i) as IDataObject).imagesValues as IImage[]; + if (images) { + body.images = images; + } + const dimension = (this.getNodeParameter('dimensionsUi', i) as IDataObject).dimensionsValues as IDimension; + if (dimension) { + body.dimensions = dimension; + } + const metadata = (this.getNodeParameter('metadataUi', i) as IDataObject).metadataValues as IDataObject[]; + if (metadata) { + body.meta_data = metadata; + } + responseData = await woocommerceApiRequest.call(this, 'PUT', `/products/${productId}`, body); + } + //https://woocommerce.github.io/woocommerce-rest-api-docs/#retrieve-a-product + if (operation === 'get') { + const productId = this.getNodeParameter('productId', i) as string; + responseData = await woocommerceApiRequest.call(this,'GET', `/products/${productId}`, {}, qs); + } + //https://woocommerce.github.io/woocommerce-rest-api-docs/#list-all-products + if (operation === 'getAll') { + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const options = this.getNodeParameter('options', i) as IDataObject; + if (options.after) { + qs.after = options.after as string; + } + if (options.before) { + qs.before = options.before as string; + } + if (options.category) { + qs.category = options.category as string; + } + if (options.context) { + qs.context = options.context as string; + } + if (options.featured) { + qs.featured = options.featured as boolean; + } + if (options.maxPrice) { + qs.max_price = options.maxPrice as string; + } + if (options.minPrice) { + qs.max_price = options.minPrice as string; + } + if (options.onSale) { + qs.on_sale = options.onSale as string; + } + if (options.order) { + qs.order = options.order as string; + } + if (options.orderBy) { + qs.orderby = options.orderBy as string; + } + if (options.search) { + qs.search = options.search as string; + } + if (options.sku) { + qs.sku = options.sku as string; + } + if (options.slug) { + qs.slug = options.slug as string; + } + if (options.status) { + qs.status = options.status as string; + } + if (options.stockStatus) { + qs.stock_status = options.stockStatus as string; + } + if (options.tag) { + qs.tag = options.tag as string; + } + if (options.taxClass) { + qs.tax_class = options.taxClass as string; + } + if (options.type) { + qs.type = options.type as string; + } + if (returnAll === true) { + responseData = await woocommerceApiRequestAllItems.call(this, 'GET', '/products', {}, qs); + } else { + qs.per_page = this.getNodeParameter('limit', i) as number; + responseData = await woocommerceApiRequest.call(this, 'GET', '/products', {}, qs); + } + } + //https://woocommerce.github.io/woocommerce-rest-api-docs/#delete-a-product + if (operation === 'delete') { + const productId = this.getNodeParameter('productId', i) as string; + responseData = await woocommerceApiRequest.call(this,'DELETE', `/products/${productId}`, {}, { force: true }); + } + } + if (Array.isArray(responseData)) { + returnData.push.apply(returnData, responseData as IDataObject[]); + } else { + returnData.push(responseData as IDataObject); + } + } + return [this.helpers.returnJsonArray(returnData)]; + } +} diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 150c7e1332..07248b2a26 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -213,6 +213,7 @@ "dist/nodes/Webhook.node.js", "dist/nodes/Wordpress/Wordpress.node.js", "dist/nodes/WooCommerce/WooCommerceTrigger.node.js", + "dist/nodes/WooCommerce/WooCommerce.node.js", "dist/nodes/WriteBinaryFile.node.js", "dist/nodes/Xml.node.js", "dist/nodes/Zendesk/Zendesk.node.js",