forked from hunterashaw/bigcommerce-client
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
176 lines (159 loc) · 7.99 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
const fetch = require('node-fetch')
const querystring = require('querystring')
module.exports = class {
/**
* Construct BigCommerceClient Instance.
* @param {string} hash Store Hash (ex: gha3w9n1at)
* @param {string} token API Token (ex: j3aovsvag3vg88hhyl4qt89q6ag2b6b)
* @param {boolean} debug Enable console output for every request
* @param {number} timeout Max time in millis for timeout (default: 15000)
* @param {number} maxAttempts Number of retry attempts on timeout (default: 3)
*/
constructor(hash, token, debug=false, timeout=15000, maxAttempts=3){
this.base = `https://api.bigcommerce.com/stores/${hash}/`
this.headers = {
'X-Auth-Token':token,
'Accept':'application/json',
'Content-Type':'application/json'
}
this.meta = {}
this.debug = debug
this.status = undefined
this.maxAttempts = maxAttempts
this.timeout = timeout
}
/**
* Performs a GET request to the BigCommerce Management API at the specified endpoint. Throws error w/ http status information if non-200 response is returned.
* @async
* @returns {object} JSON data returned from a 2-- request
* @param {string} endpoint Url endpoint from version onward (example: 'v3/catalog/products')
* @param {object} queries Object w/ keys & string values of each url query parameter (example: {sku:'10205'})
*/
async get(endpoint, queries={}){
const queryString = `${querystring.stringify(queries)}` ? `?${querystring.stringify(queries)}` : "";
const url = `${this.base}${endpoint}` + queryString;
return this.readResponse(url)
}
/**
* Function to perform on each page returned by endpoint. Should accept an array of objects from page.
* @callback eachPage
* @param {object[]} pageContents
*/
/**
* Performs sequential GET requests to the BigCommerce Management API at the specified endpoint. For each page in query it will perform the provided callback, passing an array of objects in page.
* @async
* @returns {null}
* @param {string} endoint Url endpoint from version onward (example: 'v3/catalog/products')
* @param {eachPage} eachPage Callback for each page provided by endpoint
* @param {object} queries Object w/ keys & string values of each url query parameter (example: {sku:'10205'}). Page & limit can be passed to control start & page size.
* @param {number} concurrency Amount of concurrent requests to make. Isn't a
*/
async paginate(endpoint, eachPage, queries={}, concurrency=3){
const page = await this.get(endpoint, queries)
const current = this.meta.pagination.current_page
let total = this.meta.pagination.total_pages
await eachPage(page)
if (this.debug) console.log('CURRENT PAGE:', current, 'TOTAL PAGES:', total)
if (current === total) return
for (let current_page = current + 1; current_page <= total; current_page+=concurrency){
const pages = await Promise.all([ // Produces an array of concurrent request results
...(() => {
const requests = []
for (let count = 0; count < concurrency; count++) {
queries.page = current_page + count
if (queries.page <= total) // Skip requests outside of total pages
requests.push(this.get(endpoint, queries)) // Create one request per concurrency
}
return requests
})()
])
for (const page of pages) await eachPage(page)
if (this.debug) console.log('CURRENT PAGE:', current_page, 'TOTAL PAGES:', total)
}
}
/**
* Performs sequential GET request to the BigCommerce Management API at the specified endpoint. Concatenates results from all pages.
* @async
* @returns {object[]} Concatenated JSON data returned from a 2-- request
* @param {string} endpoint Url endpoint from version onward (example: 'v3/catalog/products')
* @param {object} queries Object w/ keys & string values of each url query parameter (example: {sku:'10205'})
*/
async getAll(endpoint, queries={}){
let result = []
await this.paginate(endpoint, (items)=>{
result = result.concat(items)
}, queries)
return result
}
/**
* Performs a POST request to the BigCommerce Management API at the specified endpoint. Throws error w/ http status information if non-200 response is returned.
* @async
* @returns {object} JSON data returned from a 2-- request
* @param {string} endpoint Url endpoint from version onward (example: 'v3/catalog/products')
* @param {object} body Request body to be serialized and sent to endpoint
*/
async post(endpoint, body={}){
const url = `${this.base}${endpoint}`
return this.readResponse(url, "POST", JSON.stringify(body))
}
/**
* Performs a PUT request to the BigCommerce Management API at the specified endpoint. Throws error w/ http status information if non-200 response is returned.
* @async
* @returns {object} JSON data returned from a 2-- request
* @param {string} endpoint Url endpoint from version onward (example: 'v3/catalog/products')
* @param {object} body Request body to be serialized and sent to endpoint
*/
async put(endpoint, body){
const url = `${this.base}${endpoint}`
return this.readResponse(url, "PUT", JSON.stringify(body))
}
/**
* Performs a DELETE request to the BigCommerce Management API at the specified endpoint. Throws error w/ http status information if non-200 response is returned.
* @async
* @param {string} endpoint Url endpoint from version onward (example: 'v3/catalog/products')
* @param {object} queries Object w/ keys & string values of each url query parameter (example: {sku:'10205'})
*/
async delete(endpoint, queries={}){
const url = `${this.base}${endpoint}?${querystring.stringify(queries)}`
return this.readResponse(url, "DELETE")
}
/**
* Performs sequential DELETE requests to the BigCommerce Management API at the specified endpoint. Will perform a getAll request, then for each ID returned, it will perform a DELETE.
* @async
* @param {string} endpoint Url endpoint from version onward (example: 'v3/catalog/products')
* @param {object} queries Object w/ keys & string values of each url query parameter (example: {sku:'10205'}).
* @param {number} limit Amount of concurrent delete requests that will be performed. If the default setting of 3 errors out, set it to 1.
*/
async deleteAll(endpoint, queries={}, limit=3){
queries.limit = limit
let items = await this.get(endpoint, queries)
while (items.length){
await Promise.all(items.map((item)=>
this.delete(endpoint + '/' + item.id)
))
items = await this.get(endpoint, queries)
}
}
async readResponse(url, method="GET", body=undefined, attempts=0){
if(this.debug) console.log(method, url)
this.status = undefined;
try {
const response = await fetch(url, { method, headers:this.headers, timeout:this.timeout, body });
this.status = response.status;
if(!response.ok) {
if(response.status >= 500 && attempts < this.maxAttempts)
return this.readResponse(url, method, body, attempts + 1)
else
throw new Error(`${response.status} - ${response.statusText}: ${await response.text()}`);
}
const result = await response.text();
if(result.length) {
const body = JSON.parse(result)
this.meta = body.meta
return body.data ? body.data : body;
} else return response.status;
} catch(e) {
throw e;
}
}
}