@aligent/microservice-util-lib 1.2.1 → 1.3.0
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 +1 -1
- package/src/openapi-fetch-middlewares/oauth10a/oauth10a.js +7 -19
- package/src/openapi-fetch-middlewares/retry.d.ts +2 -1
- package/src/openapi-fetch-middlewares/retry.js +17 -12
- package/src/openapi-fetch-middlewares/utils/http-response-error.d.ts +42 -0
- package/src/openapi-fetch-middlewares/utils/http-response-error.js +65 -0
package/package.json
CHANGED
|
@@ -64,27 +64,18 @@ function combineUrlAndPathParams(url, pathParams) {
|
|
|
64
64
|
*
|
|
65
65
|
* @param {string} baseURL - The base URL.
|
|
66
66
|
* @param {string} url - The URL to process.
|
|
67
|
-
* @returns
|
|
67
|
+
* @returns The processed URL.
|
|
68
68
|
*/
|
|
69
|
-
function
|
|
69
|
+
function getOAuthUrl(baseURL, url) {
|
|
70
70
|
const oauthUrl = new URL(!baseURL || isAbsoluteURL(url) ? url : combineURLs(baseURL, url));
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (oauthUrl.search) {
|
|
74
|
-
searchParams = new URLSearchParams(oauthUrl.search);
|
|
75
|
-
oauthUrl.search = '';
|
|
76
|
-
}
|
|
77
|
-
// Do not include hash in signature
|
|
78
|
-
oauthUrl.hash = '';
|
|
71
|
+
oauthUrl.search = ''; // Query parameters are hashed as part of params rather than as part of the URL
|
|
72
|
+
oauthUrl.hash = ''; // Do not include hash in signature
|
|
79
73
|
// Remove port if it is the default for that protocol
|
|
80
74
|
if ((oauthUrl.protocol === 'https:' && oauthUrl.port === '443') ||
|
|
81
75
|
(oauthUrl.protocol === 'http:' && oauthUrl.port === '80')) {
|
|
82
76
|
oauthUrl.port = '';
|
|
83
77
|
}
|
|
84
|
-
return
|
|
85
|
-
baseUri: oauthUrl.toString(),
|
|
86
|
-
searchParams,
|
|
87
|
-
};
|
|
78
|
+
return oauthUrl.toString();
|
|
88
79
|
}
|
|
89
80
|
/**
|
|
90
81
|
* Adds a parameter to the list of parameters to sign.
|
|
@@ -174,10 +165,6 @@ async function generateOauthParams(request, options, params, config) {
|
|
|
174
165
|
if (params.query) {
|
|
175
166
|
addParamsToSign(paramsToSign, params.query);
|
|
176
167
|
}
|
|
177
|
-
const { baseUri, searchParams } = handleOAuthUrl(options.baseUrl, url);
|
|
178
|
-
if (searchParams) {
|
|
179
|
-
addParamsToSign(paramsToSign, searchParams);
|
|
180
|
-
}
|
|
181
168
|
const body = await request.text();
|
|
182
169
|
// If user submit a form, then include form parameters in the
|
|
183
170
|
// signature as parameters rather than the body hash
|
|
@@ -195,7 +182,8 @@ async function generateOauthParams(request, options, params, config) {
|
|
|
195
182
|
addParamToSign(paramsToSign, 'oauth_body_hash', bodyHash);
|
|
196
183
|
}
|
|
197
184
|
}
|
|
198
|
-
|
|
185
|
+
const oauthUrl = getOAuthUrl(options.baseUrl, url);
|
|
186
|
+
oauthParams.oauth_signature = (0, oauth_sign_1.sign)(algorithm, method, oauthUrl, paramsToSign, consumerSecret, tokenSecret);
|
|
199
187
|
// realm should not be included in the signature calculation
|
|
200
188
|
// but is optional in the OAuth 1.0 Authorization header
|
|
201
189
|
// so we need to add it after signing the request
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Middleware } from 'openapi-fetch';
|
|
2
2
|
import type { RetryConfig, RetryContext, RetryDelayFn } from './types/retry';
|
|
3
|
+
import { HttpResponseError, isHttpResponseError } from './utils/http-response-error';
|
|
3
4
|
/**
|
|
4
5
|
* This middleware implements retry logic with support for:
|
|
5
6
|
* - Configurable number of retry attempts
|
|
@@ -50,5 +51,5 @@ import type { RetryConfig, RetryContext, RetryDelayFn } from './types/retry';
|
|
|
50
51
|
* });
|
|
51
52
|
*/
|
|
52
53
|
declare function retryMiddleware(config?: RetryConfig): Middleware;
|
|
53
|
-
export { retryMiddleware };
|
|
54
|
+
export { HttpResponseError, isHttpResponseError, retryMiddleware };
|
|
54
55
|
export type { RetryConfig, RetryContext, RetryDelayFn };
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isHttpResponseError = exports.HttpResponseError = void 0;
|
|
3
4
|
exports.retryMiddleware = retryMiddleware;
|
|
5
|
+
const http_response_error_1 = require("./utils/http-response-error");
|
|
6
|
+
Object.defineProperty(exports, "HttpResponseError", { enumerable: true, get: function () { return http_response_error_1.HttpResponseError; } });
|
|
7
|
+
Object.defineProperty(exports, "isHttpResponseError", { enumerable: true, get: function () { return http_response_error_1.isHttpResponseError; } });
|
|
4
8
|
const is_network_error_1 = require("./utils/is-network-error");
|
|
5
9
|
const IDEMPOTENT_HTTP_METHODS = ['GET', 'HEAD', 'OPTIONS', 'PUT', 'DELETE'];
|
|
6
10
|
/**
|
|
@@ -85,14 +89,15 @@ function shouldRetryOnStatus(status, retryOn) {
|
|
|
85
89
|
return retryOn.includes(status);
|
|
86
90
|
}
|
|
87
91
|
/**
|
|
88
|
-
* Checks the response status and
|
|
92
|
+
* Checks the response status and throws HttpResponseError if it's not "ok".
|
|
89
93
|
*
|
|
90
94
|
* @param {Response} response - The HTTP response object.
|
|
91
|
-
* @
|
|
95
|
+
* @param {Request} request - The HTTP request object.
|
|
96
|
+
* @throws {HttpResponseError} When the response is not an "ok" response.
|
|
92
97
|
*/
|
|
93
|
-
function throwErrorIfNotOkResponse(response) {
|
|
98
|
+
function throwErrorIfNotOkResponse(response, request) {
|
|
94
99
|
if (!response.ok) {
|
|
95
|
-
throw new
|
|
100
|
+
throw new http_response_error_1.HttpResponseError(response, request);
|
|
96
101
|
}
|
|
97
102
|
}
|
|
98
103
|
/**
|
|
@@ -158,17 +163,17 @@ function retryMiddleware(config) {
|
|
|
158
163
|
const context = { attempt: 1, request, response, error: null };
|
|
159
164
|
// If retryOn is specified, only use that list
|
|
160
165
|
if (config?.retryOn && config.retryOn.length > 0) {
|
|
161
|
-
if (!shouldRetryOnStatus(
|
|
162
|
-
throwErrorIfNotOkResponse(
|
|
163
|
-
return
|
|
166
|
+
if (!shouldRetryOnStatus(response.status, config.retryOn)) {
|
|
167
|
+
throwErrorIfNotOkResponse(response, request);
|
|
168
|
+
return response;
|
|
164
169
|
}
|
|
165
170
|
return await performRetries(normalisedConfig, context);
|
|
166
171
|
}
|
|
167
172
|
// Otherwise, check if we should retry based on retry condition
|
|
168
173
|
const shouldRetry = await normalisedConfig.retryCondition(context, normalisedConfig.idempotentOnly);
|
|
169
174
|
if (!shouldRetry) {
|
|
170
|
-
throwErrorIfNotOkResponse(
|
|
171
|
-
return
|
|
175
|
+
throwErrorIfNotOkResponse(response, request);
|
|
176
|
+
return response;
|
|
172
177
|
}
|
|
173
178
|
return await performRetries(normalisedConfig, context);
|
|
174
179
|
},
|
|
@@ -214,18 +219,18 @@ async function performRetries(config, context) {
|
|
|
214
219
|
continue;
|
|
215
220
|
}
|
|
216
221
|
if (config.retryOn && !shouldRetryOnStatus(response?.status, config.retryOn)) {
|
|
217
|
-
throwErrorIfNotOkResponse(response);
|
|
222
|
+
throwErrorIfNotOkResponse(response, context.request);
|
|
218
223
|
return response;
|
|
219
224
|
}
|
|
220
225
|
const shouldRetry = await config.retryCondition(context, config.idempotentOnly);
|
|
221
226
|
if (!shouldRetry) {
|
|
222
|
-
throwErrorIfNotOkResponse(response);
|
|
227
|
+
throwErrorIfNotOkResponse(response, context.request);
|
|
223
228
|
return response;
|
|
224
229
|
}
|
|
225
230
|
} while (attempt <= maxRetries);
|
|
226
231
|
if (!response) {
|
|
227
232
|
throw context.error;
|
|
228
233
|
}
|
|
229
|
-
throwErrorIfNotOkResponse(response);
|
|
234
|
+
throwErrorIfNotOkResponse(response, context.request);
|
|
230
235
|
return response;
|
|
231
236
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom error class for HTTP response errors, similar to Axios Error.
|
|
3
|
+
* Provides detailed information about the failed request and response.
|
|
4
|
+
*/
|
|
5
|
+
export declare class HttpResponseError extends Error {
|
|
6
|
+
readonly name = "HttpResponseError";
|
|
7
|
+
readonly status: number;
|
|
8
|
+
readonly statusText: string;
|
|
9
|
+
readonly response: Response;
|
|
10
|
+
readonly request: Request;
|
|
11
|
+
readonly isHttpResponseError = true;
|
|
12
|
+
constructor(response: Response, request: Request);
|
|
13
|
+
/**
|
|
14
|
+
* Returns a JSON representation of the error for logging/debugging.
|
|
15
|
+
*/
|
|
16
|
+
toJson(): {
|
|
17
|
+
name: string;
|
|
18
|
+
message: string;
|
|
19
|
+
status: number;
|
|
20
|
+
statusText: string;
|
|
21
|
+
method: string;
|
|
22
|
+
url: string;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Type guard to check if an error is an HttpResponseError.
|
|
27
|
+
* Useful for narrowing error types in catch blocks.
|
|
28
|
+
*
|
|
29
|
+
* @param {unknown} error - The error to check.
|
|
30
|
+
* @returns {boolean} True if the error is an HttpResponseError.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* try {
|
|
34
|
+
* await fetchData();
|
|
35
|
+
* } catch (error) {
|
|
36
|
+
* if (isHttpResponseError(error)) {
|
|
37
|
+
* console.log(`Request failed with status ${error.status}`);
|
|
38
|
+
* console.log(`URL: ${error.request.url}`);
|
|
39
|
+
* }
|
|
40
|
+
* }
|
|
41
|
+
*/
|
|
42
|
+
export declare function isHttpResponseError(error: unknown): error is HttpResponseError;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HttpResponseError = void 0;
|
|
4
|
+
exports.isHttpResponseError = isHttpResponseError;
|
|
5
|
+
/**
|
|
6
|
+
* Custom error class for HTTP response errors, similar to Axios Error.
|
|
7
|
+
* Provides detailed information about the failed request and response.
|
|
8
|
+
*/
|
|
9
|
+
class HttpResponseError extends Error {
|
|
10
|
+
name = 'HttpResponseError';
|
|
11
|
+
status;
|
|
12
|
+
statusText;
|
|
13
|
+
response;
|
|
14
|
+
request;
|
|
15
|
+
isHttpResponseError = true;
|
|
16
|
+
constructor(response, request) {
|
|
17
|
+
super(`${response.status}: ${response.statusText}`);
|
|
18
|
+
this.status = response.status;
|
|
19
|
+
this.statusText = response.statusText;
|
|
20
|
+
this.response = response;
|
|
21
|
+
this.request = request;
|
|
22
|
+
// Maintains proper stack trace for where error was thrown (V8 engines)
|
|
23
|
+
if (Error.captureStackTrace) {
|
|
24
|
+
Error.captureStackTrace(this, HttpResponseError);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Returns a JSON representation of the error for logging/debugging.
|
|
29
|
+
*/
|
|
30
|
+
toJson() {
|
|
31
|
+
return {
|
|
32
|
+
name: this.name,
|
|
33
|
+
message: this.message,
|
|
34
|
+
status: this.status,
|
|
35
|
+
statusText: this.statusText,
|
|
36
|
+
method: this.request.method,
|
|
37
|
+
url: this.request.url,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
exports.HttpResponseError = HttpResponseError;
|
|
42
|
+
/**
|
|
43
|
+
* Type guard to check if an error is an HttpResponseError.
|
|
44
|
+
* Useful for narrowing error types in catch blocks.
|
|
45
|
+
*
|
|
46
|
+
* @param {unknown} error - The error to check.
|
|
47
|
+
* @returns {boolean} True if the error is an HttpResponseError.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* try {
|
|
51
|
+
* await fetchData();
|
|
52
|
+
* } catch (error) {
|
|
53
|
+
* if (isHttpResponseError(error)) {
|
|
54
|
+
* console.log(`Request failed with status ${error.status}`);
|
|
55
|
+
* console.log(`URL: ${error.request.url}`);
|
|
56
|
+
* }
|
|
57
|
+
* }
|
|
58
|
+
*/
|
|
59
|
+
function isHttpResponseError(error) {
|
|
60
|
+
return (error instanceof HttpResponseError ||
|
|
61
|
+
(typeof error === 'object' &&
|
|
62
|
+
error !== null &&
|
|
63
|
+
'isHttpResponseError' in error &&
|
|
64
|
+
error.isHttpResponseError === true));
|
|
65
|
+
}
|