├── .babelrc
├── .editorconfig
├── .eslintrc.json
├── .gitignore
├── .npmrc
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── index.d.ts
├── index.mjs
├── jest.config.js
├── package.json
├── renovate.json
└── test.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@babel/preset-env",
5 | {
6 | "targets": {
7 | "node": "8"
8 | }
9 | }
10 | ]
11 | ],
12 | "plugins": [
13 | "@babel/plugin-transform-modules-commonjs"
14 | ]
15 | }
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 | indent_style = space
6 | indent_size = 2
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
14 | [*.{json,yml}]
15 | insert_final_newline = false
16 |
17 | [.babelrc]
18 | insert_final_newline = false
19 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "es6": true
4 | },
5 | "extends": [
6 | "standard",
7 | "prettier",
8 | "plugin:jest/recommended"
9 | ],
10 | "globals": {
11 | "Atomics": "readonly",
12 | "SharedArrayBuffer": "readonly"
13 | },
14 | "parserOptions": {
15 | "ecmaVersion": 2018,
16 | "sourceType": "module"
17 | },
18 | "rules": {
19 | "indent": [
20 | 2,
21 | 2
22 | ]
23 | }
24 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | coverage
3 | run.*
4 | index.js
5 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | package-lock=false
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "8"
4 | - "9"
5 | - "10"
6 | before_script: npm run build
7 | script: npm test
8 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | All notable changes to this project will be documented in this file.
3 |
4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6 |
7 | ## [1.0.1] - 2019-08-02
8 | ### Removed
9 | - Removed `verifySSL` option, since Axios doesn't provide any option to support this feature.
10 |
11 | ## [1.0.0] - 2019-07-29
12 | ### Added
13 | - First stable version.
14 |
15 | ## [0.0.1] - 2019-07-29
16 | ### Added
17 | - Initial version.
18 |
19 | [Unreleased]: https://github.com/woocommerce/woocommerce-rest-api-js-lib/compare/v1.0.1...HEAD
20 | [1.0.1]: https://github.com/woocommerce/woocommerce-rest-api-js-lib/compare/v1.0.0...v1.0.1
21 | [1.0.0]: https://github.com/woocommerce/woocommerce-rest-api-js-lib/compare/v0.0.1...v1.0.0
22 | [0.0.1]: https://github.com/woocommerce/woocommerce-rest-api-js-lib/releases/tag/v0.0.1
23 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 WooCommerce
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WooCommerce REST API - JavaScript Library
2 |
3 | New JavaScript library for WooCommerce REST API, supports CommonJS (CJS) and Embedded System Module (ESM).
4 |
5 | Requests are made with [Axios library](https://github.com/axios/axios) with [support to promises](https://github.com/axios/axios#promises).
6 |
7 | [](http://travis-ci.org/woocommerce/woocommerce-rest-api-js-lib)
8 | [](https://david-dm.org/woocommerce/woocommerce-rest-api-js-lib)
9 | [](https://www.npmjs.com/package/@woocommerce/woocommerce-rest-api)
10 |
11 | ## Installation
12 |
13 | ```
14 | npm install --save @woocommerce/woocommerce-rest-api
15 | ```
16 |
17 | ## Getting started
18 |
19 | Generate API credentials (Consumer Key & Consumer Secret) following this instructions
20 | .
21 |
22 | Check out the WooCommerce API endpoints and data that can be manipulated in .
23 |
24 | ## Setup
25 |
26 | ### ESM example:
27 |
28 | ```js
29 | import WooCommerceRestApi from "@woocommerce/woocommerce-rest-api";
30 |
31 | const api = new WooCommerceRestApi({
32 | url: "http://example.com",
33 | consumerKey: "ck_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
34 | consumerSecret: "cs_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
35 | version: "wc/v3"
36 | });
37 | ```
38 |
39 | ### CJS example:
40 |
41 | ```js
42 | const WooCommerceRestApi = require("@woocommerce/woocommerce-rest-api").default;
43 |
44 | const api = new WooCommerceRestApi({
45 | url: "http://example.com",
46 | consumerKey: "ck_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
47 | consumerSecret: "cs_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
48 | version: "wc/v3"
49 | });
50 | ```
51 |
52 | ### Options
53 |
54 | | Option | Type | Required | Description |
55 | |-------------------|-----------|----------|---------------------------------------------------------------------------------------------------------------------|
56 | | `url` | `String` | yes | Your Store URL, example: http://woo.dev/ |
57 | | `consumerKey` | `String` | yes | Your API consumer key |
58 | | `consumerSecret` | `String` | yes | Your API consumer secret |
59 | | `wpAPIPrefix` | `String` | no | Custom WP REST API URL prefix, used to support custom prefixes created with the `rest_url_prefix` filter |
60 | | `version` | `String` | no | API version, default is `v3` |
61 | | `encoding` | `String` | no | Encoding, default is 'utf-8' |
62 | | `queryStringAuth` | `Bool` | no | When `true` and using under HTTPS force Basic Authentication as query string, default is `false` |
63 | | `port` | `string` | no | Provide support for URLs with ports, eg: `8080` |
64 | | `timeout` | `Integer` | no | Define the request timeout |
65 | | `axiosConfig` | `Object` | no | Define the custom [Axios config](https://github.com/axios/axios#request-config), also override this library options |
66 |
67 | ## Methods
68 |
69 | ### GET
70 |
71 | - `.get(endpoint)`
72 | - `.get(endpoint, params)`
73 |
74 | | Params | Type | Description |
75 | |------------|----------|---------------------------------------------------------------|
76 | | `endpoint` | `String` | WooCommerce API endpoint, example: `customers` or `orders/12` |
77 | | `params` | `Object` | Query strings params, example: `{ per_page: 20 }` |
78 |
79 | ### POST
80 |
81 | - `.post(endpoint, data)`
82 | - `.post(endpoint, data, params)`
83 |
84 | | Params | Type | Description |
85 | |------------|----------|-------------------------------------------------------------|
86 | | `endpoint` | `String` | WooCommerce API endpoint, example: `customers` or `orders` |
87 | | `data` | `Object` | JS object to be converted into JSON and sent in the request |
88 | | `params` | `Object` | Query strings params |
89 |
90 | ### PUT
91 |
92 | - `.put(endpoint, data)`
93 | - `.put(endpoint, data, params)`
94 |
95 | | Params | Type | Description |
96 | |------------|----------|-------------------------------------------------------------------|
97 | | `endpoint` | `String` | WooCommerce API endpoint, example: `customers/1` or `orders/1234` |
98 | | `data` | `Object` | JS object to be converted into JSON and sent in the request |
99 | | `params` | `Object` | Query strings params |
100 |
101 | ### DELETE
102 |
103 | - `.delete(endpoint)`
104 | - `.delete(endpoint, params)`
105 |
106 | | Params | Type | Description |
107 | |------------|----------|-----------------------------------------------------------------|
108 | | `endpoint` | `String` | WooCommerce API endpoint, example: `customers/2` or `orders/12` |
109 | | `params` | `Object` | Query strings params, example: `{ force: true }` |
110 |
111 | ### OPTIONS
112 |
113 | - `.options(endpoint)`
114 | - `.options(endpoint, params)`
115 |
116 | | Params | Type | Description |
117 | |------------|----------|-----------------------------------------------------------------|
118 | | `endpoint` | `String` | WooCommerce API endpoint, example: `customers/2` or `orders/12` |
119 | | `params` | `Object` | Query strings params |
120 |
121 | ## Example of use
122 |
123 | ```js
124 | // import WooCommerceRestApi from "@woocommerce/woocommerce-rest-api";
125 | const WooCommerceRestApi = require("@woocommerce/woocommerce-rest-api").default;
126 |
127 | const api = new WooCommerceRestApi({
128 | url: "http://example.com",
129 | consumerKey: "ck_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
130 | consumerSecret: "cs_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
131 | version: "wc/v3"
132 | });
133 |
134 | // List products
135 | api.get("products", {
136 | per_page: 20, // 20 products per page
137 | })
138 | .then((response) => {
139 | // Successful request
140 | console.log("Response Status:", response.status);
141 | console.log("Response Headers:", response.headers);
142 | console.log("Response Data:", response.data);
143 | console.log("Total of pages:", response.headers['x-wp-totalpages']);
144 | console.log("Total of items:", response.headers['x-wp-total']);
145 | })
146 | .catch((error) => {
147 | // Invalid request, for 4xx and 5xx statuses
148 | console.log("Response Status:", error.response.status);
149 | console.log("Response Headers:", error.response.headers);
150 | console.log("Response Data:", error.response.data);
151 | })
152 | .finally(() => {
153 | // Always executed.
154 | });
155 |
156 | // Create a product
157 | api.post("products", {
158 | name: "Premium Quality", // See more in https://woocommerce.github.io/woocommerce-rest-api-docs/#product-properties
159 | type: "simple",
160 | regular_price: "21.99",
161 | })
162 | .then((response) => {
163 | // Successful request
164 | console.log("Response Status:", response.status);
165 | console.log("Response Headers:", response.headers);
166 | console.log("Response Data:", response.data);
167 | })
168 | .catch((error) => {
169 | // Invalid request, for 4xx and 5xx statuses
170 | console.log("Response Status:", error.response.status);
171 | console.log("Response Headers:", error.response.headers);
172 | console.log("Response Data:", error.response.data);
173 | })
174 | .finally(() => {
175 | // Always executed.
176 | });
177 |
178 | // Edit a product
179 | api.put("products/1", {
180 | sale_price: "11.99", // See more in https://woocommerce.github.io/woocommerce-rest-api-docs/#product-properties
181 | })
182 | .then((response) => {
183 | // Successful request
184 | console.log("Response Status:", response.status);
185 | console.log("Response Headers:", response.headers);
186 | console.log("Response Data:", response.data);
187 | })
188 | .catch((error) => {
189 | // Invalid request, for 4xx and 5xx statuses
190 | console.log("Response Status:", error.response.status);
191 | console.log("Response Headers:", error.response.headers);
192 | console.log("Response Data:", error.response.data);
193 | })
194 | .finally(() => {
195 | // Always executed.
196 | });
197 |
198 | // Delete a product
199 | api.delete("products/1", {
200 | force: true, // Forces to delete instead of move to the Trash
201 | })
202 | .then((response) => {
203 | // Successful request
204 | console.log("Response Status:", response.status);
205 | console.log("Response Headers:", response.headers);
206 | console.log("Response Data:", response.data);
207 | })
208 | .catch((error) => {
209 | // Invalid request, for 4xx and 5xx statuses
210 | console.log("Response Status:", error.response.status);
211 | console.log("Response Headers:", error.response.headers);
212 | console.log("Response Data:", error.response.data);
213 | })
214 | .finally(() => {
215 | // Always executed.
216 | });
217 | ```
218 |
219 | ## Changelog
220 |
221 | [See changelog for details](https://github.com/woocommerce/woocommerce-rest-api-js-lib/blob/master/CHANGELOG.md)
222 |
--------------------------------------------------------------------------------
/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as OAuth from 'oauth-1.0a'
2 |
3 | export declare type WooCommerceRestApiVersion =
4 | | 'wc/v3'
5 | | 'wc/v2'
6 | | 'wc/v1'
7 | | 'wc-api/v3'
8 | | 'wc-api/v2'
9 | | 'wc-api/v1'
10 | export declare type WooCommerceRestApiEncoding = 'utf-8' | 'ascii'
11 | export declare type WooCommerceRestApiMethod =
12 | | 'get'
13 | | 'post'
14 | | 'put'
15 | | 'delete'
16 | | 'options'
17 |
18 | export interface IWooCommerceRestApiOptions {
19 | /* Your Store URL, example: http://woo.dev/ */
20 | url: string
21 | /* Your API consumer key */
22 | consumerKey: string
23 | /* Your API consumer secret */
24 | consumerSecret: string
25 | /* Custom WP REST API URL prefix, used to support custom prefixes created with the `rest_url_prefix filter` */
26 | wpAPIPrefix?: string
27 | /* API version, default is `v3` */
28 | version?: WooCommerceRestApiVersion
29 | /* Encoding, default is 'utf-8' */
30 | encoding?: WooCommerceRestApiEncoding
31 | /* When `true` and using under HTTPS force Basic Authentication as query string, default is `false` */
32 | queryStringAuth?: boolean
33 | /* Provide support for URLs with ports, eg: `8080` */
34 | port?: number
35 | /* Define the request timeout */
36 | timeout?: number
37 | /* Define the custom Axios config, also override this library options */
38 | axiosConfig?: any
39 | }
40 |
41 | export interface IWooCommerceRestApiQuery {
42 | [key: string]: string
43 | }
44 |
45 | /**
46 | * WooCommerce REST API wrapper
47 | *
48 | * @param {Object} opt
49 | */
50 | export default class WooCommerceRestApi {
51 | protected classVersion: string
52 | protected url: string
53 | protected consumerKey: string
54 | protected consumerSecret: string
55 | protected wpAPIPrefix: string
56 | protected version: WooCommerceRestApiVersion
57 | protected encoding: WooCommerceRestApiEncoding
58 | protected queryStringAuth: boolean
59 | protected port: number
60 | protected timeout: number
61 | protected axiosConfig: any
62 |
63 | /**
64 | * Class constructor.
65 | *
66 | * @param {Object} opt
67 | */
68 | constructor(opt: IWooCommerceRestApiOptions | WooCommerceRestApi)
69 |
70 | /**
71 | * Set default options
72 | *
73 | * @param {Object} opt
74 | */
75 | private _setDefaultsOptions(opt: IWooCommerceRestApiOptions): void
76 |
77 | /**
78 | * Parse params object.
79 | *
80 | * @param {Object} params
81 | * @param {Object} query
82 | */
83 | private _parseParamsObject(params: any, query: any): IWooCommerceRestApiQuery
84 |
85 | /**
86 | * Normalize query string for oAuth
87 | *
88 | * @param {String} url
89 | * @param {Object} params
90 | *
91 | * @return {String}
92 | */
93 | private _normalizeQueryString(url: string, params: any): string
94 |
95 | /**
96 | * Get URL
97 | *
98 | * @param {String} endpoint
99 | * @param {Object} params
100 | *
101 | * @return {String}
102 | */
103 | private _getUrl(endpoint: string, params: any): string
104 |
105 | /**
106 | * Get OAuth
107 | *
108 | * @return {Object}
109 | */
110 | private _getOAuth(): OAuth
111 |
112 | /**
113 | * Do requests
114 | *
115 | * @param {String} method
116 | * @param {String} endpoint
117 | * @param {Object} data
118 | * @param {Object} params
119 | *
120 | * @return {Object}
121 | */
122 | private _request(
123 | method: WooCommerceRestApiMethod,
124 | endpoint: string,
125 | data: any,
126 | params: any
127 | ): Promise
128 |
129 | /**
130 | * GET requests
131 | *
132 | * @param {String} endpoint
133 | * @param {Object} params
134 | *
135 | * @return {Object}
136 | */
137 | public get(endpoint: string): Promise
138 | public get(endpoint: string, params: any): Promise
139 |
140 | /**
141 | * POST requests
142 | *
143 | * @param {String} endpoint
144 | * @param {Object} data
145 | * @param {Object} params
146 | *
147 | * @return {Object}
148 | */
149 | public post(endpoint: string, data: any): Promise
150 | public post(endpoint: string, data: any, params: any): Promise
151 |
152 | /**
153 | * PUT requests
154 | *
155 | * @param {String} endpoint
156 | * @param {Object} data
157 | * @param {Object} params
158 | *
159 | * @return {Object}
160 | */
161 | public put(endpoint: string, data: any): Promise
162 | public put(endpoint: string, data: any, params: any): Promise
163 |
164 | /**
165 | * DELETE requests
166 | *
167 | * @param {String} endpoint
168 | * @param {Object} params
169 | * @param {Object} params
170 | *
171 | * @return {Object}
172 | */
173 | public delete(endpoint: string): Promise
174 | public delete(endpoint: string, params: any): Promise
175 |
176 | /**
177 | * OPTIONS requests
178 | *
179 | * @param {String} endpoint
180 | * @param {Object} params
181 | *
182 | * @return {Object}
183 | */
184 | public options(endpoint: string): Promise
185 | public options(endpoint: string, params: any): Promise
186 | }
187 |
188 | /**
189 | * Options Exception.
190 | */
191 | export class OptionsException {
192 | public name: 'Options Error'
193 | public message: string
194 |
195 | /**
196 | * Constructor.
197 | *
198 | * @param {String} message
199 | */
200 | constructor(message: string)
201 | }
202 |
--------------------------------------------------------------------------------
/index.mjs:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | import axios from "axios";
4 | import createHmac from "create-hmac";
5 | import OAuth from "oauth-1.0a";
6 | import Url from "url-parse";
7 |
8 | /**
9 | * WooCommerce REST API wrapper
10 | *
11 | * @param {Object} opt
12 | */
13 | export default class WooCommerceRestApi {
14 | /**
15 | * Class constructor.
16 | *
17 | * @param {Object} opt
18 | */
19 | constructor(opt) {
20 | if (!(this instanceof WooCommerceRestApi)) {
21 | return new WooCommerceRestApi(opt);
22 | }
23 |
24 | opt = opt || {};
25 |
26 | if (!opt.url) {
27 | throw new OptionsException("url is required");
28 | }
29 |
30 | if (!opt.consumerKey) {
31 | throw new OptionsException("consumerKey is required");
32 | }
33 |
34 | if (!opt.consumerSecret) {
35 | throw new OptionsException("consumerSecret is required");
36 | }
37 |
38 | this.classVersion = "1.0.1";
39 | this._setDefaultsOptions(opt);
40 | }
41 |
42 | /**
43 | * Set default options
44 | *
45 | * @param {Object} opt
46 | */
47 | _setDefaultsOptions(opt) {
48 | this.url = opt.url;
49 | this.wpAPIPrefix = opt.wpAPIPrefix || "wp-json";
50 | this.version = opt.version || "wc/v3";
51 | this.isHttps = /^https/i.test(this.url);
52 | this.consumerKey = opt.consumerKey;
53 | this.consumerSecret = opt.consumerSecret;
54 | this.encoding = opt.encoding || "utf8";
55 | this.queryStringAuth = opt.queryStringAuth || false;
56 | this.port = opt.port || "";
57 | this.timeout = opt.timeout;
58 | this.axiosConfig = opt.axiosConfig || {};
59 | }
60 |
61 | /**
62 | * Parse params object.
63 | *
64 | * @param {Object} params
65 | * @param {Object} query
66 | */
67 | _parseParamsObject(params, query) {
68 | for (const key in params) {
69 | const value = params[key];
70 |
71 | if (typeof value === "object") {
72 | for (const prop in value) {
73 | const itemKey = key.toString() + "[" + prop.toString() + "]";
74 | query[itemKey] = value[prop];
75 | }
76 | } else {
77 | query[key] = value;
78 | }
79 | }
80 |
81 | return query;
82 | }
83 |
84 | /**
85 | * Normalize query string for oAuth
86 | *
87 | * @param {String} url
88 | * @param {Object} params
89 | *
90 | * @return {String}
91 | */
92 | _normalizeQueryString(url, params) {
93 | // Exit if don't find query string.
94 | if (url.indexOf("?") === -1 && Object.keys(params).length === 0) {
95 | return url;
96 | }
97 |
98 | const query = new Url(url, null, true).query;
99 | const values = [];
100 |
101 | let queryString = "";
102 |
103 | // Include params object into URL.searchParams.
104 | this._parseParamsObject(params, query);
105 |
106 | for (const key in query) {
107 | values.push(key);
108 | }
109 | values.sort();
110 |
111 | for (const i in values) {
112 | if (queryString.length) {
113 | queryString += "&";
114 | }
115 |
116 | queryString += encodeURIComponent(values[i])
117 | .replace(/%5B/g, "[")
118 | .replace(/%5D/g, "]");
119 | queryString += "=";
120 | queryString += encodeURIComponent(query[values[i]]);
121 | }
122 |
123 | return url.split("?")[0] + "?" + queryString;
124 | }
125 |
126 | /**
127 | * Get URL
128 | *
129 | * @param {String} endpoint
130 | * @param {Object} params
131 | *
132 | * @return {String}
133 | */
134 | _getUrl(endpoint, params) {
135 | const api = this.wpAPIPrefix + "/";
136 |
137 | let url = this.url.slice(-1) === "/" ? this.url : this.url + "/";
138 |
139 | url = url + api + this.version + "/" + endpoint;
140 |
141 | // Include port.
142 | if (this.port !== "") {
143 | const hostname = new Url(url).hostname;
144 |
145 | url = url.replace(hostname, hostname + ":" + this.port);
146 | }
147 |
148 | if (!this.isHttps) {
149 | return this._normalizeQueryString(url, params);
150 | }
151 |
152 | return url;
153 | }
154 |
155 | /**
156 | * Get OAuth
157 | *
158 | * @return {Object}
159 | */
160 | _getOAuth() {
161 | const data = {
162 | consumer: {
163 | key: this.consumerKey,
164 | secret: this.consumerSecret
165 | },
166 | signature_method: "HMAC-SHA256",
167 | hash_function: (base, key) => {
168 | return createHmac("sha256", key)
169 | .update(base)
170 | .digest("base64");
171 | }
172 | };
173 |
174 | return new OAuth(data);
175 | }
176 |
177 | /**
178 | * Do requests
179 | *
180 | * @param {String} method
181 | * @param {String} endpoint
182 | * @param {Object} data
183 | * @param {Object} params
184 | *
185 | * @return {Object}
186 | */
187 | _request(method, endpoint, data, params = {}) {
188 | const url = this._getUrl(endpoint, params);
189 |
190 | const headers = {
191 | Accept: "application/json"
192 | };
193 | // only set "User-Agent" in node environment
194 | // the checking method is identical to upstream axios
195 | if (
196 | typeof process !== "undefined" &&
197 | Object.prototype.toString.call(process) === "[object process]"
198 | ) {
199 | headers["User-Agent"] =
200 | "WooCommerce REST API - JS Client/" + this.classVersion;
201 | }
202 |
203 | let options = {
204 | url: url,
205 | method: method,
206 | responseEncoding: this.encoding,
207 | timeout: this.timeout,
208 | responseType: "json",
209 | headers
210 | };
211 |
212 | if (this.isHttps) {
213 | if (this.queryStringAuth) {
214 | options.params = {
215 | consumer_key: this.consumerKey,
216 | consumer_secret: this.consumerSecret
217 | };
218 | } else {
219 | options.auth = {
220 | username: this.consumerKey,
221 | password: this.consumerSecret
222 | };
223 | }
224 |
225 | options.params = { ...options.params, ...params };
226 | } else {
227 | options.params = this._getOAuth().authorize({
228 | url: url,
229 | method: method
230 | });
231 | }
232 |
233 | if (data) {
234 | options.headers["Content-Type"] = "application/json;charset=utf-8";
235 | options.data = JSON.stringify(data);
236 | }
237 |
238 | // Allow set and override Axios options.
239 | options = { ...options, ...this.axiosConfig };
240 |
241 | return axios(options);
242 | }
243 |
244 | /**
245 | * GET requests
246 | *
247 | * @param {String} endpoint
248 | * @param {Object} params
249 | *
250 | * @return {Object}
251 | */
252 | get(endpoint, params = {}) {
253 | return this._request("get", endpoint, null, params);
254 | }
255 |
256 | /**
257 | * POST requests
258 | *
259 | * @param {String} endpoint
260 | * @param {Object} data
261 | * @param {Object} params
262 | *
263 | * @return {Object}
264 | */
265 | post(endpoint, data, params = {}) {
266 | return this._request("post", endpoint, data, params);
267 | }
268 |
269 | /**
270 | * PUT requests
271 | *
272 | * @param {String} endpoint
273 | * @param {Object} data
274 | * @param {Object} params
275 | *
276 | * @return {Object}
277 | */
278 | put(endpoint, data, params = {}) {
279 | return this._request("put", endpoint, data, params);
280 | }
281 |
282 | /**
283 | * DELETE requests
284 | *
285 | * @param {String} endpoint
286 | * @param {Object} params
287 | * @param {Object} params
288 | *
289 | * @return {Object}
290 | */
291 | delete(endpoint, params = {}) {
292 | return this._request("delete", endpoint, null, params);
293 | }
294 |
295 | /**
296 | * OPTIONS requests
297 | *
298 | * @param {String} endpoint
299 | * @param {Object} params
300 | *
301 | * @return {Object}
302 | */
303 | options(endpoint, params = {}) {
304 | return this._request("options", endpoint, null, params);
305 | }
306 | }
307 |
308 | /**
309 | * Options Exception.
310 | */
311 | export class OptionsException {
312 | /**
313 | * Constructor.
314 | *
315 | * @param {String} message
316 | */
317 | constructor(message) {
318 | this.name = "Options Error";
319 | this.message = message;
320 | }
321 | }
322 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | // For a detailed explanation regarding each configuration property, visit:
2 | // https://jestjs.io/docs/en/configuration.html
3 |
4 | module.exports = {
5 | // All imported modules in your tests should be mocked automatically
6 | // automock: false,
7 |
8 | // Stop running tests after `n` failures
9 | // bail: 0,
10 |
11 | // Respect "browser" field in package.json when resolving modules
12 | // browser: false,
13 |
14 | // The directory where Jest should store its cached dependency information
15 | // cacheDirectory: "/tmp/jest_rs",
16 |
17 | // Automatically clear mock calls and instances between every test
18 | // clearMocks: false,
19 |
20 | // Indicates whether the coverage information should be collected while executing the test
21 | // collectCoverage: false,
22 |
23 | // An array of glob patterns indicating a set of files for which coverage information should be collected
24 | // collectCoverageFrom: null,
25 |
26 | // The directory where Jest should output its coverage files
27 | coverageDirectory: "coverage",
28 |
29 | // An array of regexp pattern strings used to skip coverage collection
30 | coveragePathIgnorePatterns: ["/node_modules/"],
31 |
32 | // A list of reporter names that Jest uses when writing coverage reports
33 | // coverageReporters: [
34 | // "json",
35 | // "text",
36 | // "lcov",
37 | // "clover"
38 | // ],
39 |
40 | // An object that configures minimum threshold enforcement for coverage results
41 | // coverageThreshold: null,
42 |
43 | // A path to a custom dependency extractor
44 | // dependencyExtractor: null,
45 |
46 | // Make calling deprecated APIs throw helpful error messages
47 | // errorOnDeprecated: false,
48 |
49 | // Force coverage collection from ignored files using an array of glob patterns
50 | // forceCoverageMatch: [],
51 |
52 | // A path to a module which exports an async function that is triggered once before all test suites
53 | // globalSetup: null,
54 |
55 | // A path to a module which exports an async function that is triggered once after all test suites
56 | // globalTeardown: null,
57 |
58 | // A set of global variables that need to be available in all test environments
59 | // globals: {},
60 |
61 | // An array of directory names to be searched recursively up from the requiring module's location
62 | // moduleDirectories: [
63 | // "node_modules"
64 | // ],
65 |
66 | // An array of file extensions your modules use
67 | moduleFileExtensions: ["js", "mjs"],
68 |
69 | // A map from regular expressions to module names that allow to stub out resources with a single module
70 | moduleNameMapper: {
71 | axios: "axios/dist/node/axios.cjs"
72 | },
73 |
74 | // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
75 | // modulePathIgnorePatterns: [],
76 |
77 | // Activates notifications for test results
78 | // notify: false,
79 |
80 | // An enum that specifies notification mode. Requires { notify: true }
81 | // notifyMode: "failure-change",
82 |
83 | // A preset that is used as a base for Jest's configuration
84 | // preset: null,
85 |
86 | // Run tests from one or more projects
87 | // projects: null,
88 |
89 | // Use this configuration option to add custom reporters to Jest
90 | // reporters: undefined,
91 |
92 | // Automatically reset mock state between every test
93 | // resetMocks: false,
94 |
95 | // Reset the module registry before running each individual test
96 | // resetModules: false,
97 |
98 | // A path to a custom resolver
99 | // resolver: null,
100 |
101 | // Automatically restore mock state between every test
102 | // restoreMocks: false,
103 |
104 | // The root directory that Jest should scan for tests and modules within
105 | // rootDir: null,
106 |
107 | // A list of paths to directories that Jest should use to search for files in
108 | // roots: [
109 | // ""
110 | // ],
111 |
112 | // Allows you to use a custom runner instead of Jest's default test runner
113 | // runner: "jest-runner",
114 |
115 | // The paths to modules that run some code to configure or set up the testing environment before each test
116 | // setupFiles: [],
117 |
118 | // A list of paths to modules that run some code to configure or set up the testing framework before each test
119 | // setupFilesAfterEnv: [],
120 |
121 | // A list of paths to snapshot serializer modules Jest should use for snapshot testing
122 | // snapshotSerializers: [],
123 |
124 | // The test environment that will be used for testing
125 | testEnvironment: "node"
126 |
127 | // Options that will be passed to the testEnvironment
128 | // testEnvironmentOptions: {},
129 |
130 | // Adds a location field to test results
131 | // testLocationInResults: false,
132 |
133 | // The glob patterns Jest uses to detect test files
134 | // testMatch: [
135 | // "**/__tests__/**/*.[jt]s?(x)",
136 | // "**/?(*.)+(spec|test).[tj]s?(x)"
137 | // ],
138 |
139 | // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
140 | // testPathIgnorePatterns: [
141 | // "/node_modules/"
142 | // ],
143 |
144 | // The regexp pattern or array of patterns that Jest uses to detect test files
145 | // testRegex: [],
146 |
147 | // This option allows the use of a custom results processor
148 | // testResultsProcessor: null,
149 |
150 | // This option allows use of a custom test runner
151 | // testRunner: "jasmine2",
152 |
153 | // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href
154 | // testURL: "http://localhost",
155 |
156 | // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout"
157 | // timers: "real",
158 |
159 | // A map from regular expressions to paths to transformers
160 | // transform: null,
161 |
162 | // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
163 | // transformIgnorePatterns: [
164 | // "/node_modules/"
165 | // ],
166 |
167 | // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
168 | // unmockedModulePathPatterns: undefined,
169 |
170 | // Indicates whether each individual test should be reported during the run
171 | // verbose: null,
172 |
173 | // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
174 | // watchPathIgnorePatterns: [],
175 |
176 | // Whether to use watchman for file crawling
177 | // watchman: true,
178 | };
179 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@woocommerce/woocommerce-rest-api",
3 | "version": "1.0.1",
4 | "description": "WooCommerce REST API - JavaScript Library",
5 | "author": "Automattic",
6 | "license": "MIT",
7 | "keywords": [
8 | "wordpress",
9 | "woocommerce",
10 | "rest",
11 | "promise",
12 | "node"
13 | ],
14 | "homepage": "https://github.com/woocommerce/woocommerce-rest-api-js-lib",
15 | "repository": {
16 | "type": "git",
17 | "url": "git+https://github.com/woocommerce/woocommerce-rest-api-js-lib.git"
18 | },
19 | "bugs": {
20 | "url": "https://github.com/woocommerce/woocommerce-rest-api-js-lib/issues"
21 | },
22 | "main": "index",
23 | "types": "index.d.ts",
24 | "files": [
25 | "index.js",
26 | "index.mjs",
27 | "index.d.ts"
28 | ],
29 | "dependencies": {
30 | "axios": "^1.6.0",
31 | "create-hmac": "^1.1.7",
32 | "oauth-1.0a": "^2.2.6",
33 | "url-parse": "^1.4.7"
34 | },
35 | "devDependencies": {
36 | "@babel/cli": "7.6.0",
37 | "@babel/core": "7.6.0",
38 | "@babel/plugin-transform-modules-commonjs": "7.6.0",
39 | "@babel/preset-env": "7.6.0",
40 | "babel-jest": "24.9.0",
41 | "del-cli": "3.0.0",
42 | "eslint": "6.4.0",
43 | "eslint-config-prettier": "6.3.0",
44 | "eslint-config-standard": "14.1.0",
45 | "eslint-plugin-import": "2.18.2",
46 | "eslint-plugin-jest": "22.17.0",
47 | "eslint-plugin-node": "10.0.0",
48 | "eslint-plugin-prettier": "3.1.1",
49 | "eslint-plugin-promise": "4.2.1",
50 | "eslint-plugin-standard": "4.0.1",
51 | "husky": "3.0.5",
52 | "jest": "24.9.0",
53 | "lint-staged": "9.3.0",
54 | "nock": "11.3.5",
55 | "prettier": "1.18.2"
56 | },
57 | "scripts": {
58 | "build": "del index.js && babel index.mjs --out-dir .",
59 | "test": "jest",
60 | "format": "prettier --write \"*.mjs\" \"test.js\"",
61 | "lint": "eslint *.mjs",
62 | "prepare": "npm run build",
63 | "prepublishOnly": "npm test && npm run lint",
64 | "preversion": "npm run lint",
65 | "version": "npm run format && git add -A",
66 | "postversion": "git push && git push --tags"
67 | },
68 | "husky": {
69 | "hooks": {
70 | "pre-commit": "lint-staged"
71 | }
72 | },
73 | "lint-staged": {
74 | "*.{js,mjs}": [
75 | "eslint --fix",
76 | "prettier --write",
77 | "git add"
78 | ]
79 | },
80 | "engines": {
81 | "node": ">=8.0.0"
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "config:base"
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/test.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | import WooCommerceRestApi from "./index";
4 | import nock from "nock";
5 |
6 | describe("#options", () => {
7 | test("wpAPIPrefix should set WP REST API custom path", () => {
8 | const api = new WooCommerceRestApi({
9 | url: "https://test.dev",
10 | consumerKey: "ck_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
11 | consumerSecret: "cs_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
12 | wpAPIPrefix: "wp-rest",
13 | version: "wc/v3"
14 | });
15 |
16 | const endpoint = "products";
17 | const expected = "https://test.dev/wp-rest/wc/v3/" + endpoint;
18 | const url = api._getUrl(endpoint);
19 |
20 | expect(url).toBe(expected);
21 | });
22 | });
23 |
24 | describe("#methods", () => {
25 | const api = new WooCommerceRestApi({
26 | url: "https://test.dev",
27 | consumerKey: "ck_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
28 | consumerSecret: "cs_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
29 | version: "wc/v3"
30 | });
31 |
32 | test("_getUrl should return full endpoint URL", () => {
33 | const endpoint = "products";
34 | const expected = "https://test.dev/wp-json/wc/v3/" + endpoint;
35 | const url = api._getUrl(endpoint);
36 |
37 | expect(url).toBe(expected);
38 | });
39 |
40 | test("_normalizeQueryString should return query string sorted by name", () => {
41 | const url =
42 | "http://test.dev/wp-json/wc/v3/products?filter[q]=Woo+Album&fields=id&filter[limit]=1";
43 | const expected =
44 | "http://test.dev/wp-json/wc/v3/products?fields=id&filter[limit]=1&filter[q]=Woo%20Album";
45 | const normalized = api._normalizeQueryString(url);
46 |
47 | expect(normalized).toBe(expected);
48 | });
49 | });
50 |
51 | describe("#requests", () => {
52 | beforeEach(() => {
53 | nock.cleanAll();
54 | });
55 |
56 | const api = new WooCommerceRestApi({
57 | url: "https://test.dev",
58 | consumerKey: "ck_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
59 | consumerSecret: "cs_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
60 | version: "wc/v3"
61 | });
62 |
63 | test("should return content for basic auth", () => {
64 | expect.assertions(1);
65 |
66 | nock("https://test.dev/wp-json/wc/v3")
67 | .post("/orders", {})
68 | .reply(201, {
69 | ok: true
70 | });
71 |
72 | return api.post("orders", {}).then(response => {
73 | expect(response.status).toBe(201);
74 | });
75 | });
76 |
77 | test("should return content for get requests", () => {
78 | expect.assertions(1);
79 | nock("https://test.dev/wp-json/wc/v3")
80 | .get("/orders")
81 | .reply(200, {
82 | ok: true
83 | });
84 |
85 | return api.get("orders", {}).then(response => {
86 | expect(response.status).toBe(200);
87 | });
88 | });
89 |
90 | test("should return content for put requests", () => {
91 | expect.assertions(1);
92 | nock("https://test.dev/wp-json/wc/v3")
93 | .put("/orders")
94 | .reply(200, {
95 | ok: true
96 | });
97 |
98 | return api.put("orders", {}).then(response => {
99 | expect(response.status).toBe(200);
100 | });
101 | });
102 |
103 | test("should return content for delete requests", () => {
104 | expect.assertions(1);
105 | nock("https://test.dev/wp-json/wc/v3")
106 | .delete("/orders")
107 | .reply(200, {
108 | ok: true
109 | });
110 |
111 | return api.delete("orders", {}).then(response => {
112 | expect(response.status).toBe(200);
113 | });
114 | });
115 |
116 | test("should return content for options requests", () => {
117 | expect.assertions(1);
118 | nock("https://test.dev/wp-json/wc/v3")
119 | .intercept("/orders", "OPTIONS")
120 | .reply(200, {
121 | ok: true
122 | });
123 |
124 | return api.options("orders", {}).then(response => {
125 | expect(response.status).toBe(200);
126 | });
127 | });
128 |
129 | test("should return content for OAuth", () => {
130 | expect.assertions(1);
131 | const oAuth = new WooCommerceRestApi({
132 | url: "http://test.dev",
133 | consumerKey: "ck_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
134 | consumerSecret: "cs_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
135 | version: "wc/v3"
136 | });
137 |
138 | nock("http://test.dev/wp-json/wc/v3")
139 | .filteringPath(/\?.*/, "?params")
140 | .get("/orders?params")
141 | .reply(200, {
142 | ok: true
143 | });
144 |
145 | return oAuth.get("orders", {}).then(response => {
146 | expect(response.status).toBe(200);
147 | });
148 | });
149 | });
150 |
--------------------------------------------------------------------------------