forked from MONEI/Shopify-api-node
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
156 lines (131 loc) · 4.05 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
'use strict';
const camelCase = require('lodash/camelCase');
const transform = require('lodash/transform');
const defaults = require('lodash/defaults');
const assign = require('lodash/assign');
const EventEmitter = require('events');
const stopcock = require('stopcock');
const path = require('path');
const got = require('got');
const fs = require('fs');
const pkg = require('./package');
/**
* Creates a Shopify instance.
*
* @param {Object} options Configuration options
* @param {String} options.shopName The name of the shop
* @param {String} options.apiKey The API Key
* @param {String} options.password The private app password
* @param {String} options.accessToken The persistent OAuth public app token
* @param {Boolean|Object} [options.autoLimit] Limits the request rate
* @param {Number} [options.timeout] The request timeout
* @constructor
* @public
*/
function Shopify(options) {
if (!(this instanceof Shopify)) return new Shopify(options);
if (
!options ||
!options.shopName ||
!options.accessToken && (!options.apiKey || !options.password) ||
options.accessToken && (options.apiKey || options.password)
) {
throw new Error('Missing or invalid options');
}
EventEmitter.call(this);
this.options = defaults(options, { timeout: 60000 });
//
// API call limits, updated with each request.
//
this.callLimits = {
remaining: undefined,
current: undefined,
max: undefined
};
this.baseUrl = {
auth: !options.accessToken && `${options.apiKey}:${options.password}`,
hostname: !options.shopName.endsWith('.myshopify.com')
? `${options.shopName}.myshopify.com`
: options.shopName,
protocol: 'https:'
};
if (options.autoLimit) {
const conf = transform(options.autoLimit, (result, value, key) => {
if (key === 'calls') key = 'limit';
result[key] = value;
}, { bucketSize: 35 });
this.request = stopcock(this.request, conf);
}
}
Object.setPrototypeOf(Shopify.prototype, EventEmitter.prototype);
/**
* Updates API call limits.
*
* @param {String} header X-Shopify-Shop-Api-Call-Limit header
* @private
*/
Shopify.prototype.updateLimits = function updateLimits(header) {
if (!header) return;
const limits = header.split('/').map(Number);
const callLimits = this.callLimits;
callLimits.remaining = limits[1] - limits[0];
callLimits.current = limits[0];
callLimits.max = limits[1];
this.emit('callLimits', callLimits);
};
/**
* Sends a request to a Shopify API endpoint.
*
* @param {Object} url URL object
* @param {String} method HTTP method
* @param {String} [key] Key name to use for req/res body
* @param {Object} [params] Request body
* @return {Promise}
* @private
*/
Shopify.prototype.request = function request(url, method, key, params) {
const options = assign({
headers: { 'User-Agent': `${pkg.name}/${pkg.version}` },
timeout: this.options.timeout,
json: true,
retries: 0,
method
}, url);
if (this.options.accessToken) {
options.headers['X-Shopify-Access-Token'] = this.options.accessToken;
}
if (params) {
const body = key ? { [key]: params } : params;
options.headers['Content-Type'] = 'application/json';
options.body = body;
}
return got(options).then(res => {
const body = res.body;
this.updateLimits(res.headers['x-shopify-shop-api-call-limit']);
if (key) return body[key];
return body || {};
}, err => {
this.updateLimits(
err.response && err.response.headers['x-shopify-shop-api-call-limit']
);
return Promise.reject(err);
});
};
//
// Require and instantiate the resources lazily.
//
fs.readdirSync(path.join(__dirname, 'resources')).forEach(name => {
const prop = camelCase(name.slice(0, -3));
Object.defineProperty(Shopify.prototype, prop, {
get: function get() {
const resource = require(`./resources/${name}`);
return Object.defineProperty(this, prop, {
value: new resource(this)
})[prop];
},
set: function set(value) {
return Object.defineProperty(this, prop, { value })[prop];
}
});
});
module.exports = Shopify;