@verii/http-client 1.1.0-pre.1763452964 → 1.1.0-pre.1764142377
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 +120 -89
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.1764142377",
|
|
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": "39d0123fb7911d1a2814777ad0e0a25e35bc13c8"
|
|
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,33 +110,57 @@ 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
133
|
reqOptions,
|
|
179
134
|
HTTP_VERBS.POST,
|
|
180
135
|
host,
|
|
181
136
|
context,
|
|
182
|
-
isObject(payload) ? JSON.stringify(payload) : payload
|
|
137
|
+
isObject(payload) ? JSON.stringify(payload) : payload,
|
|
138
|
+
calcContentType(payload, reqOptions?.headers)
|
|
183
139
|
),
|
|
184
|
-
delete: (url, reqOptions) =>
|
|
140
|
+
delete: (url, reqOptions = {}) =>
|
|
185
141
|
request(url, reqOptions, HTTP_VERBS.DELETE, host, context),
|
|
186
142
|
responseType: 'promise',
|
|
187
143
|
};
|
|
188
144
|
};
|
|
189
145
|
};
|
|
190
146
|
|
|
147
|
+
const calcContentType = (payload, headers = {}) =>
|
|
148
|
+
isObject(payload) && !headers['content-type']
|
|
149
|
+
? 'application/json'
|
|
150
|
+
: headers['content-type'];
|
|
151
|
+
|
|
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
|
+
);
|
|
162
|
+
};
|
|
163
|
+
|
|
191
164
|
const parseOptions = (options) => {
|
|
192
165
|
const clientOptions = {
|
|
193
166
|
connect: {
|
|
@@ -205,6 +178,47 @@ const parseOptions = (options) => {
|
|
|
205
178
|
};
|
|
206
179
|
};
|
|
207
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
|
+
|
|
208
222
|
const parsePrefixUrl = (prefixUrl) => {
|
|
209
223
|
const url = new URL(prefixUrl);
|
|
210
224
|
return {
|
|
@@ -215,34 +229,45 @@ const parsePrefixUrl = (prefixUrl) => {
|
|
|
215
229
|
};
|
|
216
230
|
|
|
217
231
|
const buildUrl = (host, url, reqOptions) => {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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)];
|
|
225
241
|
};
|
|
226
242
|
|
|
227
243
|
const parseFullURL = (url, reqOptions) => {
|
|
228
244
|
const { origin, pathname, searchParams } = new URL(url);
|
|
229
245
|
return [
|
|
230
246
|
origin,
|
|
231
|
-
addSearchParams(pathname, reqOptions?.searchParams
|
|
247
|
+
addSearchParams(pathname, searchParams, reqOptions?.searchParams),
|
|
232
248
|
];
|
|
233
249
|
};
|
|
234
250
|
|
|
235
|
-
const buildRelativePath = (rootPath, url, reqOptions) =>
|
|
236
|
-
|
|
237
|
-
|
|
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),
|
|
238
258
|
reqOptions?.searchParams
|
|
239
259
|
);
|
|
260
|
+
};
|
|
240
261
|
|
|
241
|
-
const addSearchParams = (path, searchParams) =>
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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
|
+
};
|
|
246
271
|
|
|
247
272
|
const buildBearerAuthorizationHeader = (token) =>
|
|
248
273
|
token ? { Authorization: `Bearer ${token}` } : {};
|
|
@@ -253,4 +278,10 @@ const HTTP_VERBS = {
|
|
|
253
278
|
DELETE: 'DELETE',
|
|
254
279
|
};
|
|
255
280
|
|
|
256
|
-
module.exports = {
|
|
281
|
+
module.exports = {
|
|
282
|
+
initHttpClient,
|
|
283
|
+
parseOptions,
|
|
284
|
+
parsePrefixUrl,
|
|
285
|
+
initCache,
|
|
286
|
+
buildUrl,
|
|
287
|
+
};
|