@zcatalyst/transport 0.0.1 → 0.0.2
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/dist-cjs/fetch-handler.js +266 -0
- package/dist-cjs/http-handler.js +402 -0
- package/dist-cjs/index.browser.js +18 -0
- package/dist-cjs/index.js +32 -0
- package/dist-cjs/utils/clonable-stream.js +107 -0
- package/dist-cjs/utils/constants.js +55 -0
- package/dist-cjs/utils/enums.js +22 -0
- package/dist-cjs/utils/errors.js +10 -0
- package/dist-cjs/utils/form-data.js +217 -0
- package/dist-cjs/utils/helpers.js +10 -0
- package/dist-cjs/utils/interfaces.js +2 -0
- package/dist-cjs/utils/request-agent.js +22 -0
- package/dist-cjs/utils/request-timeout.js +14 -0
- package/dist-es/fetch-handler.js +261 -0
- package/dist-es/http-handler.js +361 -0
- package/dist-es/index.browser.js +12 -0
- package/dist-es/index.js +23 -0
- package/dist-es/utils/clonable-stream.js +105 -0
- package/dist-es/utils/constants.js +52 -0
- package/dist-es/utils/enums.js +19 -0
- package/dist-es/utils/errors.js +6 -0
- package/dist-es/utils/form-data.js +213 -0
- package/dist-es/utils/helpers.js +7 -0
- package/dist-es/utils/interfaces.js +1 -0
- package/dist-es/utils/request-agent.js +20 -0
- package/dist-es/utils/request-timeout.js +11 -0
- package/dist-types/fetch-handler.d.ts +40 -0
- package/dist-types/http-handler.d.ts +39 -0
- package/dist-types/index.browser.d.ts +13 -0
- package/dist-types/index.d.ts +16 -0
- package/dist-types/ts3.4/fetch-handler.d.ts +40 -0
- package/dist-types/ts3.4/http-handler.d.ts +39 -0
- package/dist-types/ts3.4/index.browser.d.ts +13 -0
- package/dist-types/ts3.4/index.d.ts +16 -0
- package/dist-types/ts3.4/utils/clonable-stream.d.ts +21 -0
- package/dist-types/ts3.4/utils/constants.d.ts +11 -0
- package/dist-types/ts3.4/utils/enums.d.ts +16 -0
- package/dist-types/ts3.4/utils/errors.d.ts +4 -0
- package/dist-types/ts3.4/utils/form-data.d.ts +44 -0
- package/dist-types/ts3.4/utils/helpers.d.ts +1 -0
- package/dist-types/ts3.4/utils/interfaces.d.ts +64 -0
- package/dist-types/ts3.4/utils/request-agent.d.ts +6 -0
- package/dist-types/ts3.4/utils/request-timeout.d.ts +1 -0
- package/dist-types/utils/clonable-stream.d.ts +21 -0
- package/dist-types/utils/constants.d.ts +11 -0
- package/dist-types/utils/enums.d.ts +16 -0
- package/dist-types/utils/errors.d.ts +4 -0
- package/dist-types/utils/form-data.d.ts +44 -0
- package/dist-types/utils/helpers.d.ts +1 -0
- package/dist-types/utils/interfaces.d.ts +64 -0
- package/dist-types/utils/request-agent.d.ts +6 -0
- package/dist-types/utils/request-timeout.d.ts +1 -0
- package/package.json +10 -5
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
4
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
5
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
6
|
+
};
|
|
7
|
+
var _a, _ResponseHandler_attachAppSpecificHeaders, _ResponseHandler_followZcrfTokenProtocol, _ResponseHandler_followJwtZCAuthProtocol, _ResponseHandler_sendRequest;
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.ResponseHandler = exports.DefaultHttpResponse = exports.keepAliveSupport = void 0;
|
|
10
|
+
const auth_client_1 = require("@zcatalyst/auth-client");
|
|
11
|
+
const utils_1 = require("@zcatalyst/utils");
|
|
12
|
+
const constants_1 = require("./utils/constants");
|
|
13
|
+
const errors_1 = require("./utils/errors");
|
|
14
|
+
const request_timeout_1 = require("./utils/request-timeout");
|
|
15
|
+
const { REQ_METHOD } = utils_1.CONSTANTS;
|
|
16
|
+
exports.keepAliveSupport = {
|
|
17
|
+
supported: undefined
|
|
18
|
+
};
|
|
19
|
+
class DefaultHttpResponse {
|
|
20
|
+
constructor(resp) {
|
|
21
|
+
this.statusCode = resp.statusCode;
|
|
22
|
+
this.headers = resp.headers;
|
|
23
|
+
this.config = resp.config;
|
|
24
|
+
this.resp = resp;
|
|
25
|
+
}
|
|
26
|
+
get data() {
|
|
27
|
+
if (this.resp.data === undefined) {
|
|
28
|
+
throw new errors_1.CatalystAPIError('UNPARSABLE_RESPONSE', `Error while processing response data. Raw server ` +
|
|
29
|
+
`response: "${this.resp.data}". `, '', this.statusCode);
|
|
30
|
+
}
|
|
31
|
+
return this.resp.data;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.DefaultHttpResponse = DefaultHttpResponse;
|
|
35
|
+
class ResponseHandler {
|
|
36
|
+
constructor() { }
|
|
37
|
+
static async fireGeneralRequest({ requestCore, url }, requestOptions = {}) {
|
|
38
|
+
try {
|
|
39
|
+
const headers = requestCore.headers || {};
|
|
40
|
+
const options = {
|
|
41
|
+
method: requestCore.method,
|
|
42
|
+
headers,
|
|
43
|
+
credentials: requestOptions.auth ? 'include' : 'omit',
|
|
44
|
+
body: requestCore.method !== REQ_METHOD.get ? requestCore.body : undefined
|
|
45
|
+
};
|
|
46
|
+
if (requestOptions.auth) {
|
|
47
|
+
options.headers = await _a.attachZCAuthHeaders(headers);
|
|
48
|
+
options.headers = __classPrivateFieldGet(this, _a, "m", _ResponseHandler_attachAppSpecificHeaders).call(this, headers);
|
|
49
|
+
}
|
|
50
|
+
const controller = new AbortController();
|
|
51
|
+
if (requestOptions.abortSignal) {
|
|
52
|
+
requestOptions.abortSignal.addEventListener('abort', () => controller.abort());
|
|
53
|
+
}
|
|
54
|
+
options.signal = controller.signal;
|
|
55
|
+
const response = await Promise.race([
|
|
56
|
+
fetch(url, options),
|
|
57
|
+
(0, request_timeout_1.requestTimeout)(requestOptions?.requestTimeout)
|
|
58
|
+
]);
|
|
59
|
+
if (!response.ok) {
|
|
60
|
+
const errorData = await response.json().catch(() => null);
|
|
61
|
+
throw new errors_1.CatalystAPIError(`HTTP_ERROR_${response.status}`, errorData?.message || response.statusText, errorData, response.status);
|
|
62
|
+
}
|
|
63
|
+
if (requestOptions.retry && requestOptions.retry > 0) {
|
|
64
|
+
try {
|
|
65
|
+
return await _a.wrapResponse(response, {
|
|
66
|
+
...requestOptions,
|
|
67
|
+
request: requestCore
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
requestOptions.retry--;
|
|
72
|
+
return this.fireGeneralRequest({ requestCore, url }, requestOptions);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return await _a.wrapResponse(response, {
|
|
76
|
+
...requestOptions,
|
|
77
|
+
request: requestCore
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
if (error instanceof errors_1.CatalystAPIError) {
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
if (error.name === 'AbortError') {
|
|
85
|
+
throw new errors_1.CatalystAPIError('REQUEST_ABORTED', 'The request was aborted', error);
|
|
86
|
+
}
|
|
87
|
+
if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {
|
|
88
|
+
throw new errors_1.CatalystAPIError('NETWORK_ERROR', 'Network error occurred while making the request', error);
|
|
89
|
+
}
|
|
90
|
+
throw new errors_1.CatalystAPIError('REQUEST_FAILED', error.message || 'Request failed', error, error.statusCode);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
static async wrapResponse(response, options) {
|
|
94
|
+
try {
|
|
95
|
+
let data;
|
|
96
|
+
switch (options?.expecting || "json") {
|
|
97
|
+
case "buffer":
|
|
98
|
+
data = await response.arrayBuffer();
|
|
99
|
+
break;
|
|
100
|
+
case "raw":
|
|
101
|
+
data = await response.blob();
|
|
102
|
+
break;
|
|
103
|
+
case "json":
|
|
104
|
+
data = await response.json();
|
|
105
|
+
break;
|
|
106
|
+
case "string":
|
|
107
|
+
data = await response.text();
|
|
108
|
+
break;
|
|
109
|
+
default:
|
|
110
|
+
throw new errors_1.CatalystAPIError('UNSUPPORTED_RESPONSE_TYPE', `Unsupported response type: ${options?.expecting}`);
|
|
111
|
+
}
|
|
112
|
+
return new DefaultHttpResponse({
|
|
113
|
+
headers: response.headers,
|
|
114
|
+
statusCode: response.status,
|
|
115
|
+
data,
|
|
116
|
+
request: options?.request || {},
|
|
117
|
+
config: options || {}
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
throw new errors_1.CatalystAPIError('RESPONSE_PARSE_ERROR', 'Failed to parse response data', error);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
static async fireRawRequest(requestCore, reqOptions) {
|
|
125
|
+
let headers = requestCore.headers;
|
|
126
|
+
if (reqOptions.auth) {
|
|
127
|
+
headers = await _a.attachZCAuthHeaders(headers);
|
|
128
|
+
headers = __classPrivateFieldGet(this, _a, "m", _ResponseHandler_attachAppSpecificHeaders).call(this, headers);
|
|
129
|
+
headers = { ...headers, credentials: 'include' };
|
|
130
|
+
}
|
|
131
|
+
const options = {
|
|
132
|
+
method: requestCore.method,
|
|
133
|
+
headers
|
|
134
|
+
};
|
|
135
|
+
if (requestCore.method !== REQ_METHOD.get && requestCore.body !== null)
|
|
136
|
+
options.body = requestCore.body;
|
|
137
|
+
const url = this.configManager.APIDomain
|
|
138
|
+
? `${this.configManager.APIDomain}${requestCore.url}`
|
|
139
|
+
: requestCore.url;
|
|
140
|
+
return await this.wrapResponse(await fetch(url, options));
|
|
141
|
+
}
|
|
142
|
+
static attachZCAuthHeaders(headers) {
|
|
143
|
+
switch (this.configManager.AuthProtocol) {
|
|
144
|
+
case auth_client_1.Auth_Protocol.ZcrfTokenProtocol:
|
|
145
|
+
return __classPrivateFieldGet(_a, _a, "m", _ResponseHandler_followZcrfTokenProtocol).call(_a, headers);
|
|
146
|
+
case auth_client_1.Auth_Protocol.JwtTokenProtocol:
|
|
147
|
+
return __classPrivateFieldGet(_a, _a, "m", _ResponseHandler_followJwtZCAuthProtocol).call(_a, headers);
|
|
148
|
+
default:
|
|
149
|
+
return Promise.resolve(headers);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
static getJWTZCAuthToken() {
|
|
153
|
+
const conf = auth_client_1.ConfigManager.getInstance();
|
|
154
|
+
return new Promise((resolve, reject) => {
|
|
155
|
+
const jwtZCAuthToken = (0, utils_1.getToken)();
|
|
156
|
+
if (jwtZCAuthToken === '') {
|
|
157
|
+
reject('Unable to get the JWT Access Token.');
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
resolve({
|
|
161
|
+
access_token: `${conf.jwtAuthTokenPrefix} ${jwtZCAuthToken}`
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
static appendQueryString(url, qs) {
|
|
167
|
+
if (!qs || Object.keys(qs).length === 0) {
|
|
168
|
+
return url;
|
|
169
|
+
}
|
|
170
|
+
const searchParams = new URLSearchParams();
|
|
171
|
+
Object.entries(qs)
|
|
172
|
+
.filter(([_, value]) => value != null)
|
|
173
|
+
.forEach(([key, value]) => searchParams.append(key, String(value)));
|
|
174
|
+
const baseUrl = url.split('?')[0];
|
|
175
|
+
const existingParams = url.split('?')[1] || '';
|
|
176
|
+
return `${baseUrl}?${existingParams ? `${existingParams}&` : ''}${searchParams.toString()}`;
|
|
177
|
+
}
|
|
178
|
+
static async send(options) {
|
|
179
|
+
const headers = options.headers || {};
|
|
180
|
+
let data = options.data;
|
|
181
|
+
if (data !== undefined) {
|
|
182
|
+
switch (options.type) {
|
|
183
|
+
case "json":
|
|
184
|
+
data = JSON.stringify(data);
|
|
185
|
+
headers['Content-Type'] = constants_1.HTTP_HEADER_MAP.CONTENT_JSON;
|
|
186
|
+
break;
|
|
187
|
+
case "file":
|
|
188
|
+
const formData = new FormData();
|
|
189
|
+
const keyData = Object.keys(data);
|
|
190
|
+
keyData.forEach((key) => {
|
|
191
|
+
formData.append(key, data[key]);
|
|
192
|
+
});
|
|
193
|
+
data = formData;
|
|
194
|
+
break;
|
|
195
|
+
case "raw":
|
|
196
|
+
data = JSON.stringify(data);
|
|
197
|
+
if (headers['Content-Type'] === undefined) {
|
|
198
|
+
headers['Content-Type'] = 'application/octet-stream';
|
|
199
|
+
}
|
|
200
|
+
break;
|
|
201
|
+
default:
|
|
202
|
+
data = JSON.stringify(data);
|
|
203
|
+
headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
|
204
|
+
headers['Content-Length'] = Buffer.byteLength(data) + '';
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (this.configManager.APIDomain === null && !options.origin) {
|
|
208
|
+
throw new errors_1.CatalystAPIError('API_REQUEST_ERROR', 'Unable to get the base url');
|
|
209
|
+
}
|
|
210
|
+
if (options.service !== "external" && options.path) {
|
|
211
|
+
options.path = `${(0, utils_1.getServicePath)(options.service)}/project/${this.configManager.ProjectID}${options.path}`;
|
|
212
|
+
}
|
|
213
|
+
const request = {
|
|
214
|
+
url: options.url ?? `${options.origin ?? this.configManager.APIDomain}${options.path}`,
|
|
215
|
+
method: options.method,
|
|
216
|
+
...(data ? { body: data } : {}),
|
|
217
|
+
headers
|
|
218
|
+
};
|
|
219
|
+
request.url = this.appendQueryString(request.url, options.qs);
|
|
220
|
+
return await __classPrivateFieldGet(this, _a, "m", _ResponseHandler_sendRequest).call(this, request, {
|
|
221
|
+
expecting: options.expecting,
|
|
222
|
+
auth: options.auth ?? true
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
exports.ResponseHandler = ResponseHandler;
|
|
227
|
+
_a = ResponseHandler, _ResponseHandler_attachAppSpecificHeaders = function _ResponseHandler_attachAppSpecificHeaders(headers) {
|
|
228
|
+
const normalizedHeaders = headers;
|
|
229
|
+
const currentAccept = normalizedHeaders['Accept'];
|
|
230
|
+
if (!currentAccept) {
|
|
231
|
+
normalizedHeaders['Accept'] = 'application/vnd.catalyst.v2+json';
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
normalizedHeaders['Accept'] = `application/vnd.catalyst.v2+json, ${currentAccept}`;
|
|
235
|
+
}
|
|
236
|
+
if (typeof this.configManager?.OrgId === 'string') {
|
|
237
|
+
normalizedHeaders['CATALYST-ORG'] = this.configManager.OrgId;
|
|
238
|
+
}
|
|
239
|
+
normalizedHeaders['CATALYST-COMPONENT'] = 'true';
|
|
240
|
+
return normalizedHeaders;
|
|
241
|
+
}, _ResponseHandler_followZcrfTokenProtocol = async function _ResponseHandler_followZcrfTokenProtocol(headers) {
|
|
242
|
+
return auth_client_1.zcAuth
|
|
243
|
+
.collectZCRFToken()
|
|
244
|
+
.then(() => {
|
|
245
|
+
headers[constants_1.X_ZCSRF_TOKEN] =
|
|
246
|
+
`${constants_1.ZD_CSRPARAM}=${this.configManager.CsrfToken}`;
|
|
247
|
+
return headers;
|
|
248
|
+
})
|
|
249
|
+
.catch((err) => {
|
|
250
|
+
throw new errors_1.CatalystAPIError('API_ERROR', err.message, err.status);
|
|
251
|
+
});
|
|
252
|
+
}, _ResponseHandler_followJwtZCAuthProtocol = async function _ResponseHandler_followJwtZCAuthProtocol(headers) {
|
|
253
|
+
return this.getJWTZCAuthToken()
|
|
254
|
+
.then((resp) => {
|
|
255
|
+
headers[constants_1.HTTP_HEADER_MAP.AUTHORIZATION_KEY] =
|
|
256
|
+
resp.access_token;
|
|
257
|
+
return headers;
|
|
258
|
+
})
|
|
259
|
+
.catch((err) => {
|
|
260
|
+
throw new errors_1.CatalystAPIError('API_ERROR', err.message, err.status);
|
|
261
|
+
});
|
|
262
|
+
}, _ResponseHandler_sendRequest = async function _ResponseHandler_sendRequest(request, options) {
|
|
263
|
+
const { url, ...requestCore } = request;
|
|
264
|
+
return _a.fireGeneralRequest({ url, requestCore }, options);
|
|
265
|
+
};
|
|
266
|
+
ResponseHandler.configManager = auth_client_1.ConfigManager.getInstance();
|
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.AuthorizedHttpClient = exports.HttpClient = exports.DefaultHttpResponse = void 0;
|
|
40
|
+
const utils_1 = require("@zcatalyst/utils");
|
|
41
|
+
const http_1 = __importDefault(require("http"));
|
|
42
|
+
const https_1 = __importDefault(require("https"));
|
|
43
|
+
const web_1 = require("node:stream/web");
|
|
44
|
+
const querystring_1 = require("querystring");
|
|
45
|
+
const stream_1 = require("stream");
|
|
46
|
+
const url_1 = require("url");
|
|
47
|
+
const util_1 = require("util");
|
|
48
|
+
const package_json_1 = require("../package.json");
|
|
49
|
+
const errors_1 = require("./utils/errors");
|
|
50
|
+
const form_data_1 = __importDefault(require("./utils/form-data"));
|
|
51
|
+
const helpers_1 = require("./utils/helpers");
|
|
52
|
+
const request_agent_1 = __importDefault(require("./utils/request-agent"));
|
|
53
|
+
const { PROJECT_KEY_NAME, IS_LOCAL, ENVIRONMENT_KEY_NAME, ENVIRONMENT, USER_KEY_NAME, CREDENTIAL_USER, CATALYST_ORIGIN, X_ZOHO_CATALYST_ORG_ID, USER_AGENT, APM_INSIGHT, ACCEPT_HEADER, REQ_RETRY_THRESHOLD, PROJECT_HEADER, IS_APM } = utils_1.CONSTANTS;
|
|
54
|
+
class DefaultHttpResponse {
|
|
55
|
+
constructor(resp) {
|
|
56
|
+
this.statusCode = resp.statusCode;
|
|
57
|
+
this.headers = resp.headers;
|
|
58
|
+
this.config = resp.config;
|
|
59
|
+
this.resp = resp;
|
|
60
|
+
}
|
|
61
|
+
get data() {
|
|
62
|
+
switch (this.config.expecting) {
|
|
63
|
+
case "string":
|
|
64
|
+
if (this.resp.data === undefined) {
|
|
65
|
+
throw new errors_1.CatalystAPIError('UNPARSABLE_RESPONSE', `Error while processing response data. Raw server ` +
|
|
66
|
+
`response: "${this.resp.data}". Status code: "${this.statusCode}".`, '', this.statusCode);
|
|
67
|
+
}
|
|
68
|
+
return this.resp.data;
|
|
69
|
+
case "buffer":
|
|
70
|
+
if (this.resp.buffer === undefined) {
|
|
71
|
+
throw new errors_1.CatalystAPIError('UNPARSABLE_RESPONSE', `Error while processing response buffer. Raw server ` +
|
|
72
|
+
`response: "${this.resp.data}". Status code: "${this.statusCode}".`, '', this.statusCode);
|
|
73
|
+
}
|
|
74
|
+
return this.resp.buffer;
|
|
75
|
+
case "raw":
|
|
76
|
+
return this.resp.stream;
|
|
77
|
+
default:
|
|
78
|
+
try {
|
|
79
|
+
return JSON.parse(this.resp.data);
|
|
80
|
+
}
|
|
81
|
+
catch (e) {
|
|
82
|
+
throw new errors_1.CatalystAPIError('UNPARSABLE_RESPONSE', `Error while parsing response data: "${(0, util_1.inspect)(e)}". Raw server ` +
|
|
83
|
+
`response: "${this.resp.data}". Status code: "${this.statusCode}".`, '', this.statusCode);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
exports.DefaultHttpResponse = DefaultHttpResponse;
|
|
89
|
+
function rejectWithContext(reject, statusCode, data) {
|
|
90
|
+
try {
|
|
91
|
+
const catalystError = JSON.parse(data);
|
|
92
|
+
reject({
|
|
93
|
+
statusCode,
|
|
94
|
+
code: catalystError.data.error_code,
|
|
95
|
+
message: catalystError.data.message
|
|
96
|
+
});
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
reject({
|
|
101
|
+
statusCode,
|
|
102
|
+
message: (0, util_1.inspect)(data)
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async function streamToBuffer(stream) {
|
|
107
|
+
const chunks = [];
|
|
108
|
+
return new Promise((resolve, reject) => {
|
|
109
|
+
stream.destroyed && reject('Invalid response stream');
|
|
110
|
+
stream.on('data', (chunk) => {
|
|
111
|
+
chunks.push(chunk);
|
|
112
|
+
});
|
|
113
|
+
stream.on('error', reject);
|
|
114
|
+
stream.on('end', () => resolve(Buffer.concat(chunks)));
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
function constructFormData(data) {
|
|
118
|
+
const formData = new form_data_1.default();
|
|
119
|
+
const keyData = Object.keys(data);
|
|
120
|
+
keyData.forEach((key) => {
|
|
121
|
+
formData.append(key, data[key]);
|
|
122
|
+
});
|
|
123
|
+
return formData;
|
|
124
|
+
}
|
|
125
|
+
async function _finalizeRequest(resolve, reject, response) {
|
|
126
|
+
if (response.statusCode === undefined) {
|
|
127
|
+
reject(new errors_1.CatalystAPIError('UNKNOWN_STATUSCODE', 'unable to obtain status code from response', response));
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
if (response.statusCode >= 200 && response.statusCode < 300) {
|
|
131
|
+
resolve(response);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (response.stream?.pipe === undefined) {
|
|
135
|
+
rejectWithContext(reject, response.statusCode, response.data);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
try {
|
|
139
|
+
if (response.stream !== undefined && response.data === undefined) {
|
|
140
|
+
const responseBuffer = await streamToBuffer(response.stream);
|
|
141
|
+
response.data = responseBuffer.toString();
|
|
142
|
+
}
|
|
143
|
+
if (response.statusCode === 404) {
|
|
144
|
+
rejectWithContext(reject, response.statusCode, response.data || 'Not Found');
|
|
145
|
+
}
|
|
146
|
+
else if (response.statusCode === 403) {
|
|
147
|
+
rejectWithContext(reject, response.statusCode, response.data || 'Access Denied');
|
|
148
|
+
}
|
|
149
|
+
else if (response.statusCode === 401) {
|
|
150
|
+
rejectWithContext(reject, response.statusCode, response.data || 'Unauthorized');
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
rejectWithContext(reject, response.statusCode, response.data || 'Unknown response');
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
catch (e) {
|
|
157
|
+
const errMsg = e instanceof Error ? e.message : (0, util_1.inspect)(e);
|
|
158
|
+
rejectWithContext(reject, response.statusCode, errMsg);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function _appendQueryData(url, data) {
|
|
162
|
+
if (data && Object.keys(data).length > 0) {
|
|
163
|
+
url += url.includes('?') ? '&' : '?';
|
|
164
|
+
url += (0, querystring_1.stringify)(data);
|
|
165
|
+
}
|
|
166
|
+
return url;
|
|
167
|
+
}
|
|
168
|
+
async function _request(transport, options, config, data, retryCount = 0) {
|
|
169
|
+
const clonedData = data === undefined
|
|
170
|
+
? undefined
|
|
171
|
+
: config.type !== "file"
|
|
172
|
+
?
|
|
173
|
+
data
|
|
174
|
+
:
|
|
175
|
+
data.createClone();
|
|
176
|
+
return new Promise(async (resolve, reject) => {
|
|
177
|
+
const retryRequest = async (err) => {
|
|
178
|
+
utils_1.LOGGER.warn('>>> RETRYING REQUEST ');
|
|
179
|
+
if (retryCount++ === REQ_RETRY_THRESHOLD) {
|
|
180
|
+
reject(err);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
try {
|
|
184
|
+
options.agent = new request_agent_1.default((0, helpers_1.isHttps)(config.url), options.hostname, true).agent;
|
|
185
|
+
const resp = await _request(transport, options, config, clonedData, retryCount);
|
|
186
|
+
resolve(resp);
|
|
187
|
+
}
|
|
188
|
+
catch (e) {
|
|
189
|
+
reject(e);
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
const startTimeStamp = Date.now();
|
|
193
|
+
const req = transport.request(options, async (res) => {
|
|
194
|
+
if (req.destroyed) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const response = {
|
|
198
|
+
headers: res.headers,
|
|
199
|
+
request: req,
|
|
200
|
+
stream: res,
|
|
201
|
+
statusCode: res.statusCode,
|
|
202
|
+
config
|
|
203
|
+
};
|
|
204
|
+
utils_1.LOGGER.debug(`>>> HTTP REQUEST : ${req.method?.toUpperCase()} ${req.protocol}//${req.host}${req.path}`);
|
|
205
|
+
process.env.ZC_SECURE?.toLowerCase() === 'override' &&
|
|
206
|
+
utils_1.LOGGER.fine(`>>> REQUEST HEADERS : ${JSON.stringify(options.headers)}`);
|
|
207
|
+
utils_1.LOGGER.debug(`<<< HTTP RESPONSE : ${res.statusCode} : ${Date.now() - startTimeStamp} ms`);
|
|
208
|
+
process.env.ZC_SECURE?.toLowerCase() === 'override' &&
|
|
209
|
+
utils_1.LOGGER.fine(`<<< RESPONSE HEADERS : ${JSON.stringify(res.headers)}`);
|
|
210
|
+
if (config.expecting === "raw") {
|
|
211
|
+
return _finalizeRequest(resolve, reject, response);
|
|
212
|
+
}
|
|
213
|
+
try {
|
|
214
|
+
const responseBuffer = await streamToBuffer(res);
|
|
215
|
+
response.data = responseBuffer.toString();
|
|
216
|
+
response.buffer = responseBuffer;
|
|
217
|
+
}
|
|
218
|
+
catch (err) {
|
|
219
|
+
if (req.destroyed || (config.abortSignal && config.abortSignal.aborted)) {
|
|
220
|
+
req.destroy();
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
reject(err);
|
|
224
|
+
}
|
|
225
|
+
_finalizeRequest(resolve, reject, response);
|
|
226
|
+
});
|
|
227
|
+
req.on('error', (err) => {
|
|
228
|
+
utils_1.LOGGER.debug(`>>> HTTP REQUEST : ${req.method?.toUpperCase()} ${req.protocol}//${req.host}${req.path}`);
|
|
229
|
+
process.env.ZC_SECURE?.toLowerCase() === 'override' &&
|
|
230
|
+
utils_1.LOGGER.fine(`>>> REQUEST HEADERS : ${JSON.stringify(options.headers)}`);
|
|
231
|
+
utils_1.LOGGER.debug(`<<< HTTP REQUEST ERROR : ${(0, util_1.inspect)(err)} : ${Date.now() - startTimeStamp} ms`);
|
|
232
|
+
if (req.destroyed || config.type === "raw") {
|
|
233
|
+
return reject(err);
|
|
234
|
+
}
|
|
235
|
+
retryRequest(err);
|
|
236
|
+
});
|
|
237
|
+
if (data === undefined) {
|
|
238
|
+
req.end();
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
if (config.type !== "file" && config.type !== "raw") {
|
|
242
|
+
req.write(data);
|
|
243
|
+
req.end();
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
if (data instanceof web_1.ReadableStream) {
|
|
247
|
+
data = webStreamToNodeStream(data);
|
|
248
|
+
}
|
|
249
|
+
data.on('error', (er) => {
|
|
250
|
+
reject(er);
|
|
251
|
+
req.end();
|
|
252
|
+
});
|
|
253
|
+
data.pipe(req).on('finish', req.end);
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
function webStreamToNodeStream(webStream) {
|
|
257
|
+
const reader = webStream.getReader();
|
|
258
|
+
return new stream_1.Readable({
|
|
259
|
+
async read() {
|
|
260
|
+
const { done, value } = await reader.read();
|
|
261
|
+
if (done) {
|
|
262
|
+
this.push(null);
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
this.push(value);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
async function sendRequest(config) {
|
|
271
|
+
let data;
|
|
272
|
+
let headers = Object.assign({
|
|
273
|
+
[USER_AGENT.KEY]: USER_AGENT.PREFIX + package_json_1.version
|
|
274
|
+
}, config.headers);
|
|
275
|
+
if (config.data !== undefined) {
|
|
276
|
+
switch (config.type) {
|
|
277
|
+
case "json":
|
|
278
|
+
data = JSON.stringify(config.data);
|
|
279
|
+
headers['Content-Type'] = 'application/json';
|
|
280
|
+
break;
|
|
281
|
+
case "file":
|
|
282
|
+
data = constructFormData(config.data);
|
|
283
|
+
headers = data.getHeaders(headers);
|
|
284
|
+
break;
|
|
285
|
+
case "raw":
|
|
286
|
+
data = config.data;
|
|
287
|
+
if (headers['Content-Type'] === undefined) {
|
|
288
|
+
headers['Content-Type'] = 'application/octet-stream';
|
|
289
|
+
}
|
|
290
|
+
break;
|
|
291
|
+
default:
|
|
292
|
+
data = (0, querystring_1.stringify)(config.data);
|
|
293
|
+
headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
|
294
|
+
headers['Content-Length'] = Buffer.byteLength(data) + '';
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
const origin = config.origin || CATALYST_ORIGIN;
|
|
298
|
+
config.url = config.url || new url_1.URL(config.path || '', origin).href;
|
|
299
|
+
if (config.qs !== undefined) {
|
|
300
|
+
config.url = _appendQueryData(config.url, config.qs);
|
|
301
|
+
}
|
|
302
|
+
const parsedUrl = new url_1.URL(config.url);
|
|
303
|
+
if (parsedUrl.hostname === null) {
|
|
304
|
+
throw new errors_1.CatalystAPIError('UNPARSABLE_CONFIG', 'Hostname cannot be null', config.path, 400);
|
|
305
|
+
}
|
|
306
|
+
const isHttpsProtocol = (0, helpers_1.isHttps)(parsedUrl);
|
|
307
|
+
const requestAgent = new request_agent_1.default(isHttpsProtocol, parsedUrl.hostname, false);
|
|
308
|
+
parsedUrl.searchParams?.sort();
|
|
309
|
+
const options = {
|
|
310
|
+
hostname: parsedUrl.hostname,
|
|
311
|
+
port: parsedUrl.port,
|
|
312
|
+
path: parsedUrl.pathname + parsedUrl.search,
|
|
313
|
+
method: config.method,
|
|
314
|
+
headers,
|
|
315
|
+
agent: requestAgent.agent
|
|
316
|
+
};
|
|
317
|
+
const transport = isHttpsProtocol ? https_1.default : http_1.default;
|
|
318
|
+
return _request(transport, options, config, data);
|
|
319
|
+
}
|
|
320
|
+
class HttpClient {
|
|
321
|
+
constructor(app) {
|
|
322
|
+
this.app = app;
|
|
323
|
+
this.user = CREDENTIAL_USER.admin;
|
|
324
|
+
}
|
|
325
|
+
async send(req, apmTrackerName) {
|
|
326
|
+
req.headers = Object.assign({}, req.headers);
|
|
327
|
+
req.qs = Object.assign({}, req.qs);
|
|
328
|
+
req.retry = req.retry || true;
|
|
329
|
+
if (this.app !== undefined && req.service !== "external") {
|
|
330
|
+
this.user = this.app.credential.getCurrentUser();
|
|
331
|
+
req.headers[PROJECT_KEY_NAME] = this.app.config.projectKey;
|
|
332
|
+
req.headers[ENVIRONMENT_KEY_NAME] = this.app.config.environment;
|
|
333
|
+
req.headers[ENVIRONMENT] = this.app.config.environment;
|
|
334
|
+
if ((0, utils_1.isNonEmptyString)(process.env.X_ZOHO_CATALYST_ORG_ID)) {
|
|
335
|
+
req.headers[X_ZOHO_CATALYST_ORG_ID] = process.env.X_ZOHO_CATALYST_ORG_ID;
|
|
336
|
+
}
|
|
337
|
+
if ((0, utils_1.isNonEmptyString)(this.app.config.projectSecretKey)) {
|
|
338
|
+
req.headers[PROJECT_HEADER.projectSecretKey] = this.app.config
|
|
339
|
+
.projectSecretKey;
|
|
340
|
+
}
|
|
341
|
+
req.headers[USER_KEY_NAME] = this.app.credential.getCurrentUserType();
|
|
342
|
+
if (IS_LOCAL === 'true') {
|
|
343
|
+
switch (this.user) {
|
|
344
|
+
case CREDENTIAL_USER.admin:
|
|
345
|
+
req.origin =
|
|
346
|
+
'https://' +
|
|
347
|
+
CATALYST_ORIGIN.replace('https://', '').replace('http://', '');
|
|
348
|
+
break;
|
|
349
|
+
case CREDENTIAL_USER.user:
|
|
350
|
+
req.origin = 'https://' + this.app.config.projectDomain;
|
|
351
|
+
break;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
if (req.service === "baas") {
|
|
355
|
+
req.headers[ACCEPT_HEADER.KEY] =
|
|
356
|
+
ACCEPT_HEADER.VALUE + ', ' + (req.headers[ACCEPT_HEADER.KEY] || '');
|
|
357
|
+
}
|
|
358
|
+
req.path =
|
|
359
|
+
(0, utils_1.getServicePath)(req.service) + `/project/${this.app.config.projectId}` + req.path;
|
|
360
|
+
}
|
|
361
|
+
try {
|
|
362
|
+
let resp;
|
|
363
|
+
if (req.track && apmTrackerName && IS_APM === 'true') {
|
|
364
|
+
try {
|
|
365
|
+
const apminsight = await Promise.resolve().then(() => __importStar(require('apminsight')));
|
|
366
|
+
resp = await apminsight.startTracker(APM_INSIGHT.tracker_name, apmTrackerName, () => sendRequest(req));
|
|
367
|
+
}
|
|
368
|
+
catch (err) {
|
|
369
|
+
throw new errors_1.CatalystAPIError('APM_TRACKER_ERROR', 'To enable APM tracking locally, please download the apminsight package from the UI and place it in the node_modules directory of your project.', err, 400);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
resp = await sendRequest(req);
|
|
374
|
+
}
|
|
375
|
+
return new DefaultHttpResponse(resp);
|
|
376
|
+
}
|
|
377
|
+
catch (err) {
|
|
378
|
+
if (err instanceof Error) {
|
|
379
|
+
throw new errors_1.CatalystAPIError('REQUEST_FAILURE', err.message, err, err.message.includes('ECONNREFUSED') ? 503 : 400);
|
|
380
|
+
}
|
|
381
|
+
throw err;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
exports.HttpClient = HttpClient;
|
|
386
|
+
class AuthorizedHttpClient extends HttpClient {
|
|
387
|
+
constructor(app, component) {
|
|
388
|
+
super(app);
|
|
389
|
+
if (component) {
|
|
390
|
+
this.componentName = component.getComponentName();
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
async send(request) {
|
|
394
|
+
const requestCopy = Object.assign({ user: CREDENTIAL_USER.user }, request);
|
|
395
|
+
requestCopy.headers = Object.assign({}, request.headers);
|
|
396
|
+
if (request.auth !== false) {
|
|
397
|
+
await this.app?.authenticateRequest(requestCopy);
|
|
398
|
+
}
|
|
399
|
+
return await super.send(requestCopy, this.componentName);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
exports.AuthorizedHttpClient = AuthorizedHttpClient;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CatalystAPIError = exports.ResponseType = exports.RequestType = exports.Handler = void 0;
|
|
4
|
+
const fetch_handler_1 = require("./fetch-handler");
|
|
5
|
+
class Handler {
|
|
6
|
+
constructor(app, component) {
|
|
7
|
+
this.component = component;
|
|
8
|
+
}
|
|
9
|
+
async send(options) {
|
|
10
|
+
return (await fetch_handler_1.ResponseHandler.send(options));
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
exports.Handler = Handler;
|
|
14
|
+
var enums_1 = require("./utils/enums");
|
|
15
|
+
Object.defineProperty(exports, "RequestType", { enumerable: true, get: function () { return enums_1.RequestType; } });
|
|
16
|
+
Object.defineProperty(exports, "ResponseType", { enumerable: true, get: function () { return enums_1.ResponseType; } });
|
|
17
|
+
var errors_1 = require("./utils/errors");
|
|
18
|
+
Object.defineProperty(exports, "CatalystAPIError", { enumerable: true, get: function () { return errors_1.CatalystAPIError; } });
|