chargebee 3.7.0 → 3.8.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -0
- package/cjs/RequestWrapper.js +117 -56
- package/cjs/asyncApiSupport.js +7 -7
- package/cjs/coreCommon.js +4 -4
- package/cjs/createChargebee.js +16 -15
- package/cjs/environment.js +1 -1
- package/cjs/net/ClientInterface.js +2 -2
- package/cjs/net/FetchClient.js +6 -6
- package/cjs/net/NodeClient.js +4 -4
- package/cjs/resources/api_endpoints.js +2064 -270
- package/cjs/util.js +61 -36
- package/esm/RequestWrapper.js +115 -54
- package/esm/asyncApiSupport.js +1 -1
- package/esm/coreCommon.js +2 -2
- package/esm/createChargebee.js +6 -5
- package/esm/environment.js +1 -1
- package/esm/net/ClientInterface.js +1 -1
- package/esm/net/FetchClient.js +1 -1
- package/esm/net/NodeClient.js +1 -1
- package/esm/resources/api_endpoints.js +2064 -270
- package/esm/util.js +59 -36
- package/package.json +1 -1
- package/types/index.d.ts +23 -0
package/cjs/util.js
CHANGED
|
@@ -7,53 +7,50 @@ exports.serialize = serialize;
|
|
|
7
7
|
exports.encodeListParams = encodeListParams;
|
|
8
8
|
exports.getHost = getHost;
|
|
9
9
|
exports.encodeParams = encodeParams;
|
|
10
|
+
exports.log = log;
|
|
11
|
+
exports.generateUUIDv4 = generateUUIDv4;
|
|
10
12
|
const extend = (deep, target, copy) => {
|
|
11
13
|
_extendsFn(deep, target, copy);
|
|
12
14
|
};
|
|
13
15
|
exports.extend = extend;
|
|
14
16
|
const _extendsFn = (...args) => {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if (
|
|
38
|
-
|
|
39
|
-
(typeof copy === 'object' || (copyIsArray = (0, exports.isArray)(copy)))) {
|
|
40
|
-
if (copyIsArray) {
|
|
41
|
-
copyIsArray = false;
|
|
42
|
-
clone = src && (0, exports.isArray)(src) ? src : [];
|
|
43
|
-
}
|
|
44
|
-
else {
|
|
45
|
-
clone = src && typeof src === 'object' ? src : {};
|
|
46
|
-
}
|
|
47
|
-
target[name] = (0, exports.extend)(deep, clone, copy);
|
|
17
|
+
let options, name, src, copy, copyIsArray, clone;
|
|
18
|
+
let target = args[0] || {};
|
|
19
|
+
let i = 1;
|
|
20
|
+
const length = args.length;
|
|
21
|
+
let deep = false;
|
|
22
|
+
if (typeof target === 'boolean') {
|
|
23
|
+
deep = target;
|
|
24
|
+
target = args[1] || {};
|
|
25
|
+
i = 2;
|
|
26
|
+
}
|
|
27
|
+
if (typeof target !== 'object' && typeof target !== 'function') {
|
|
28
|
+
target = {};
|
|
29
|
+
}
|
|
30
|
+
for (; i < length; i++) {
|
|
31
|
+
if ((options = args[i]) !== null && options !== undefined) {
|
|
32
|
+
for (name in options) {
|
|
33
|
+
src = target[name];
|
|
34
|
+
copy = options[name];
|
|
35
|
+
if (target === copy) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (deep && copy && (typeof copy === 'object' || (0, exports.isArray)(copy))) {
|
|
39
|
+
if ((0, exports.isArray)(copy)) {
|
|
40
|
+
clone = (0, exports.isArray)(src) ? src : [];
|
|
48
41
|
}
|
|
49
|
-
else
|
|
50
|
-
|
|
42
|
+
else {
|
|
43
|
+
clone = (0, exports.isObject)(src) ? src : {};
|
|
51
44
|
}
|
|
45
|
+
target[name] = _extendsFn(deep, clone, copy);
|
|
46
|
+
}
|
|
47
|
+
else if (copy !== undefined) {
|
|
48
|
+
target[name] = copy;
|
|
52
49
|
}
|
|
53
50
|
}
|
|
54
51
|
}
|
|
55
|
-
return target;
|
|
56
52
|
}
|
|
53
|
+
return target;
|
|
57
54
|
};
|
|
58
55
|
const isArray = (obj) => {
|
|
59
56
|
return (Array.isArray(obj) ||
|
|
@@ -221,3 +218,31 @@ function encodeParams(paramObj, serialized, scope, index, jsonKeys, level = 0) {
|
|
|
221
218
|
}
|
|
222
219
|
return serialized.join('&').replace(/%20/g, '+');
|
|
223
220
|
}
|
|
221
|
+
function log(env, { level = 'INFO', message = '', context = {}, functionName = '' }) {
|
|
222
|
+
if (!env.enableDebugLogs) {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
const timestamp = new Date().toISOString();
|
|
226
|
+
const service = 'chargebee-node';
|
|
227
|
+
const metaString = Object.entries(context)
|
|
228
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
229
|
+
.join(', ');
|
|
230
|
+
const logLine = `[${timestamp}] [${level.toUpperCase()}] [${service}] ${functionName} - ${message}${metaString ? ` (${metaString})` : ''}`;
|
|
231
|
+
console.debug(logLine);
|
|
232
|
+
}
|
|
233
|
+
const crypto_1 = require("crypto");
|
|
234
|
+
function generateUUIDv4() {
|
|
235
|
+
const bytes = (0, crypto_1.randomBytes)(16);
|
|
236
|
+
// Set version to 4 (UUIDv4)
|
|
237
|
+
bytes[6] = (bytes[6] & 0x0f) | 0x40;
|
|
238
|
+
// Set variant to 10xxxxxx
|
|
239
|
+
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
|
240
|
+
const hex = bytes.toString('hex');
|
|
241
|
+
return [
|
|
242
|
+
hex.slice(0, 8),
|
|
243
|
+
hex.slice(8, 12),
|
|
244
|
+
hex.slice(12, 16),
|
|
245
|
+
hex.slice(16, 20),
|
|
246
|
+
hex.slice(20),
|
|
247
|
+
].join('-');
|
|
248
|
+
}
|
package/esm/RequestWrapper.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { extend, callbackifyPromise, getApiURL, encodeListParams, encodeParams, serialize, getHost, } from './util
|
|
2
|
-
import { handleResponse } from './coreCommon
|
|
1
|
+
import { extend, callbackifyPromise, getApiURL, encodeListParams, encodeParams, serialize, getHost, log, generateUUIDv4 as uuidv4, } from './util';
|
|
2
|
+
import { handleResponse } from './coreCommon';
|
|
3
3
|
import { Buffer } from 'node:buffer';
|
|
4
4
|
export class RequestWrapper {
|
|
5
5
|
constructor(args, apiCall, envArg) {
|
|
@@ -20,72 +20,133 @@ export class RequestWrapper {
|
|
|
20
20
|
}
|
|
21
21
|
return idParam;
|
|
22
22
|
}
|
|
23
|
-
|
|
23
|
+
static parseRetryAfter(retryAfter) {
|
|
24
|
+
if (!retryAfter)
|
|
25
|
+
return null;
|
|
26
|
+
const seconds = parseInt(retryAfter, 10);
|
|
27
|
+
if (!isNaN(seconds)) {
|
|
28
|
+
return seconds * 1000;
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
async request() {
|
|
24
33
|
let env = {};
|
|
25
34
|
extend(true, env, this.envArg);
|
|
35
|
+
const retryConfig = Object.assign({ enabled: false, maxRetries: 3, delayMs: 200, retryOn: [500, 502, 503, 504] }, env.retryConfig);
|
|
26
36
|
const urlIdParam = this.apiCall.hasIdInUrl ? this.args[0] : null;
|
|
27
37
|
let params = this.apiCall.hasIdInUrl
|
|
28
38
|
? this.args[1]
|
|
29
39
|
: this.args[0];
|
|
30
40
|
let headers = this.apiCall.hasIdInUrl ? this.args[2] : this.args[1];
|
|
31
41
|
Object.assign(this.httpHeaders, headers);
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
42
|
+
if (this.apiCall.httpMethod === 'POST' &&
|
|
43
|
+
!this.httpHeaders['chargebee-idempotency-key'] &&
|
|
44
|
+
this.apiCall.options &&
|
|
45
|
+
this.apiCall.options.isIdempotent) {
|
|
46
|
+
this.httpHeaders['chargebee-idempotency-key'] = uuidv4();
|
|
47
|
+
}
|
|
48
|
+
const makeRequest = async (attempt = 0) => {
|
|
49
|
+
let path = getApiURL(env, this.apiCall.urlPrefix, this.apiCall.urlSuffix, urlIdParam);
|
|
50
|
+
if (typeof params === 'undefined' || params === null) {
|
|
51
|
+
params = {};
|
|
40
52
|
}
|
|
53
|
+
if (this.apiCall.httpMethod === 'GET') {
|
|
54
|
+
const queryParam = this.apiCall.isListReq
|
|
55
|
+
? encodeListParams(serialize(params))
|
|
56
|
+
: encodeParams(serialize(params));
|
|
57
|
+
path += '?' + queryParam;
|
|
58
|
+
params = {};
|
|
59
|
+
}
|
|
60
|
+
const jsonKeys = this.apiCall.jsonKeys;
|
|
61
|
+
const data = this.apiCall.isJsonRequest
|
|
62
|
+
? JSON.stringify(params)
|
|
63
|
+
: encodeParams(params, undefined, undefined, undefined, jsonKeys);
|
|
64
|
+
const requestHeaders = Object.assign({}, this.httpHeaders);
|
|
65
|
+
if (data.length) {
|
|
66
|
+
extend(true, requestHeaders, {
|
|
67
|
+
'Content-Length': data.length,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
const contentType = this.apiCall.isJsonRequest
|
|
71
|
+
? 'application/json;charset=UTF-8'
|
|
72
|
+
: 'application/x-www-form-urlencoded; charset=utf-8';
|
|
73
|
+
extend(true, requestHeaders, {
|
|
74
|
+
Authorization: 'Basic ' + Buffer.from(env.apiKey + ':').toString('base64'),
|
|
75
|
+
Accept: 'application/json',
|
|
76
|
+
'Content-Type': contentType,
|
|
77
|
+
'User-Agent': 'Chargebee-NodeJs-Client ' + env.clientVersion,
|
|
78
|
+
'Lang-Version': typeof process === 'undefined' ? '' : process.version,
|
|
79
|
+
});
|
|
80
|
+
if (attempt > 0) {
|
|
81
|
+
requestHeaders['X-CB-Retry-Attempt'] = attempt.toString();
|
|
82
|
+
}
|
|
83
|
+
const resp = await this.envArg.httpClient.makeApiRequest({
|
|
84
|
+
host: getHost(env, this.apiCall.subDomain),
|
|
85
|
+
port: env.port,
|
|
86
|
+
path,
|
|
87
|
+
method: this.apiCall.httpMethod,
|
|
88
|
+
protocol: env.protocol,
|
|
89
|
+
headers: requestHeaders,
|
|
90
|
+
data,
|
|
91
|
+
timeout: env.timeout,
|
|
92
|
+
});
|
|
93
|
+
return new Promise((resolve, reject) => {
|
|
94
|
+
handleResponse((err, response) => {
|
|
95
|
+
if (err)
|
|
96
|
+
return reject(err);
|
|
97
|
+
return resolve(response);
|
|
98
|
+
}, resp);
|
|
99
|
+
});
|
|
100
|
+
};
|
|
101
|
+
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
102
|
+
const withRetry = async (retryCount, startTime) => {
|
|
103
|
+
var _a, _b, _c, _d, _e, _f;
|
|
41
104
|
try {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
105
|
+
return await makeRequest(retryCount);
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
const statusCode = (_d = (_c = (_a = err.statusCode) !== null && _a !== void 0 ? _a : (_b = err.response) === null || _b === void 0 ? void 0 : _b.statusCode) !== null && _c !== void 0 ? _c : err.http_code) !== null && _d !== void 0 ? _d : err.http_status_code;
|
|
109
|
+
const isRateLimitError = statusCode === 429 && retryConfig.enabled;
|
|
110
|
+
if (isRateLimitError) {
|
|
111
|
+
const headers = ((_e = err.response) === null || _e === void 0 ? void 0 : _e.headers) || err.headers || {};
|
|
112
|
+
const retryAfterHeader = (_f = headers['retry-after']) === null || _f === void 0 ? void 0 : _f.toLowerCase();
|
|
113
|
+
const parsedDelay = RequestWrapper.parseRetryAfter(retryAfterHeader);
|
|
114
|
+
if (!parsedDelay) {
|
|
115
|
+
log(env, {
|
|
116
|
+
level: 'ERROR',
|
|
117
|
+
message: `Rate limit error occurred, but no retry-after header found. Retrying in ${retryConfig.delayMs}ms.`,
|
|
118
|
+
});
|
|
119
|
+
throw err;
|
|
120
|
+
}
|
|
121
|
+
log(env, {
|
|
122
|
+
level: 'INFO',
|
|
123
|
+
message: `Rate limit error occurred. Retrying in ${parsedDelay}ms.`,
|
|
124
|
+
});
|
|
125
|
+
await delay(parsedDelay);
|
|
53
126
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
127
|
+
else {
|
|
128
|
+
const canRetry = retryConfig.enabled &&
|
|
129
|
+
retryConfig.retryOn.includes(statusCode) &&
|
|
130
|
+
retryCount < retryConfig.maxRetries;
|
|
131
|
+
if (!canRetry) {
|
|
132
|
+
log(env, {
|
|
133
|
+
level: 'ERROR',
|
|
134
|
+
message: `Request failed after ${retryCount} retries: ${err.message}`,
|
|
135
|
+
});
|
|
136
|
+
throw err;
|
|
137
|
+
}
|
|
138
|
+
let retryDelayMs = retryConfig.delayMs * Math.pow(2, retryCount) +
|
|
139
|
+
Math.random() * 100;
|
|
140
|
+
log(env, {
|
|
141
|
+
level: 'INFO',
|
|
142
|
+
message: `Retrying request [${retryCount + 1}/${retryConfig.maxRetries}] in ${retryDelayMs}ms due to status code ${statusCode}.`,
|
|
61
143
|
});
|
|
144
|
+
await delay(retryDelayMs);
|
|
62
145
|
}
|
|
63
|
-
|
|
64
|
-
? 'application/json;charset=UTF-8'
|
|
65
|
-
: 'application/x-www-form-urlencoded; charset=utf-8';
|
|
66
|
-
extend(true, this.httpHeaders, {
|
|
67
|
-
Authorization: 'Basic ' + Buffer.from(env.apiKey + ':').toString('base64'),
|
|
68
|
-
Accept: 'application/json',
|
|
69
|
-
'Content-Type': contentType,
|
|
70
|
-
'User-Agent': 'Chargebee-NodeJs-Client ' + env.clientVersion,
|
|
71
|
-
'Lang-Version': typeof process === 'undefined' ? '' : process.version,
|
|
72
|
-
});
|
|
73
|
-
const resp = await this.envArg.httpClient.makeApiRequest({
|
|
74
|
-
host: getHost(env, this.apiCall.subDomain),
|
|
75
|
-
port: env.port,
|
|
76
|
-
path,
|
|
77
|
-
method: this.apiCall.httpMethod,
|
|
78
|
-
protocol: env.protocol,
|
|
79
|
-
headers: this.httpHeaders,
|
|
80
|
-
data: data,
|
|
81
|
-
timeout: env.timeout,
|
|
82
|
-
});
|
|
83
|
-
handleResponse(callBackWrapper, resp);
|
|
146
|
+
return await withRetry(retryCount + 1, startTime);
|
|
84
147
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
});
|
|
148
|
+
};
|
|
149
|
+
const promise = withRetry(0, Date.now());
|
|
89
150
|
return callbackifyPromise(promise);
|
|
90
151
|
}
|
|
91
152
|
}
|
package/esm/asyncApiSupport.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isFunction, extend, callbackifyPromise } from './util
|
|
1
|
+
import { isFunction, extend, callbackifyPromise } from './util';
|
|
2
2
|
export const waitForProcessToComplete = (retrieveHandling, env) => {
|
|
3
3
|
if (typeof retrieveHandling == 'undefined' || !isFunction(retrieveHandling)) {
|
|
4
4
|
throw new Error('The handling parameter should be a method.');
|
package/esm/coreCommon.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ChargebeeError } from './chargebeeError
|
|
2
|
-
import { isNotUndefinedNEmpty } from './util
|
|
1
|
+
import { ChargebeeError } from './chargebeeError';
|
|
2
|
+
import { isNotUndefinedNEmpty } from './util';
|
|
3
3
|
const IDEMPOTENCY_REPLAYED_HEADER = 'chargebee-idempotency-replayed';
|
|
4
4
|
export function throwError(callBack, rawError, headers) {
|
|
5
5
|
const error = new ChargebeeError({
|
package/esm/createChargebee.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { RequestWrapper } from './RequestWrapper
|
|
2
|
-
import { Environment } from './environment
|
|
3
|
-
import { Endpoints } from './resources/api_endpoints
|
|
4
|
-
import { extend, sleep } from './util
|
|
5
|
-
import { waitForProcessToComplete } from './asyncApiSupport
|
|
1
|
+
import { RequestWrapper } from './RequestWrapper';
|
|
2
|
+
import { Environment } from './environment';
|
|
3
|
+
import { Endpoints } from './resources/api_endpoints';
|
|
4
|
+
import { extend, sleep } from './util';
|
|
5
|
+
import { waitForProcessToComplete } from './asyncApiSupport';
|
|
6
6
|
export const CreateChargebee = (httpClient) => {
|
|
7
7
|
const Chargebee = function (conf) {
|
|
8
8
|
this._env = Object.assign({}, Environment);
|
|
@@ -78,6 +78,7 @@ export const CreateChargebee = (httpClient) => {
|
|
|
78
78
|
subDomain: metaArr[5],
|
|
79
79
|
isJsonRequest: metaArr[6],
|
|
80
80
|
jsonKeys: metaArr[7],
|
|
81
|
+
options: metaArr[8],
|
|
81
82
|
};
|
|
82
83
|
this[res][apiCall.methodName] = this._createApiFunc(apiCall, this._env);
|
|
83
84
|
}
|
package/esm/environment.js
CHANGED
|
@@ -8,7 +8,7 @@ export const Environment = {
|
|
|
8
8
|
hostSuffix: '.chargebee.com',
|
|
9
9
|
apiPath: '/api/v2',
|
|
10
10
|
timeout: DEFAULT_TIME_OUT,
|
|
11
|
-
clientVersion: 'v3.
|
|
11
|
+
clientVersion: 'v3.8.0-beta.1',
|
|
12
12
|
port: DEFAULT_PORT,
|
|
13
13
|
timemachineWaitInMillis: DEFAULT_TIME_MACHINE_WAIT,
|
|
14
14
|
exportWaitInMillis: DEFAULT_EXPORT_WAIT,
|
package/esm/net/FetchClient.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HttpClient, HttpClientResponse, } from './ClientInterface
|
|
1
|
+
import { HttpClient, HttpClientResponse, } from './ClientInterface';
|
|
2
2
|
export class FetchHttpClient extends HttpClient {
|
|
3
3
|
async makeApiRequest(props) {
|
|
4
4
|
const headers = this._createHeaders(props.headers);
|
package/esm/net/NodeClient.js
CHANGED