@verii/http-client 1.1.0-pre.1757916914 → 1.1.0-pre.1758002630
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/index.js +2 -2
- package/package.json +4 -3
- package/src/client.js +151 -44
package/index.js
CHANGED
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.1758002630",
|
|
4
4
|
"description": "HTTP client for Verii network",
|
|
5
5
|
"repository": "https://github.com/LFDT-Verii/core",
|
|
6
6
|
"main": "index.js",
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
"http-errors": "^2.0.0",
|
|
21
21
|
"lodash": "^4.17.21",
|
|
22
22
|
"nanoid": "~5.1.0",
|
|
23
|
-
"undici": "^7.0.0"
|
|
23
|
+
"undici": "^7.0.0",
|
|
24
|
+
"undici-oidc-interceptor": "^0.6.0"
|
|
24
25
|
},
|
|
25
26
|
"devDependencies": {
|
|
26
27
|
"eslint": "8.57.1",
|
|
@@ -41,5 +42,5 @@
|
|
|
41
42
|
"lib"
|
|
42
43
|
]
|
|
43
44
|
},
|
|
44
|
-
"gitHead": "
|
|
45
|
+
"gitHead": "b961d761dc0f0710df506d90222145afe3c35cf6"
|
|
45
46
|
}
|
package/src/client.js
CHANGED
|
@@ -15,71 +15,149 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
const { nanoid } = require('nanoid/non-secure');
|
|
18
|
-
const {
|
|
18
|
+
const {
|
|
19
|
+
Agent,
|
|
20
|
+
interceptors,
|
|
21
|
+
cacheStores,
|
|
22
|
+
getGlobalDispatcher,
|
|
23
|
+
} = require('undici');
|
|
24
|
+
const { createOidcInterceptor } = require('undici-oidc-interceptor');
|
|
25
|
+
const { map } = require('lodash/fp');
|
|
19
26
|
const pkg = require('../package.json');
|
|
20
27
|
|
|
21
28
|
const USER_AGENT_HEADER = `${pkg.name}/${pkg.version}`;
|
|
22
29
|
const registeredPrefixUrls = new Map();
|
|
23
30
|
|
|
31
|
+
const initCache = () => new cacheStores.MemoryCacheStore();
|
|
32
|
+
|
|
33
|
+
const buildInterceptors = ({
|
|
34
|
+
isTest,
|
|
35
|
+
cache,
|
|
36
|
+
tokensEndpoint,
|
|
37
|
+
clientId,
|
|
38
|
+
clientSecret,
|
|
39
|
+
scopes,
|
|
40
|
+
audience,
|
|
41
|
+
}) => {
|
|
42
|
+
const requiredInterceptors = [
|
|
43
|
+
interceptors.responseError(),
|
|
44
|
+
...addCache(cache),
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
if (tokensEndpoint) {
|
|
48
|
+
const origins = map(
|
|
49
|
+
(url) => url.origin,
|
|
50
|
+
registeredPrefixUrls.values().toArray()
|
|
51
|
+
);
|
|
52
|
+
const oidcInterceptor = createOidcInterceptor({
|
|
53
|
+
idpTokenUrl: tokensEndpoint,
|
|
54
|
+
clientId,
|
|
55
|
+
clientSecret,
|
|
56
|
+
retryOnStatusCodes: [401],
|
|
57
|
+
scopes,
|
|
58
|
+
audience,
|
|
59
|
+
urls: origins,
|
|
60
|
+
});
|
|
61
|
+
requiredInterceptors.push(oidcInterceptor);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!isTest) {
|
|
65
|
+
requiredInterceptors.push(
|
|
66
|
+
interceptors.dns({ maxTTL: 300000, maxItems: 2000, dualStack: false })
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return requiredInterceptors;
|
|
71
|
+
};
|
|
72
|
+
|
|
24
73
|
const initHttpClient = (options) => {
|
|
74
|
+
const { prefixUrl, isTest, bearerToken } = options;
|
|
75
|
+
|
|
25
76
|
const { clientOptions, traceIdHeader, customHeaders } = parseOptions(options);
|
|
26
77
|
|
|
27
|
-
// register
|
|
28
|
-
|
|
78
|
+
// register prefixUrl
|
|
79
|
+
if (prefixUrl) {
|
|
29
80
|
const parsedPrefixUrl = parsePrefixUrl(prefixUrl);
|
|
30
81
|
registeredPrefixUrls.set(prefixUrl, parsedPrefixUrl);
|
|
31
82
|
}
|
|
32
83
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
84
|
+
let agent;
|
|
85
|
+
|
|
86
|
+
if (isTest) {
|
|
87
|
+
const existingAgent = getGlobalDispatcher();
|
|
88
|
+
agent = existingAgent.compose(buildInterceptors(options));
|
|
89
|
+
} else {
|
|
90
|
+
agent = new Agent(clientOptions).compose(buildInterceptors(options));
|
|
91
|
+
}
|
|
41
92
|
|
|
42
|
-
const request = async (
|
|
93
|
+
const request = async (
|
|
94
|
+
url,
|
|
95
|
+
reqOptions,
|
|
96
|
+
method,
|
|
97
|
+
host,
|
|
98
|
+
{ traceId, log },
|
|
99
|
+
body
|
|
100
|
+
) => {
|
|
43
101
|
const reqId = nanoid();
|
|
44
102
|
const reqHeaders = {
|
|
45
103
|
'user-agent': USER_AGENT_HEADER,
|
|
46
104
|
[traceIdHeader]: traceId,
|
|
47
105
|
...customHeaders,
|
|
106
|
+
...reqOptions?.headers,
|
|
107
|
+
...buildBearerAuthorizationHeader(bearerToken),
|
|
48
108
|
};
|
|
49
|
-
const [origin, path] =
|
|
50
|
-
host != null
|
|
51
|
-
? [host.origin, buildRelativePath(host.rootPath, url, reqOptions)]
|
|
52
|
-
: parseFullURL(url, clientOptions, reqOptions);
|
|
109
|
+
const [origin, path] = buildUrl(host, url, reqOptions, clientOptions);
|
|
53
110
|
|
|
54
111
|
log.info({ origin, path, url, reqId, reqHeaders }, 'HttpClient request');
|
|
55
112
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
113
|
+
try {
|
|
114
|
+
const httpRequest = {
|
|
115
|
+
origin,
|
|
116
|
+
path,
|
|
117
|
+
method,
|
|
118
|
+
headers: reqHeaders,
|
|
119
|
+
};
|
|
120
|
+
if (body) {
|
|
121
|
+
httpRequest.body = body;
|
|
122
|
+
}
|
|
123
|
+
const httpResponse = await agent.request(httpRequest);
|
|
124
|
+
const { statusCode, headers: resHeaders, body: rawBody } = httpResponse;
|
|
125
|
+
return {
|
|
126
|
+
rawBody,
|
|
127
|
+
statusCode,
|
|
128
|
+
resHeaders,
|
|
129
|
+
json: async () => {
|
|
130
|
+
try {
|
|
131
|
+
const bodyJson = await rawBody.json();
|
|
132
|
+
log.info(
|
|
133
|
+
{ origin, url, reqId, statusCode, resHeaders, body: bodyJson },
|
|
134
|
+
'HttpClient response'
|
|
135
|
+
);
|
|
136
|
+
return bodyJson;
|
|
137
|
+
} catch (error) {
|
|
138
|
+
log.error(
|
|
139
|
+
{ origin, url, reqId, statusCode, resHeaders, error },
|
|
140
|
+
'JSON parsing error'
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
return {};
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
text: async () => {
|
|
147
|
+
const bodyText = await rawBody.text();
|
|
148
|
+
log.info(
|
|
149
|
+
{ origin, url, reqId, statusCode, resHeaders, body: bodyText },
|
|
150
|
+
'HttpClient response'
|
|
151
|
+
);
|
|
152
|
+
return bodyText;
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
} catch (error) {
|
|
156
|
+
// eslint-disable-next-line better-mutation/no-mutation
|
|
157
|
+
error.url = `${origin}${path}`;
|
|
158
|
+
|
|
159
|
+
throw error;
|
|
160
|
+
}
|
|
83
161
|
};
|
|
84
162
|
|
|
85
163
|
return (...args) => {
|
|
@@ -93,6 +171,17 @@ const initHttpClient = (options) => {
|
|
|
93
171
|
return {
|
|
94
172
|
get: (url, reqOptions) =>
|
|
95
173
|
request(url, reqOptions, HTTP_VERBS.GET, host, context),
|
|
174
|
+
post: (url, payload, reqOptions) =>
|
|
175
|
+
request(
|
|
176
|
+
url,
|
|
177
|
+
reqOptions,
|
|
178
|
+
HTTP_VERBS.POST,
|
|
179
|
+
host,
|
|
180
|
+
context,
|
|
181
|
+
JSON.stringify(payload)
|
|
182
|
+
),
|
|
183
|
+
delete: (url, reqOptions) =>
|
|
184
|
+
request(url, reqOptions, HTTP_VERBS.DELETE, host, context),
|
|
96
185
|
responseType: 'promise',
|
|
97
186
|
};
|
|
98
187
|
};
|
|
@@ -124,6 +213,16 @@ const parsePrefixUrl = (prefixUrl) => {
|
|
|
124
213
|
};
|
|
125
214
|
};
|
|
126
215
|
|
|
216
|
+
const buildUrl = (host, url, reqOptions, clientOptions) => {
|
|
217
|
+
const fullUrl = reqOptions?.prefixUrl
|
|
218
|
+
? new URL(url, reqOptions.prefixUrl).toString()
|
|
219
|
+
: url;
|
|
220
|
+
|
|
221
|
+
return host && !reqOptions?.prefixUrl
|
|
222
|
+
? [host.origin, buildRelativePath(host.rootPath, url, reqOptions)]
|
|
223
|
+
: parseFullURL(fullUrl, clientOptions, reqOptions);
|
|
224
|
+
};
|
|
225
|
+
|
|
127
226
|
const parseFullURL = (url, clientOptions, reqOptions) => {
|
|
128
227
|
const { origin, pathname } = new URL(url);
|
|
129
228
|
return [origin, addSearchParams(pathname, reqOptions?.searchParams)];
|
|
@@ -138,8 +237,16 @@ const buildRelativePath = (rootPath, url, reqOptions) =>
|
|
|
138
237
|
const addSearchParams = (path, searchParams) =>
|
|
139
238
|
searchParams != null ? `${path}?${searchParams}` : path;
|
|
140
239
|
|
|
240
|
+
const addCache = (store) =>
|
|
241
|
+
store ? [interceptors.cache({ store, methods: ['GET'] })] : [];
|
|
242
|
+
|
|
243
|
+
const buildBearerAuthorizationHeader = (token) =>
|
|
244
|
+
token ? { Authorization: `Bearer ${token}` } : {};
|
|
245
|
+
|
|
141
246
|
const HTTP_VERBS = {
|
|
142
247
|
GET: 'GET',
|
|
248
|
+
POST: 'POST',
|
|
249
|
+
DELETE: 'DELETE',
|
|
143
250
|
};
|
|
144
251
|
|
|
145
|
-
module.exports = { initHttpClient, parseOptions, parsePrefixUrl };
|
|
252
|
+
module.exports = { initHttpClient, parseOptions, parsePrefixUrl, initCache };
|