@xrystal/core 3.26.5 → 3.26.6
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/package.json
CHANGED
|
@@ -38,20 +38,4 @@ export declare class BaseApiClient extends Client {
|
|
|
38
38
|
request(path: string, options?: CustomRequestOptions): Promise<Response>;
|
|
39
39
|
private _execute;
|
|
40
40
|
}
|
|
41
|
-
export declare abstract class AuthenticatedApiClient extends BaseApiClient {
|
|
42
|
-
private _authPromise;
|
|
43
|
-
constructor(config: any);
|
|
44
|
-
request(path: string, options?: RequestInit & {
|
|
45
|
-
version?: string;
|
|
46
|
-
retries?: number;
|
|
47
|
-
debug?: boolean;
|
|
48
|
-
}): Promise<Response>;
|
|
49
|
-
private _synchronizedAuthentication;
|
|
50
|
-
private internalLogin;
|
|
51
|
-
authentication(): Promise<any>;
|
|
52
|
-
}
|
|
53
|
-
export declare class SoapClient extends Client {
|
|
54
|
-
constructor(config: any);
|
|
55
|
-
call(methodName: string, args: any): Promise<any>;
|
|
56
|
-
}
|
|
57
41
|
export {};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { createHash, createHmac } from 'node:crypto';
|
|
2
|
-
import soap from 'soap';
|
|
3
2
|
import { Configs, Logger } from '../../../loader';
|
|
4
3
|
import { LoggerLayerEnum, x } from '../../index';
|
|
5
4
|
export class ClientStore {
|
|
@@ -16,7 +15,7 @@ export class Client {
|
|
|
16
15
|
baseURL;
|
|
17
16
|
version = null;
|
|
18
17
|
timeout = 15000;
|
|
19
|
-
debug
|
|
18
|
+
debug;
|
|
20
19
|
authConfigs;
|
|
21
20
|
breaker = {
|
|
22
21
|
failures: 0,
|
|
@@ -26,13 +25,13 @@ export class Client {
|
|
|
26
25
|
cooldown: 30000
|
|
27
26
|
};
|
|
28
27
|
constructor({ clientName, baseURL, version, timeout, auth, debug }) {
|
|
29
|
-
this.debug = debug ?? this.debug;
|
|
30
28
|
this.clientName = clientName;
|
|
31
29
|
this.baseURL = baseURL;
|
|
32
30
|
this.version = version || null;
|
|
33
31
|
this.authConfigs = auth;
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
this.timeout = timeout || 15000;
|
|
33
|
+
const configDebug = Number(this.configs.all.debug) >= LoggerLayerEnum.DEBUG;
|
|
34
|
+
this.debug = debug ?? configDebug;
|
|
36
35
|
if (auth?.token)
|
|
37
36
|
ClientStore.set(clientName, { accessToken: auth.token });
|
|
38
37
|
}
|
|
@@ -51,14 +50,14 @@ export class BaseApiClient extends Client {
|
|
|
51
50
|
this.breaker.state = 'HALF_OPEN';
|
|
52
51
|
}
|
|
53
52
|
else {
|
|
54
|
-
throw new Error(
|
|
53
|
+
throw new Error(`[CircuitBreaker] ${this.clientName} is OPEN`);
|
|
55
54
|
}
|
|
56
55
|
}
|
|
57
56
|
const maxRetries = options.retries ?? 3;
|
|
58
57
|
let attempt = 0;
|
|
59
58
|
while (attempt < maxRetries) {
|
|
60
59
|
try {
|
|
61
|
-
const response = await this._execute(path, options);
|
|
60
|
+
const response = await this._execute(path, { ...options });
|
|
62
61
|
if (response.ok || response.status === 401) {
|
|
63
62
|
this.breaker.failures = 0;
|
|
64
63
|
this.breaker.state = 'CLOSED';
|
|
@@ -81,7 +80,7 @@ export class BaseApiClient extends Client {
|
|
|
81
80
|
await new Promise(res => setTimeout(res, backoff));
|
|
82
81
|
}
|
|
83
82
|
}
|
|
84
|
-
throw new Error(`${this.clientName}
|
|
83
|
+
throw new Error(`${this.clientName} failed after ${maxRetries} attempts`);
|
|
85
84
|
}
|
|
86
85
|
async _execute(path, options) {
|
|
87
86
|
const base = this.baseURL.replace(/\/$/, '');
|
|
@@ -91,7 +90,11 @@ export class BaseApiClient extends Client {
|
|
|
91
90
|
const store = Logger.storage.getStore();
|
|
92
91
|
const correlationId = store?.get('correlationId');
|
|
93
92
|
const headers = new Headers(options.headers || {});
|
|
94
|
-
|
|
93
|
+
const isUrlEncoded = options.body instanceof URLSearchParams;
|
|
94
|
+
const isFormData = options.body instanceof FormData;
|
|
95
|
+
if (!isUrlEncoded && !isFormData) {
|
|
96
|
+
headers.set('Content-Type', 'application/json');
|
|
97
|
+
}
|
|
95
98
|
if (correlationId)
|
|
96
99
|
headers.set('x-correlation-id', correlationId);
|
|
97
100
|
const systemSecret = this.configs.all.internalSecret || process.env.INTERNAL_SECRET;
|
|
@@ -104,93 +107,36 @@ export class BaseApiClient extends Client {
|
|
|
104
107
|
headers.set('x-internal-timestamp', timestamp);
|
|
105
108
|
headers.set('x-internal-client', this.clientName);
|
|
106
109
|
}
|
|
107
|
-
const isDebugMode = options.debug
|
|
110
|
+
const isDebugMode = options.debug ?? this.debug;
|
|
108
111
|
const { version, retries, debug, ...fetchOptions } = options;
|
|
109
|
-
|
|
110
|
-
|
|
112
|
+
let processedBody = fetchOptions.body;
|
|
113
|
+
if (processedBody && typeof processedBody === 'object' && !isUrlEncoded && !isFormData) {
|
|
114
|
+
processedBody = JSON.stringify(processedBody);
|
|
111
115
|
}
|
|
112
116
|
if (isDebugMode) {
|
|
113
|
-
const
|
|
114
|
-
method:
|
|
117
|
+
const logData = {
|
|
118
|
+
method: fetchOptions.method || 'GET',
|
|
115
119
|
url,
|
|
116
|
-
|
|
117
|
-
body: options.body
|
|
120
|
+
body: processedBody && typeof processedBody === 'string' ? JSON.parse(processedBody) : processedBody
|
|
118
121
|
};
|
|
119
|
-
|
|
122
|
+
console.log(`[DEBUG] ${this.clientName}`, logData);
|
|
123
|
+
this.logger.winston.info(`${this.clientName} Request`, logData);
|
|
120
124
|
}
|
|
121
125
|
const controller = new AbortController();
|
|
122
126
|
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
123
|
-
const response = await fetch(url, { ...fetchOptions, headers, signal: controller.signal });
|
|
124
|
-
clearTimeout(timeoutId);
|
|
125
|
-
return response;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
export class AuthenticatedApiClient extends BaseApiClient {
|
|
129
|
-
_authPromise = null;
|
|
130
|
-
constructor(config) { super(config); }
|
|
131
|
-
async request(path, options = {}) {
|
|
132
|
-
const headerName = this.authConfigs?.header || 'Authorization';
|
|
133
|
-
const prefix = this.authConfigs?.prefix ?? 'Bearer';
|
|
134
|
-
const injectToken = (h, t) => h.set(headerName, prefix ? `${prefix} ${t}` : t);
|
|
135
|
-
let store = ClientStore.get(this.clientName);
|
|
136
|
-
const headers = new Headers(options.headers || {});
|
|
137
|
-
if (store.accessToken)
|
|
138
|
-
injectToken(headers, store.accessToken);
|
|
139
|
-
options.headers = headers;
|
|
140
|
-
let response = await super.request(path, options);
|
|
141
|
-
if (response.status === 401) {
|
|
142
|
-
await this._synchronizedAuthentication();
|
|
143
|
-
const freshStore = ClientStore.get(this.clientName);
|
|
144
|
-
if (freshStore.accessToken) {
|
|
145
|
-
const retryHeaders = new Headers(options.headers);
|
|
146
|
-
injectToken(retryHeaders, freshStore.accessToken);
|
|
147
|
-
return super.request(path, { ...options, headers: retryHeaders });
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
return response;
|
|
151
|
-
}
|
|
152
|
-
async _synchronizedAuthentication() {
|
|
153
|
-
if (this._authPromise)
|
|
154
|
-
return this._authPromise;
|
|
155
|
-
this._authPromise = (async () => {
|
|
156
|
-
await this.authentication();
|
|
157
|
-
await this.internalLogin();
|
|
158
|
-
})().finally(() => { this._authPromise = null; });
|
|
159
|
-
return this._authPromise;
|
|
160
|
-
}
|
|
161
|
-
async internalLogin() {
|
|
162
|
-
if (!this.authConfigs || !this.authConfigs.endpoint)
|
|
163
|
-
return;
|
|
164
|
-
const { endpoint, payload, method, tokenPath } = this.authConfigs;
|
|
165
127
|
try {
|
|
166
|
-
const response = await fetch(
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
128
|
+
const response = await fetch(url, {
|
|
129
|
+
...fetchOptions,
|
|
130
|
+
body: processedBody,
|
|
131
|
+
headers,
|
|
132
|
+
signal: controller.signal
|
|
170
133
|
});
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if (token)
|
|
174
|
-
ClientStore.set(this.clientName, { accessToken: token });
|
|
175
|
-
return data;
|
|
134
|
+
clearTimeout(timeoutId);
|
|
135
|
+
return response;
|
|
176
136
|
}
|
|
177
137
|
catch (error) {
|
|
138
|
+
clearTimeout(timeoutId);
|
|
178
139
|
throw error;
|
|
179
140
|
}
|
|
180
141
|
}
|
|
181
|
-
async authentication() { return true; }
|
|
182
|
-
}
|
|
183
|
-
export class SoapClient extends Client {
|
|
184
|
-
constructor(config) { super(config); }
|
|
185
|
-
async call(methodName, args) {
|
|
186
|
-
try {
|
|
187
|
-
const client = await soap.createClientAsync(this.baseURL);
|
|
188
|
-
const result = await client[`${methodName}Async`](args);
|
|
189
|
-
return result[0];
|
|
190
|
-
}
|
|
191
|
-
catch (error) {
|
|
192
|
-
this.logger.winston.error(`${this.clientName} SOAP Error: ${error.message}`);
|
|
193
|
-
return null;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
142
|
}
|