@verii/http-client 1.1.0-pre.1764065131 → 1.1.0-pre.1765262457
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 +2 -2
- package/src/client.js +118 -98
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@verii/http-client",
|
|
3
|
-
"version": "1.1.0-pre.
|
|
3
|
+
"version": "1.1.0-pre.1765262457",
|
|
4
4
|
"description": "HTTP client for Verii network",
|
|
5
5
|
"repository": "https://github.com/LFDT-Verii/core",
|
|
6
6
|
"main": "index.js",
|
|
@@ -42,5 +42,5 @@
|
|
|
42
42
|
"lib"
|
|
43
43
|
]
|
|
44
44
|
},
|
|
45
|
-
"gitHead": "
|
|
45
|
+
"gitHead": "6a5515772ad290ae6cbc6884b85ef2ad86afa4c3"
|
|
46
46
|
}
|
package/src/client.js
CHANGED
|
@@ -22,74 +22,28 @@ const {
|
|
|
22
22
|
getGlobalDispatcher,
|
|
23
23
|
} = require('undici');
|
|
24
24
|
const { createOidcInterceptor } = require('undici-oidc-interceptor');
|
|
25
|
-
const { map } = require('lodash/fp');
|
|
26
25
|
const { isObject } = require('lodash');
|
|
27
26
|
const pkg = require('../package.json');
|
|
28
27
|
|
|
29
28
|
const USER_AGENT_HEADER = `${pkg.name}/${pkg.version}`;
|
|
30
|
-
const registeredPrefixUrls = new Map();
|
|
31
29
|
|
|
32
30
|
const initCache = () => new cacheStores.MemoryCacheStore();
|
|
33
31
|
|
|
34
|
-
const buildInterceptors = ({
|
|
35
|
-
isTest,
|
|
36
|
-
cache,
|
|
37
|
-
tokensEndpoint,
|
|
38
|
-
clientId,
|
|
39
|
-
clientSecret,
|
|
40
|
-
scopes,
|
|
41
|
-
audience,
|
|
42
|
-
}) => {
|
|
43
|
-
const requiredInterceptors = [
|
|
44
|
-
interceptors.responseError(),
|
|
45
|
-
...addCache(cache),
|
|
46
|
-
];
|
|
47
|
-
|
|
48
|
-
if (tokensEndpoint) {
|
|
49
|
-
const origins = map(
|
|
50
|
-
(url) => url.origin,
|
|
51
|
-
registeredPrefixUrls.values().toArray()
|
|
52
|
-
);
|
|
53
|
-
const oidcInterceptor = createOidcInterceptor({
|
|
54
|
-
idpTokenUrl: tokensEndpoint,
|
|
55
|
-
clientId,
|
|
56
|
-
clientSecret,
|
|
57
|
-
retryOnStatusCodes: [401],
|
|
58
|
-
scope: scopes,
|
|
59
|
-
audience,
|
|
60
|
-
urls: origins,
|
|
61
|
-
});
|
|
62
|
-
requiredInterceptors.push(oidcInterceptor);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (!isTest) {
|
|
66
|
-
requiredInterceptors.push(
|
|
67
|
-
interceptors.dns({ maxTTL: 300000, maxItems: 2000, dualStack: false })
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return requiredInterceptors;
|
|
72
|
-
};
|
|
73
|
-
|
|
74
32
|
const initHttpClient = (options) => {
|
|
75
33
|
const { prefixUrl, isTest, bearerToken } = options;
|
|
76
34
|
|
|
77
35
|
const { clientOptions, traceIdHeader, customHeaders } = parseOptions(options);
|
|
78
36
|
|
|
79
|
-
|
|
80
|
-
if (prefixUrl) {
|
|
81
|
-
const parsedPrefixUrl = parsePrefixUrl(prefixUrl);
|
|
82
|
-
registeredPrefixUrls.set(prefixUrl, parsedPrefixUrl);
|
|
83
|
-
}
|
|
37
|
+
const presetHost = prefixUrl != null ? parsePrefixUrl(prefixUrl) : undefined;
|
|
84
38
|
|
|
85
|
-
|
|
39
|
+
const baseAgent = isTest ? getGlobalDispatcher() : new Agent(clientOptions);
|
|
40
|
+
const agent = baseAgent.compose(buildInterceptorChain(presetHost, options));
|
|
86
41
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
42
|
+
const defaultReqHeaders = {
|
|
43
|
+
'user-agent': USER_AGENT_HEADER,
|
|
44
|
+
...customHeaders,
|
|
45
|
+
...buildBearerAuthorizationHeader(bearerToken),
|
|
46
|
+
};
|
|
93
47
|
|
|
94
48
|
const request = async (
|
|
95
49
|
url,
|
|
@@ -97,16 +51,11 @@ const initHttpClient = (options) => {
|
|
|
97
51
|
method,
|
|
98
52
|
host,
|
|
99
53
|
{ traceId, log },
|
|
100
|
-
body
|
|
54
|
+
body,
|
|
55
|
+
contentType
|
|
101
56
|
) => {
|
|
102
57
|
const reqId = nanoid();
|
|
103
|
-
const reqHeaders =
|
|
104
|
-
'user-agent': USER_AGENT_HEADER,
|
|
105
|
-
[traceIdHeader]: traceId,
|
|
106
|
-
...customHeaders,
|
|
107
|
-
...reqOptions?.headers,
|
|
108
|
-
...buildBearerAuthorizationHeader(bearerToken),
|
|
109
|
-
};
|
|
58
|
+
const reqHeaders = buildReqHeaders(reqOptions, contentType, traceId);
|
|
110
59
|
const [origin, path] = buildUrl(host, url, reqOptions);
|
|
111
60
|
|
|
112
61
|
log.info({ origin, path, url, reqId, reqHeaders }, 'HttpClient request');
|
|
@@ -161,42 +110,55 @@ const initHttpClient = (options) => {
|
|
|
161
110
|
}
|
|
162
111
|
};
|
|
163
112
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
113
|
+
const buildReqHeaders = (reqOptions, contentType, traceId) => {
|
|
114
|
+
const reqHeaders = {
|
|
115
|
+
...defaultReqHeaders,
|
|
116
|
+
...(reqOptions.headers ?? {}),
|
|
117
|
+
[traceIdHeader]: traceId,
|
|
118
|
+
};
|
|
119
|
+
if (contentType) {
|
|
120
|
+
reqHeaders['content-type'] = contentType;
|
|
170
121
|
}
|
|
122
|
+
return reqHeaders;
|
|
123
|
+
};
|
|
171
124
|
|
|
125
|
+
return (...args) => {
|
|
126
|
+
const { host, context } = parseArgs(presetHost, args);
|
|
172
127
|
return {
|
|
173
|
-
get: (url, reqOptions) =>
|
|
128
|
+
get: (url, reqOptions = {}) =>
|
|
174
129
|
request(url, reqOptions, HTTP_VERBS.GET, host, context),
|
|
175
|
-
post: (url, payload, reqOptions) =>
|
|
130
|
+
post: (url, payload, reqOptions = {}) =>
|
|
176
131
|
request(
|
|
177
132
|
url,
|
|
178
|
-
|
|
179
|
-
...reqOptions,
|
|
180
|
-
headers: setContentType(reqOptions?.headers || {}, payload),
|
|
181
|
-
},
|
|
133
|
+
reqOptions,
|
|
182
134
|
HTTP_VERBS.POST,
|
|
183
135
|
host,
|
|
184
136
|
context,
|
|
185
|
-
isObject(payload) ? JSON.stringify(payload) : payload
|
|
137
|
+
isObject(payload) ? JSON.stringify(payload) : payload,
|
|
138
|
+
calcContentType(payload, reqOptions?.headers)
|
|
186
139
|
),
|
|
187
|
-
delete: (url, reqOptions) =>
|
|
140
|
+
delete: (url, reqOptions = {}) =>
|
|
188
141
|
request(url, reqOptions, HTTP_VERBS.DELETE, host, context),
|
|
189
142
|
responseType: 'promise',
|
|
190
143
|
};
|
|
191
144
|
};
|
|
192
145
|
};
|
|
193
146
|
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
147
|
+
const calcContentType = (payload, headers = {}) =>
|
|
148
|
+
isObject(payload) && !headers['content-type']
|
|
149
|
+
? 'application/json'
|
|
150
|
+
: headers['content-type'];
|
|
198
151
|
|
|
199
|
-
|
|
152
|
+
const parseArgs = (presetHost, args) => {
|
|
153
|
+
if (args.length === 1) {
|
|
154
|
+
return { host: presetHost, context: args[0] };
|
|
155
|
+
}
|
|
156
|
+
if (args.length === 2) {
|
|
157
|
+
return { host: parsePrefixUrl(args[0]), context: args[1] };
|
|
158
|
+
}
|
|
159
|
+
throw new Error(
|
|
160
|
+
`HttpClient: Expected 1 or 2 arguments, received ${args.length}`
|
|
161
|
+
);
|
|
200
162
|
};
|
|
201
163
|
|
|
202
164
|
const parseOptions = (options) => {
|
|
@@ -216,6 +178,47 @@ const parseOptions = (options) => {
|
|
|
216
178
|
};
|
|
217
179
|
};
|
|
218
180
|
|
|
181
|
+
const buildInterceptorChain = (
|
|
182
|
+
host,
|
|
183
|
+
{
|
|
184
|
+
isTest,
|
|
185
|
+
cache: cacheStore,
|
|
186
|
+
tokensEndpoint,
|
|
187
|
+
clientId,
|
|
188
|
+
clientSecret,
|
|
189
|
+
scopes,
|
|
190
|
+
audience,
|
|
191
|
+
}
|
|
192
|
+
) => {
|
|
193
|
+
const chain = [];
|
|
194
|
+
if (!isTest) {
|
|
195
|
+
chain.push(
|
|
196
|
+
interceptors.dns({ maxTTL: 300000, maxItems: 2000, dualStack: false })
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
chain.push(interceptors.responseError());
|
|
201
|
+
|
|
202
|
+
if (cacheStore != null) {
|
|
203
|
+
chain.push(interceptors.cache({ store: cacheStore, methods: ['GET'] }));
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (tokensEndpoint && host != null) {
|
|
207
|
+
const oidcInterceptor = createOidcInterceptor({
|
|
208
|
+
idpTokenUrl: tokensEndpoint,
|
|
209
|
+
clientId,
|
|
210
|
+
clientSecret,
|
|
211
|
+
retryOnStatusCodes: [401],
|
|
212
|
+
scope: scopes,
|
|
213
|
+
audience,
|
|
214
|
+
urls: [host.origin],
|
|
215
|
+
});
|
|
216
|
+
chain.push(oidcInterceptor);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return chain;
|
|
220
|
+
};
|
|
221
|
+
|
|
219
222
|
const parsePrefixUrl = (prefixUrl) => {
|
|
220
223
|
const url = new URL(prefixUrl);
|
|
221
224
|
return {
|
|
@@ -226,34 +229,45 @@ const parsePrefixUrl = (prefixUrl) => {
|
|
|
226
229
|
};
|
|
227
230
|
|
|
228
231
|
const buildUrl = (host, url, reqOptions) => {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
232
|
+
if (/https?:\/\//.test(url)) {
|
|
233
|
+
return parseFullURL(url, reqOptions);
|
|
234
|
+
}
|
|
235
|
+
if (!host) {
|
|
236
|
+
throw new Error(
|
|
237
|
+
'HttpClient: Cannot build URL without prefixUrl or full url'
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
return [host.origin, buildRelativePath(host.rootPath ?? '', url, reqOptions)];
|
|
236
241
|
};
|
|
237
242
|
|
|
238
243
|
const parseFullURL = (url, reqOptions) => {
|
|
239
244
|
const { origin, pathname, searchParams } = new URL(url);
|
|
240
245
|
return [
|
|
241
246
|
origin,
|
|
242
|
-
addSearchParams(pathname, reqOptions?.searchParams
|
|
247
|
+
addSearchParams(pathname, searchParams, reqOptions?.searchParams),
|
|
243
248
|
];
|
|
244
249
|
};
|
|
245
250
|
|
|
246
|
-
const buildRelativePath = (rootPath, url, reqOptions) =>
|
|
247
|
-
|
|
248
|
-
|
|
251
|
+
const buildRelativePath = (rootPath, url, reqOptions) => {
|
|
252
|
+
const relativePath = `${rootPath}${url.charAt(0) === '/' ? '' : '/'}${url}`;
|
|
253
|
+
const [pathname, searchParamsString] = relativePath.split('?');
|
|
254
|
+
|
|
255
|
+
return addSearchParams(
|
|
256
|
+
pathname,
|
|
257
|
+
new URLSearchParams(searchParamsString),
|
|
249
258
|
reqOptions?.searchParams
|
|
250
259
|
);
|
|
260
|
+
};
|
|
251
261
|
|
|
252
|
-
const addSearchParams = (path, searchParams) =>
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
262
|
+
const addSearchParams = (path, searchParams, additionalParams) => {
|
|
263
|
+
const params = additionalParams?.size
|
|
264
|
+
? new URLSearchParams([
|
|
265
|
+
...Array.from(searchParams.entries()),
|
|
266
|
+
...Array.from(additionalParams.entries()),
|
|
267
|
+
])
|
|
268
|
+
: searchParams;
|
|
269
|
+
return params?.size ? `${path}?${params}` : path;
|
|
270
|
+
};
|
|
257
271
|
|
|
258
272
|
const buildBearerAuthorizationHeader = (token) =>
|
|
259
273
|
token ? { Authorization: `Bearer ${token}` } : {};
|
|
@@ -264,4 +278,10 @@ const HTTP_VERBS = {
|
|
|
264
278
|
DELETE: 'DELETE',
|
|
265
279
|
};
|
|
266
280
|
|
|
267
|
-
module.exports = {
|
|
281
|
+
module.exports = {
|
|
282
|
+
initHttpClient,
|
|
283
|
+
parseOptions,
|
|
284
|
+
parsePrefixUrl,
|
|
285
|
+
initCache,
|
|
286
|
+
buildUrl,
|
|
287
|
+
};
|