lambda-essentials-ts 6.1.1 → 7.0.3
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/lib/exceptions/clientException.js +2 -1
- package/lib/httpClient/httpClient.d.ts +10 -6
- package/lib/httpClient/httpClient.js +56 -60
- package/lib/httpClient/redisStorage.d.ts +2 -0
- package/lib/httpClient/redisStorage.js +36 -0
- package/lib/index.d.ts +2 -1
- package/lib/index.js +20 -8
- package/lib/logger/logger.d.ts +9 -0
- package/lib/logger/logger.js +33 -19
- package/lib/openApi/openApiModel.d.ts +4 -4
- package/lib/openApi/openApiWrapper.js +25 -17
- package/lib/tokenProvider/kmsTokenProvider.js +4 -3
- package/lib/tokenProvider/secretsManagerTokenProvider.js +1 -1
- package/lib/tokenProvider/tokenProvider.js +12 -11
- package/lib/util.d.ts +1 -1
- package/lib/util.js +9 -12
- package/package.json +25 -22
- package/CHANGELOG.md +0 -271
- package/lib/httpClient/deduplicateRequestAdapter.d.ts +0 -2
- package/lib/httpClient/deduplicateRequestAdapter.js +0 -20
|
@@ -4,7 +4,7 @@ exports.ClientException = void 0;
|
|
|
4
4
|
const exception_1 = require("./exception");
|
|
5
5
|
class ClientException extends exception_1.Exception {
|
|
6
6
|
constructor(serviceName, originalStatusCode, details, originalMessage, statusCodeMapOverride) {
|
|
7
|
-
super(`Dependent service "${serviceName}" returned error: ${originalMessage
|
|
7
|
+
super(`Dependent service "${serviceName}" returned error: ${originalMessage ?? 'Unknown error'}`, ClientException.convertStatusCode(originalStatusCode, statusCodeMapOverride), details);
|
|
8
8
|
this.serviceName = serviceName;
|
|
9
9
|
this.originalStatusCode = originalStatusCode;
|
|
10
10
|
}
|
|
@@ -27,4 +27,5 @@ ClientException.statusCodeMap = {
|
|
|
27
27
|
403: 403,
|
|
28
28
|
404: 422,
|
|
29
29
|
422: 422,
|
|
30
|
+
429: 429,
|
|
30
31
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
|
|
2
|
-
import { IAxiosCacheAdapterOptions } from 'axios-cache-adapter';
|
|
1
|
+
import { AxiosHeaderValue, AxiosInstance, AxiosRequestConfig, AxiosResponse, RawAxiosRequestHeaders } from 'axios';
|
|
3
2
|
import { RetryConfig } from 'retry-axios';
|
|
3
|
+
import { CacheOptions } from 'axios-cache-interceptor';
|
|
4
4
|
/**
|
|
5
5
|
* Allows to specify which http data should be logged.
|
|
6
6
|
*/
|
|
@@ -27,7 +27,11 @@ export default class HttpClient {
|
|
|
27
27
|
/**
|
|
28
28
|
* Resolves the token with the token provider and adds it to the headers
|
|
29
29
|
*/
|
|
30
|
-
createHeadersWithResolvedToken(headers?:
|
|
30
|
+
createHeadersWithResolvedToken(headers?: RawAxiosRequestHeaders | {
|
|
31
|
+
[key: string]: AxiosHeaderValue;
|
|
32
|
+
} | Record<string, string>): Promise<{
|
|
33
|
+
[p: string]: AxiosHeaderValue;
|
|
34
|
+
}>;
|
|
31
35
|
/**
|
|
32
36
|
* Get from the given url. Bearer token is automatically injected if tokenResolverFunction was provided to the constructor.
|
|
33
37
|
*/
|
|
@@ -83,10 +87,10 @@ export interface HttpClientOptions {
|
|
|
83
87
|
*/
|
|
84
88
|
enableCache?: boolean;
|
|
85
89
|
/**
|
|
86
|
-
* Cache options
|
|
87
|
-
* @link https://
|
|
90
|
+
* Cache options (global defaults for axios-cache-interceptor)
|
|
91
|
+
* @link https://axios-cache-interceptor.js.org/config
|
|
88
92
|
*/
|
|
89
|
-
cacheOptions?:
|
|
93
|
+
cacheOptions?: CacheOptions;
|
|
90
94
|
/**
|
|
91
95
|
* Enable automatic retries
|
|
92
96
|
*/
|
|
@@ -15,29 +15,37 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
|
|
15
15
|
}) : function(o, v) {
|
|
16
16
|
o["default"] = v;
|
|
17
17
|
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
};
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
25
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
37
|
};
|
|
28
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
39
|
exports.HttpLogType = void 0;
|
|
30
40
|
const uuid = __importStar(require("uuid"));
|
|
31
|
-
const url_1 = require("url");
|
|
32
41
|
const axios_1 = __importDefault(require("axios"));
|
|
33
|
-
const axios_cache_adapter_1 = require("axios-cache-adapter");
|
|
34
42
|
const rax = __importStar(require("retry-axios"));
|
|
35
43
|
const md5_1 = __importDefault(require("md5"));
|
|
36
44
|
const util_1 = require("../util");
|
|
37
45
|
const internalException_1 = require("../exceptions/internalException");
|
|
38
46
|
const clientException_1 = require("../exceptions/clientException");
|
|
39
47
|
const shared_1 = require("../shared");
|
|
40
|
-
const
|
|
48
|
+
const axios_cache_interceptor_1 = require("axios-cache-interceptor");
|
|
41
49
|
const invalidToken = 'Invalid token';
|
|
42
50
|
/**
|
|
43
51
|
* Allows to specify which http data should be logged.
|
|
@@ -46,55 +54,47 @@ var HttpLogType;
|
|
|
46
54
|
(function (HttpLogType) {
|
|
47
55
|
HttpLogType["requests"] = "requests";
|
|
48
56
|
HttpLogType["responses"] = "responses";
|
|
49
|
-
})(HttpLogType
|
|
57
|
+
})(HttpLogType || (exports.HttpLogType = HttpLogType = {}));
|
|
50
58
|
class HttpClient {
|
|
51
59
|
/**
|
|
52
60
|
* Create a new Instance of the HttpClient
|
|
53
61
|
*/
|
|
54
62
|
// eslint-disable-next-line complexity
|
|
55
63
|
constructor(options) {
|
|
56
|
-
var _a, _b, _c, _d, _e;
|
|
57
64
|
// eslint-disable-next-line no-console
|
|
58
|
-
this.logFunction =
|
|
59
|
-
this.logOptions =
|
|
60
|
-
this.tokenResolverFunction = options
|
|
61
|
-
this.correlationIdResolverFunction = options
|
|
62
|
-
this.enableCache =
|
|
63
|
-
this.enableRetry =
|
|
64
|
-
this.timeout = options
|
|
65
|
-
this.clientExceptionStatusCodeMapOverride = options
|
|
66
|
-
this.client =
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
key: (req) => HttpClient.generateCacheKey(req),
|
|
80
|
-
});
|
|
81
|
-
// debounce concurrent calls with the same cacheKey so that only one HTTP request is made
|
|
82
|
-
adapters = (0, deduplicateRequestAdapter_1.createDebounceRequestAdapter)(cache.adapter, HttpClient.generateCacheKey);
|
|
83
|
-
}
|
|
84
|
-
return adapters;
|
|
85
|
-
})(),
|
|
65
|
+
this.logFunction = options?.logFunction ?? console.log;
|
|
66
|
+
this.logOptions = options?.logOptions ?? { enabledLogs: [HttpLogType.requests] };
|
|
67
|
+
this.tokenResolverFunction = options?.tokenResolver;
|
|
68
|
+
this.correlationIdResolverFunction = options?.correlationIdResolver;
|
|
69
|
+
this.enableCache = options?.enableCache ?? false;
|
|
70
|
+
this.enableRetry = options?.enableRetry ?? false;
|
|
71
|
+
this.timeout = options?.timeout;
|
|
72
|
+
this.clientExceptionStatusCodeMapOverride = options?.clientExceptionStatusCodeMapOverride;
|
|
73
|
+
this.client = options?.client ?? axios_1.default.create();
|
|
74
|
+
if (this.enableCache) {
|
|
75
|
+
(0, axios_cache_interceptor_1.setupCache)(this.client, {
|
|
76
|
+
// 5 minutes TTL
|
|
77
|
+
ttl: 5 * 60 * 1000,
|
|
78
|
+
// Respect cache-control headers and have ttl as a fallback https://axios-cache-interceptor.js.org/config/request-specifics#cache-interpretheader
|
|
79
|
+
interpretHeader: true,
|
|
80
|
+
// Serve stale cache on error
|
|
81
|
+
staleIfError: true,
|
|
82
|
+
// Use custom cache key to include auth, url, params and body hash
|
|
83
|
+
generateKey: (req) => HttpClient.generateCacheKey(req),
|
|
84
|
+
// Allow overriding defaults via provided options
|
|
85
|
+
...options?.cacheOptions,
|
|
86
86
|
});
|
|
87
|
+
}
|
|
87
88
|
if (this.enableRetry) {
|
|
88
89
|
this.client.defaults.raxConfig = {
|
|
89
|
-
...options
|
|
90
|
-
instance: this.client,
|
|
91
|
-
httpMethodsToRetry: ['GET', 'HEAD', 'OPTIONS', 'DELETE', 'PUT', 'POST'],
|
|
90
|
+
...options?.retryOptions,
|
|
91
|
+
instance: this.client, // always attach retry-axios to the private axios client
|
|
92
|
+
httpMethodsToRetry: ['GET', 'HEAD', 'OPTIONS', 'DELETE', 'PUT', 'POST'], // extending the defaults to retry POST calls
|
|
92
93
|
onRetryAttempt: (err) => {
|
|
93
|
-
var _a, _b;
|
|
94
94
|
this.logFunction({
|
|
95
95
|
title: 'HTTP Response Error Retry',
|
|
96
96
|
level: 'INFO',
|
|
97
|
-
retryAttempt:
|
|
97
|
+
retryAttempt: err.config?.raxConfig?.currentRetryAttempt,
|
|
98
98
|
...HttpClient.extractRequestLogData(err.config),
|
|
99
99
|
error: (0, util_1.serializeAxiosError)(err),
|
|
100
100
|
});
|
|
@@ -119,7 +119,6 @@ class HttpClient {
|
|
|
119
119
|
}
|
|
120
120
|
return config;
|
|
121
121
|
}, (error) => {
|
|
122
|
-
var _a;
|
|
123
122
|
const serializedAxiosError = (0, util_1.serializeAxiosError)(error);
|
|
124
123
|
this.logFunction({
|
|
125
124
|
title: 'HTTP Request Error',
|
|
@@ -127,10 +126,10 @@ class HttpClient {
|
|
|
127
126
|
...HttpClient.extractRequestLogData(error.config),
|
|
128
127
|
error: serializedAxiosError,
|
|
129
128
|
});
|
|
130
|
-
const hostname =
|
|
131
|
-
? new
|
|
129
|
+
const hostname = error.config?.url
|
|
130
|
+
? new URL(error.config.url, error.config.baseURL).hostname
|
|
132
131
|
: 'N/A';
|
|
133
|
-
throw new clientException_1.ClientException(hostname, serializedAxiosError
|
|
132
|
+
throw new clientException_1.ClientException(hostname, serializedAxiosError?.status, serializedAxiosError?.details, serializedAxiosError?.message, this.clientExceptionStatusCodeMapOverride);
|
|
134
133
|
});
|
|
135
134
|
this.client.interceptors.response.use((response) => {
|
|
136
135
|
if (this.logOptions.enabledLogs.includes(HttpLogType.responses)) {
|
|
@@ -143,7 +142,6 @@ class HttpClient {
|
|
|
143
142
|
}
|
|
144
143
|
return response;
|
|
145
144
|
}, (error) => {
|
|
146
|
-
var _a;
|
|
147
145
|
// when retries are configured, this middleware gets triggered for each retry
|
|
148
146
|
// it changes the error object to ClientException and therefore the transformation can be run only once
|
|
149
147
|
if (error instanceof clientException_1.ClientException) {
|
|
@@ -166,14 +164,13 @@ class HttpClient {
|
|
|
166
164
|
error: serializedAxiosError,
|
|
167
165
|
});
|
|
168
166
|
}
|
|
169
|
-
const hostname =
|
|
170
|
-
? new
|
|
167
|
+
const hostname = error.config?.url
|
|
168
|
+
? new URL(error.config.url, error.config.baseURL).hostname
|
|
171
169
|
: 'N/A';
|
|
172
|
-
throw new clientException_1.ClientException(hostname, serializedAxiosError
|
|
170
|
+
throw new clientException_1.ClientException(hostname, serializedAxiosError?.status, serializedAxiosError?.details, serializedAxiosError?.message, this.clientExceptionStatusCodeMapOverride);
|
|
173
171
|
});
|
|
174
172
|
}
|
|
175
173
|
static extractRequestLogData(requestConfig) {
|
|
176
|
-
var _a;
|
|
177
174
|
if (!requestConfig) {
|
|
178
175
|
return {};
|
|
179
176
|
}
|
|
@@ -182,14 +179,13 @@ class HttpClient {
|
|
|
182
179
|
url: requestConfig.url,
|
|
183
180
|
query: requestConfig.params,
|
|
184
181
|
request: (0, util_1.safeJsonParse)(requestConfig.data, requestConfig.data),
|
|
185
|
-
correlationId:
|
|
182
|
+
correlationId: requestConfig.headers?.[shared_1.orionCorrelationIdRoot],
|
|
186
183
|
};
|
|
187
184
|
}
|
|
188
185
|
// implemented based on https://github.com/RasCarlito/axios-cache-adapter/blob/master/src/cache.js#L77
|
|
189
186
|
static generateCacheKey(req) {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
? (_b = (0, util_1.safeJwtCanonicalIdParse)(req.headers.Authorization.replace('Bearer ', ''))) !== null && _b !== void 0 ? _b : uuid.v4()
|
|
187
|
+
const prefix = req.headers?.Authorization
|
|
188
|
+
? (0, util_1.safeJwtCanonicalIdParse)(req.headers.Authorization.replace('Bearer ', '')) ?? uuid.v4()
|
|
193
189
|
: 'shared';
|
|
194
190
|
const url = `${req.baseURL ? req.baseURL : ''}${req.url}`;
|
|
195
191
|
const query = req.params ? JSON.stringify(req.params) : ''; // possible improvement: optimize cache-hit ratio by sorting the query params
|
|
@@ -199,13 +195,13 @@ class HttpClient {
|
|
|
199
195
|
/**
|
|
200
196
|
* Resolves the token with the token provider and adds it to the headers
|
|
201
197
|
*/
|
|
202
|
-
async createHeadersWithResolvedToken(headers
|
|
198
|
+
async createHeadersWithResolvedToken(headers) {
|
|
203
199
|
const newHeaders = {};
|
|
204
200
|
if (this.correlationIdResolverFunction) {
|
|
205
201
|
newHeaders[shared_1.orionCorrelationIdRoot] = this.correlationIdResolverFunction();
|
|
206
202
|
}
|
|
207
203
|
if (this.tokenResolverFunction) {
|
|
208
|
-
if (headers.Authorization) {
|
|
204
|
+
if (headers && headers.Authorization) {
|
|
209
205
|
throw new internalException_1.InternalException('Authorization header already specified, please create a new HttpClient with a different (or without a) tokenResolver');
|
|
210
206
|
}
|
|
211
207
|
else {
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = createRedisStorage;
|
|
4
|
+
const axios_cache_interceptor_1 = require("axios-cache-interceptor");
|
|
5
|
+
const KEY_PREFIX = 'axios-cache-';
|
|
6
|
+
const MIN_TTL = 60000;
|
|
7
|
+
function createRedisStorage(client) {
|
|
8
|
+
// source https://axios-cache-interceptor.js.org/guide/storages#node-redis-storage
|
|
9
|
+
return (0, axios_cache_interceptor_1.buildStorage)({
|
|
10
|
+
async find(key) {
|
|
11
|
+
const result = await client.get(`${KEY_PREFIX}${key}`);
|
|
12
|
+
return result ? JSON.parse(result) : undefined;
|
|
13
|
+
},
|
|
14
|
+
// eslint-disable-next-line complexity
|
|
15
|
+
async set(key, value, req) {
|
|
16
|
+
await client.set(`${KEY_PREFIX}${key}`, JSON.stringify(value), {
|
|
17
|
+
PXAT:
|
|
18
|
+
// We don't want to keep indefinitely values in the storage if
|
|
19
|
+
// their request don't finish somehow. Either set its value as
|
|
20
|
+
// the TTL or 1 minute (MIN_TTL).
|
|
21
|
+
value.state === 'loading'
|
|
22
|
+
? Date.now() +
|
|
23
|
+
(req?.cache && typeof req.cache.ttl === 'number' ? req.cache.ttl : MIN_TTL)
|
|
24
|
+
: (value.state === 'stale' && value.ttl) ||
|
|
25
|
+
(value.state === 'cached' && !(0, axios_cache_interceptor_1.canStale)(value))
|
|
26
|
+
? value.createdAt + value.ttl
|
|
27
|
+
: // otherwise, we can't determine when it should expire, so we keep
|
|
28
|
+
// it indefinitely.
|
|
29
|
+
undefined,
|
|
30
|
+
});
|
|
31
|
+
},
|
|
32
|
+
async remove(key) {
|
|
33
|
+
await client.del(`${KEY_PREFIX}${key}`);
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
}
|
package/lib/index.d.ts
CHANGED
|
@@ -15,5 +15,6 @@ import { InvalidDataException } from './exceptions/invalidDataException';
|
|
|
15
15
|
import { NotFoundException } from './exceptions/notFoundException';
|
|
16
16
|
import { ValidationException } from './exceptions/validationException';
|
|
17
17
|
import { serializeObject, serializeAxiosError } from './util';
|
|
18
|
-
|
|
18
|
+
import RedisStorage from './httpClient/redisStorage';
|
|
19
|
+
export { Logger, OpenApiWrapper, ApiResponse, HttpClient, HttpLogType, KmsTokenProvider, SecretsManagerTokenProvider, ValidationException, NotFoundException, InvalidDataException, InternalException, ForbiddenException, Exception, ConflictException, ClientException, serializeObject, serializeAxiosError, RedisStorage, };
|
|
19
20
|
export type { LoggerConfiguration, SuggestedLogObject, HttpClientOptions, HttpLogOptions, ApiRequest, GetRequest, PostRequest, PutRequest, DeleteRequest, TokenProviderHttpClient, KmsTokenProviderOptions, KmsTokenConfiguration, SecretsManagerTokenProviderOptions, SecretsManagerTokenConfiguration, };
|
package/lib/index.js
CHANGED
|
@@ -15,18 +15,28 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
|
|
15
15
|
}) : function(o, v) {
|
|
16
16
|
o["default"] = v;
|
|
17
17
|
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
};
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
25
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
37
|
};
|
|
28
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
-
exports.serializeAxiosError = exports.serializeObject = exports.ClientException = exports.ConflictException = exports.Exception = exports.ForbiddenException = exports.InternalException = exports.InvalidDataException = exports.NotFoundException = exports.ValidationException = exports.SecretsManagerTokenProvider = exports.KmsTokenProvider = exports.HttpLogType = exports.HttpClient = exports.ApiResponse = exports.OpenApiWrapper = exports.Logger = void 0;
|
|
39
|
+
exports.RedisStorage = exports.serializeAxiosError = exports.serializeObject = exports.ClientException = exports.ConflictException = exports.Exception = exports.ForbiddenException = exports.InternalException = exports.InvalidDataException = exports.NotFoundException = exports.ValidationException = exports.SecretsManagerTokenProvider = exports.KmsTokenProvider = exports.HttpLogType = exports.HttpClient = exports.ApiResponse = exports.OpenApiWrapper = exports.Logger = void 0;
|
|
30
40
|
const httpClient_1 = __importStar(require("./httpClient/httpClient"));
|
|
31
41
|
exports.HttpClient = httpClient_1.default;
|
|
32
42
|
Object.defineProperty(exports, "HttpLogType", { enumerable: true, get: function () { return httpClient_1.HttpLogType; } });
|
|
@@ -59,3 +69,5 @@ Object.defineProperty(exports, "ValidationException", { enumerable: true, get: f
|
|
|
59
69
|
const util_1 = require("./util");
|
|
60
70
|
Object.defineProperty(exports, "serializeObject", { enumerable: true, get: function () { return util_1.serializeObject; } });
|
|
61
71
|
Object.defineProperty(exports, "serializeAxiosError", { enumerable: true, get: function () { return util_1.serializeAxiosError; } });
|
|
72
|
+
const redisStorage_1 = __importDefault(require("./httpClient/redisStorage"));
|
|
73
|
+
exports.RedisStorage = redisStorage_1.default;
|
package/lib/logger/logger.d.ts
CHANGED
|
@@ -2,11 +2,13 @@ export default class Logger {
|
|
|
2
2
|
invocationId: string;
|
|
3
3
|
private readonly logFunction;
|
|
4
4
|
private readonly jsonSpace;
|
|
5
|
+
private readonly payloadLimit;
|
|
5
6
|
private staticData;
|
|
6
7
|
constructor(configuration?: LoggerConfiguration);
|
|
7
8
|
/**
|
|
8
9
|
* Create a new invocation which will end up setting the additional invocation metadata for the request, which will be used when logging.
|
|
9
10
|
* @param staticData Any static data that are assigned to every log message. Typical might be an environment parameter or version number.
|
|
11
|
+
* @param invocationId
|
|
10
12
|
*/
|
|
11
13
|
startInvocation(staticData?: any, invocationId?: string): void;
|
|
12
14
|
log(message: string | SuggestedLogObject): void;
|
|
@@ -21,6 +23,13 @@ export interface LoggerConfiguration {
|
|
|
21
23
|
* the number of spaces that are used then stringifying the message.
|
|
22
24
|
*/
|
|
23
25
|
jsonSpace?: number;
|
|
26
|
+
/**
|
|
27
|
+
* the limit of a stringified payload in characters above which the payload will be truncated.
|
|
28
|
+
* 3000 characters are reserved
|
|
29
|
+
* @min 10000
|
|
30
|
+
* @default 32768
|
|
31
|
+
*/
|
|
32
|
+
payloadLimit?: number;
|
|
24
33
|
}
|
|
25
34
|
export interface SuggestedLogObject {
|
|
26
35
|
/**
|
package/lib/logger/logger.js
CHANGED
|
@@ -16,13 +16,23 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
|
|
16
16
|
}) : function(o, v) {
|
|
17
17
|
o["default"] = v;
|
|
18
18
|
});
|
|
19
|
-
var __importStar = (this && this.__importStar) || function (
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
};
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
26
36
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
27
37
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
28
38
|
};
|
|
@@ -31,20 +41,27 @@ const uuid = __importStar(require("uuid"));
|
|
|
31
41
|
const fast_safe_stringify_1 = __importDefault(require("fast-safe-stringify"));
|
|
32
42
|
const is_error_1 = __importDefault(require("is-error"));
|
|
33
43
|
const util_1 = require("../util");
|
|
44
|
+
const DEFAULT_PAYLOAD_LIMIT = 32768;
|
|
45
|
+
const MIN_PAYLOAD_LIMIT = 10000;
|
|
34
46
|
class Logger {
|
|
35
47
|
constructor(configuration) {
|
|
36
|
-
|
|
37
|
-
this.
|
|
38
|
-
this.
|
|
48
|
+
this.logFunction = configuration?.logFunction ?? console.log;
|
|
49
|
+
this.jsonSpace = configuration?.jsonSpace ?? 2;
|
|
50
|
+
this.payloadLimit = !configuration?.payloadLimit
|
|
51
|
+
? DEFAULT_PAYLOAD_LIMIT
|
|
52
|
+
: configuration?.payloadLimit < MIN_PAYLOAD_LIMIT
|
|
53
|
+
? MIN_PAYLOAD_LIMIT
|
|
54
|
+
: configuration.payloadLimit;
|
|
39
55
|
this.invocationId = 'none';
|
|
40
56
|
}
|
|
41
57
|
/**
|
|
42
58
|
* Create a new invocation which will end up setting the additional invocation metadata for the request, which will be used when logging.
|
|
43
59
|
* @param staticData Any static data that are assigned to every log message. Typical might be an environment parameter or version number.
|
|
60
|
+
* @param invocationId
|
|
44
61
|
*/
|
|
45
62
|
startInvocation(staticData, invocationId) {
|
|
46
63
|
this.staticData = staticData;
|
|
47
|
-
this.invocationId = invocationId
|
|
64
|
+
this.invocationId = invocationId ?? uuid.v4();
|
|
48
65
|
}
|
|
49
66
|
// eslint-disable-next-line complexity
|
|
50
67
|
log(message) {
|
|
@@ -71,7 +88,7 @@ class Logger {
|
|
|
71
88
|
}
|
|
72
89
|
const payload = {
|
|
73
90
|
invocationId: this.invocationId,
|
|
74
|
-
|
|
91
|
+
...messageAsObject,
|
|
75
92
|
};
|
|
76
93
|
const truncateToken = (innerPayload) => {
|
|
77
94
|
return innerPayload.replace(/(eyJ[a-zA-Z0-9_-]{5,}\.eyJ[a-zA-Z0-9_-]{5,})\.[a-zA-Z0-9_-]*/gi, (m, p1) => `${p1}.<sig>`);
|
|
@@ -79,15 +96,12 @@ class Logger {
|
|
|
79
96
|
const replacer = (key, value) => ((0, is_error_1.default)(value) ? Logger.errorToObject(value) : value);
|
|
80
97
|
let stringifiedPayload = truncateToken((0, fast_safe_stringify_1.default)(payload, replacer, this.jsonSpace));
|
|
81
98
|
stringifiedPayload = (0, util_1.redactSecret)(stringifiedPayload);
|
|
82
|
-
|
|
83
|
-
if (stringifiedPayload.length >= 32768) {
|
|
99
|
+
if (stringifiedPayload.length >= this.payloadLimit) {
|
|
84
100
|
const replacementPayload = {
|
|
85
101
|
invocationId: this.invocationId,
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
truncatedPayload: stringifiedPayload.substring(0, 10000),
|
|
90
|
-
},
|
|
102
|
+
title: 'Payload too large',
|
|
103
|
+
fields: Object.keys(payload),
|
|
104
|
+
truncatedPayload: stringifiedPayload.substring(0, this.payloadLimit - 3000),
|
|
91
105
|
};
|
|
92
106
|
stringifiedPayload = (0, fast_safe_stringify_1.default)(replacementPayload, replacer, this.jsonSpace);
|
|
93
107
|
}
|
|
@@ -23,7 +23,7 @@ export interface OpenApiModel {
|
|
|
23
23
|
errorMiddleware: ErrorMiddleware;
|
|
24
24
|
}, overrideLogger?: () => void): any;
|
|
25
25
|
}
|
|
26
|
-
export
|
|
27
|
-
export
|
|
28
|
-
export
|
|
29
|
-
export
|
|
26
|
+
export type ApiControllerRoute<T = ApiRequest> = (path: string, handler: (request: T) => Promise<ApiResponse>) => void;
|
|
27
|
+
export type RequestMiddleware = (request: ApiRequest) => ApiRequest;
|
|
28
|
+
export type ResponseMiddleware = (request: ApiRequest, response: ApiResponse) => ApiResponse;
|
|
29
|
+
export type ErrorMiddleware = (request: ApiRequest, error: Exception | Error) => ApiResponse;
|
|
@@ -15,13 +15,23 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
|
|
15
15
|
}) : function(o, v) {
|
|
16
16
|
o["default"] = v;
|
|
17
17
|
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
};
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
25
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
37
|
};
|
|
@@ -40,19 +50,18 @@ class OpenApiWrapper {
|
|
|
40
50
|
this.userPrincipal = this.notSet;
|
|
41
51
|
this.requestId = this.notSet;
|
|
42
52
|
this.correlationId = this.notSet;
|
|
43
|
-
if (config
|
|
53
|
+
if (config?.enableNewRelicTracking) {
|
|
44
54
|
// eslint-disable-next-line global-require
|
|
45
55
|
this.newrelic = require('newrelic');
|
|
46
56
|
}
|
|
47
57
|
// @ts-ignore Later Use the options Type from OpenApiFactory
|
|
48
58
|
this.api = new openapi_factory_1.default({
|
|
49
59
|
requestMiddleware: async (request) => {
|
|
50
|
-
var _a, _b;
|
|
51
60
|
const correlationId = this.generateCorrelationId(request.headers);
|
|
52
61
|
requestLogger.startInvocation(null, correlationId);
|
|
53
62
|
const userData = this.determineUserData(request.headers, request.requestContext.authorizer);
|
|
54
|
-
this.userToken =
|
|
55
|
-
this.userPrincipal =
|
|
63
|
+
this.userToken = userData.userToken ?? this.notSet;
|
|
64
|
+
this.userPrincipal = userData.userPrincipal ?? this.notSet;
|
|
56
65
|
this.requestId = request.requestContext.requestId;
|
|
57
66
|
requestLogger.log({
|
|
58
67
|
title: 'RequestLogger',
|
|
@@ -69,7 +78,7 @@ class OpenApiWrapper {
|
|
|
69
78
|
user: this.userPrincipal,
|
|
70
79
|
query: request.multiValueQueryStringParameters,
|
|
71
80
|
});
|
|
72
|
-
if (config
|
|
81
|
+
if (config?.enableNewRelicTracking) {
|
|
73
82
|
this.newrelic.addCustomAttributes({
|
|
74
83
|
canonicalId: this.userPrincipal,
|
|
75
84
|
correlationId,
|
|
@@ -141,19 +150,18 @@ class OpenApiWrapper {
|
|
|
141
150
|
}
|
|
142
151
|
generateCorrelationId(headers) {
|
|
143
152
|
const existingCorrelationId = headers[shared_1.orionCorrelationIdRoot];
|
|
144
|
-
this.correlationId = existingCorrelationId
|
|
153
|
+
this.correlationId = existingCorrelationId ?? uuid.v4();
|
|
145
154
|
return this.correlationId;
|
|
146
155
|
}
|
|
147
156
|
determineUserData(headers, authorizerContext) {
|
|
148
|
-
var _a, _b, _c;
|
|
149
157
|
if (authorizerContext) {
|
|
150
158
|
return {
|
|
151
|
-
userPrincipal:
|
|
152
|
-
userToken:
|
|
159
|
+
userPrincipal: authorizerContext.canonicalId ?? authorizerContext.principalId,
|
|
160
|
+
userToken: authorizerContext.jwt ?? authorizerContext.accessToken,
|
|
153
161
|
};
|
|
154
162
|
}
|
|
155
163
|
if (headers.Authorization) {
|
|
156
|
-
const userToken =
|
|
164
|
+
const userToken = headers.Authorization.split(' ')?.[1];
|
|
157
165
|
const userPrincipal = (0, util_1.safeJwtCanonicalIdParse)(userToken);
|
|
158
166
|
return { userToken, userPrincipal };
|
|
159
167
|
}
|
|
@@ -12,11 +12,12 @@ class KmsTokenProvider extends tokenProvider_1.default {
|
|
|
12
12
|
this.kmsConfiguration = options.tokenConfiguration;
|
|
13
13
|
}
|
|
14
14
|
async getClientSecret() {
|
|
15
|
-
var _a;
|
|
16
15
|
const data = await this.kmsClient.send(new client_kms_1.DecryptCommand({
|
|
17
|
-
CiphertextBlob: Buffer.from(this.kmsConfiguration.encryptedClientSecret, 'base64'),
|
|
16
|
+
CiphertextBlob: new Uint8Array(Buffer.from(this.kmsConfiguration.encryptedClientSecret, 'base64')),
|
|
18
17
|
}));
|
|
19
|
-
const secret =
|
|
18
|
+
const secret = data.Plaintext
|
|
19
|
+
? Buffer.from(data.Plaintext).toString()
|
|
20
|
+
: undefined;
|
|
20
21
|
if (!secret) {
|
|
21
22
|
throw new Error('Request error: failed to decrypt secret using KMS');
|
|
22
23
|
}
|
|
@@ -15,7 +15,7 @@ class SecretsManagerTokenProvider extends tokenProvider_1.default {
|
|
|
15
15
|
const secret = await this.secretsManagerClient.send(new client_secrets_manager_1.GetSecretValueCommand({
|
|
16
16
|
SecretId: this.secretsManagerConfiguration.clientSecretId,
|
|
17
17
|
}));
|
|
18
|
-
if (!
|
|
18
|
+
if (!secret?.SecretString) {
|
|
19
19
|
throw new Error('Request error: failed to retrieve secret from Secrets Manager');
|
|
20
20
|
}
|
|
21
21
|
return JSON.parse(secret.SecretString);
|
|
@@ -5,27 +5,28 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
7
7
|
const httpClient_1 = __importDefault(require("../httpClient/httpClient"));
|
|
8
|
+
const logger_1 = __importDefault(require("../logger/logger"));
|
|
8
9
|
class TokenProvider {
|
|
9
10
|
/**
|
|
10
11
|
* Create a new Instance of the TokenProvider
|
|
11
12
|
*/
|
|
12
13
|
constructor(options) {
|
|
13
|
-
var _a;
|
|
14
14
|
this.httpClient =
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
options.httpClient ??
|
|
16
|
+
new httpClient_1.default({
|
|
17
|
+
logFunction: (msg) => new logger_1.default().log(msg),
|
|
18
|
+
enableCache: false,
|
|
19
|
+
enableRetry: true,
|
|
20
|
+
retryOptions: {
|
|
21
|
+
retry: 3,
|
|
22
|
+
},
|
|
23
|
+
});
|
|
22
24
|
this.configuration = options.tokenConfiguration;
|
|
23
25
|
}
|
|
24
26
|
/**
|
|
25
27
|
* Get access token. Subsequent calls are cached and the token is renewed only if it is expired.
|
|
26
28
|
*/
|
|
27
29
|
async getToken() {
|
|
28
|
-
var _a;
|
|
29
30
|
if (!this.currentTokenPromise) {
|
|
30
31
|
this.currentTokenPromise = this.getTokenWithoutCache();
|
|
31
32
|
return this.currentTokenPromise;
|
|
@@ -33,7 +34,7 @@ class TokenProvider {
|
|
|
33
34
|
try {
|
|
34
35
|
const jwtToken = await this.currentTokenPromise;
|
|
35
36
|
// lower the token expiry time by 10s so that the returned token will be not immediately expired
|
|
36
|
-
if (!jwtToken ||
|
|
37
|
+
if (!jwtToken || jsonwebtoken_1.default.decode(jwtToken)?.exp < Date.now() / 1000 - 10) {
|
|
37
38
|
this.currentTokenPromise = this.getTokenWithoutCache();
|
|
38
39
|
}
|
|
39
40
|
return await this.currentTokenPromise;
|
|
@@ -48,7 +49,7 @@ class TokenProvider {
|
|
|
48
49
|
*/
|
|
49
50
|
async getTokenWithoutCache() {
|
|
50
51
|
const secret = await this.getClientSecret();
|
|
51
|
-
if (!
|
|
52
|
+
if (!secret?.Auth0ClientID || !secret.Auth0ClientSecret) {
|
|
52
53
|
throw new Error('Request error: failed to retrieve Auth0 Client ID/Secret');
|
|
53
54
|
}
|
|
54
55
|
const headers = { 'Content-Type': 'application/json' };
|
package/lib/util.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export declare function safeJwtCanonicalIdParse(jwtToken: string): string | unde
|
|
|
3
3
|
export declare function safeJsonParse(input: any, defaultValue: unknown): unknown;
|
|
4
4
|
export declare const redactSecret: (data: string) => string;
|
|
5
5
|
export declare function serializeObject(obj: unknown, redact?: boolean): object;
|
|
6
|
-
export declare function serializeAxiosError(error: AxiosError): SerializedAxiosError | undefined;
|
|
6
|
+
export declare function serializeAxiosError(error: AxiosError<any>): SerializedAxiosError | undefined;
|
|
7
7
|
export interface SerializedAxiosError {
|
|
8
8
|
status: number;
|
|
9
9
|
details: any;
|
package/lib/util.js
CHANGED
|
@@ -3,18 +3,20 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
6
|
+
exports.redactSecret = void 0;
|
|
7
|
+
exports.safeJwtCanonicalIdParse = safeJwtCanonicalIdParse;
|
|
8
|
+
exports.safeJsonParse = safeJsonParse;
|
|
9
|
+
exports.serializeObject = serializeObject;
|
|
10
|
+
exports.serializeAxiosError = serializeAxiosError;
|
|
7
11
|
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
8
12
|
function safeJwtCanonicalIdParse(jwtToken) {
|
|
9
|
-
var _a;
|
|
10
13
|
try {
|
|
11
|
-
return
|
|
14
|
+
return jsonwebtoken_1.default.decode(jwtToken)?.['https://claims.cimpress.io/canonical_id'];
|
|
12
15
|
}
|
|
13
|
-
catch
|
|
16
|
+
catch {
|
|
14
17
|
return undefined;
|
|
15
18
|
}
|
|
16
19
|
}
|
|
17
|
-
exports.safeJwtCanonicalIdParse = safeJwtCanonicalIdParse;
|
|
18
20
|
function safeJsonParse(input, defaultValue) {
|
|
19
21
|
try {
|
|
20
22
|
return JSON.parse(input);
|
|
@@ -23,7 +25,6 @@ function safeJsonParse(input, defaultValue) {
|
|
|
23
25
|
return defaultValue;
|
|
24
26
|
}
|
|
25
27
|
}
|
|
26
|
-
exports.safeJsonParse = safeJsonParse;
|
|
27
28
|
const redactSecret = (data) => {
|
|
28
29
|
return data.replace(/(\\*"*'*client_secret\\*"*'*:\s*\\*"*'*)([^"'\\]+)(\\*"*'*)/gi, (m, p1, p2, p3) => `${p1}<REDACTED>${p3}`);
|
|
29
30
|
};
|
|
@@ -32,7 +33,6 @@ function serializeObject(obj, redact) {
|
|
|
32
33
|
let modObj = obj;
|
|
33
34
|
if (obj && typeof obj === 'object') {
|
|
34
35
|
modObj = Object.getOwnPropertyNames(obj).reduce((map, key) => {
|
|
35
|
-
// eslint-disable-next-line no-param-reassign
|
|
36
36
|
map[key] = obj[key];
|
|
37
37
|
return map;
|
|
38
38
|
}, {});
|
|
@@ -41,9 +41,7 @@ function serializeObject(obj, redact) {
|
|
|
41
41
|
? JSON.parse((0, exports.redactSecret)(JSON.stringify(modObj)))
|
|
42
42
|
: JSON.parse(JSON.stringify(modObj));
|
|
43
43
|
}
|
|
44
|
-
exports.serializeObject = serializeObject;
|
|
45
44
|
function serializeAxiosError(error) {
|
|
46
|
-
var _a;
|
|
47
45
|
if (!error.response) {
|
|
48
46
|
return {
|
|
49
47
|
status: 500,
|
|
@@ -52,9 +50,8 @@ function serializeAxiosError(error) {
|
|
|
52
50
|
}
|
|
53
51
|
const { status, data } = error.response;
|
|
54
52
|
return {
|
|
55
|
-
status:
|
|
56
|
-
details: data.details && Object.keys(data.details).length > 0 ? data.details : data,
|
|
53
|
+
status: data.originalStatusCode ?? status, // Propagate original status code of ClientException
|
|
54
|
+
details: data.details && Object.keys(data.details).length > 0 ? data.details : data, // Prevent wrapping of Exception
|
|
57
55
|
message: data.message,
|
|
58
56
|
};
|
|
59
57
|
}
|
|
60
|
-
exports.serializeAxiosError = serializeAxiosError;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lambda-essentials-ts",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.3",
|
|
4
4
|
"description": "A selection of the finest modules supporting authorization, API routing, error handling, logging and sending HTTP requests.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"private": false,
|
|
@@ -26,30 +26,33 @@
|
|
|
26
26
|
},
|
|
27
27
|
"homepage": "https://github.com/Cimpress-MCP/lambda-essentials-ts#readme",
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@aws-sdk/client-kms": "^3.
|
|
30
|
-
"@aws-sdk/client-secrets-manager": "^3.
|
|
31
|
-
"
|
|
32
|
-
"axios
|
|
33
|
-
"
|
|
29
|
+
"@aws-sdk/client-kms": "^3.930.0",
|
|
30
|
+
"@aws-sdk/client-secrets-manager": "^3.930.0",
|
|
31
|
+
"@types/node": "^24.10.1",
|
|
32
|
+
"axios": "1.13.2",
|
|
33
|
+
"axios-cache-interceptor": "^1.8.3",
|
|
34
|
+
"fast-safe-stringify": "~2.1.1",
|
|
34
35
|
"is-error": "~2.2.2",
|
|
35
|
-
"jsonwebtoken": "9.0.
|
|
36
|
+
"jsonwebtoken": "9.0.2",
|
|
36
37
|
"md5": "~2.3.0",
|
|
37
38
|
"openapi-factory": "5.4.60",
|
|
38
|
-
"retry-axios": "~2.
|
|
39
|
-
"uuid": "~
|
|
39
|
+
"retry-axios": "~3.2.1",
|
|
40
|
+
"uuid": "~11.1.0"
|
|
40
41
|
},
|
|
41
42
|
"devDependencies": {
|
|
42
|
-
"@types/jest": "^
|
|
43
|
-
"@types/newrelic": "^9.14.
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
43
|
+
"@types/jest": "^29.5.14",
|
|
44
|
+
"@types/newrelic": "^9.14.8",
|
|
45
|
+
"redis": "^5.9.0",
|
|
46
|
+
"axios-mock-adapter": "^2.1.0",
|
|
47
|
+
"eslint": "^8.57.1",
|
|
48
|
+
"eslint-config-cimpress-atsquad": "^2.2.0-beta",
|
|
49
|
+
"husky": "^9.1.7",
|
|
50
|
+
"jest": "^29.7.0",
|
|
51
|
+
"lint-staged": "^15.5.2",
|
|
52
|
+
"prettier": "^2.8.8",
|
|
53
|
+
"ts-jest": "^29.4.5",
|
|
54
|
+
"ts-node": "^10.9.2",
|
|
55
|
+
"typescript": "^5.9.3"
|
|
53
56
|
},
|
|
54
57
|
"eslintConfig": {
|
|
55
58
|
"extends": "cimpress-atsquad"
|
|
@@ -74,9 +77,9 @@
|
|
|
74
77
|
"collectCoverage": false
|
|
75
78
|
},
|
|
76
79
|
"engines": {
|
|
77
|
-
"node": ">=
|
|
80
|
+
"node": ">=20.0.0"
|
|
78
81
|
},
|
|
79
82
|
"peerDependencies": {
|
|
80
|
-
"newrelic": "^
|
|
83
|
+
"newrelic": "^12.0.0"
|
|
81
84
|
}
|
|
82
85
|
}
|
package/CHANGELOG.md
DELETED
|
@@ -1,271 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
All notable changes to this project will be documented in this file.
|
|
4
|
-
|
|
5
|
-
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
|
6
|
-
|
|
7
|
-
## [6.1.1] - 2025-10-27
|
|
8
|
-
|
|
9
|
-
### Changed
|
|
10
|
-
|
|
11
|
-
- TokenProviderHttpClient now uses the http client with retries as default.
|
|
12
|
-
|
|
13
|
-
## [6.1.0] - 2024-09-30
|
|
14
|
-
|
|
15
|
-
### Changed
|
|
16
|
-
|
|
17
|
-
- Preserve the original message in ClientException
|
|
18
|
-
|
|
19
|
-
## [6.0.0] - 2024-02-22
|
|
20
|
-
|
|
21
|
-
### Changed
|
|
22
|
-
|
|
23
|
-
- **[Breaking change]** Upgraded aws-sdk to v3 which has `SecretsManager` and `KMS` replaced by `SecretsManagerClient` and `KMSClient` class.
|
|
24
|
-
The functionality and interface remains the same, the imports need to be changed.
|
|
25
|
-
|
|
26
|
-
## [5.4.0] - 2024-02-08
|
|
27
|
-
|
|
28
|
-
### Added
|
|
29
|
-
|
|
30
|
-
HttpClient options now accept `clientExceptionStatusCodeMapOverride` which can be used to override the default HTTP error status code mapping. This is useful e.g. when a dependent service is not following REST-ful best practices and e.g. returns a 403 when there's an intermittent network error communicating with the authorization service
|
|
31
|
-
|
|
32
|
-
## [5.3.2] - 2024-02-08
|
|
33
|
-
|
|
34
|
-
### Fixed
|
|
35
|
-
|
|
36
|
-
Error details of external HTTP error responses are propagated correctly
|
|
37
|
-
|
|
38
|
-
## [5.3.1] - 2023-10-25
|
|
39
|
-
|
|
40
|
-
### Fixed
|
|
41
|
-
|
|
42
|
-
The `getUserToken()` and `getUserPrincipal()` order was wrongly set in version `5.3.0`. The new fixed
|
|
43
|
-
priority order:
|
|
44
|
-
|
|
45
|
-
`getUserToken()`
|
|
46
|
-
|
|
47
|
-
1. `request.authorizerContext.jwt`
|
|
48
|
-
2. `request.authorizerContext.accessToken` (new)
|
|
49
|
-
3. `request.headers.Authorization`
|
|
50
|
-
|
|
51
|
-
`getUserPrincipal()`
|
|
52
|
-
|
|
53
|
-
1. `authorizerContext.canonicalId` (**prefer canonicalId**)
|
|
54
|
-
2. `authorizerContext.principalId` (new)
|
|
55
|
-
3. `request.headers.Authorization`
|
|
56
|
-
|
|
57
|
-
## [5.3.0] - 2023-09-07
|
|
58
|
-
|
|
59
|
-
### Changed
|
|
60
|
-
|
|
61
|
-
The `getUserToken()` and `getUserPrincipal()` methods now support multiple sources of for their values
|
|
62
|
-
|
|
63
|
-
`getUserToken()` in priority order:
|
|
64
|
-
|
|
65
|
-
1. `request.authorizerContext.accessToken` (new)
|
|
66
|
-
2. `request.authorizerContext.jwt`
|
|
67
|
-
3. `request.headers.Authorization`
|
|
68
|
-
|
|
69
|
-
`getUserPrincipal()` in priority order:
|
|
70
|
-
|
|
71
|
-
1. `authorizerContext.principalId` (new)
|
|
72
|
-
2. `authorizerContext.canonicalId`
|
|
73
|
-
3. `request.headers.Authorization`
|
|
74
|
-
|
|
75
|
-
## [5.2.2] - 2023-08-25
|
|
76
|
-
|
|
77
|
-
### Added
|
|
78
|
-
|
|
79
|
-
HttpClient now also logs unexpected (e.g. network) errors that are not coming from Axios
|
|
80
|
-
|
|
81
|
-
## [5.2.0] - 2023-06-08
|
|
82
|
-
|
|
83
|
-
### Added
|
|
84
|
-
|
|
85
|
-
Tracking of `canonicalId` and `correlationId` in New Relic.
|
|
86
|
-
|
|
87
|
-
!IMPORTANT! You must exclude the `newrelic` module from `webpack.config.ts` like so:
|
|
88
|
-
|
|
89
|
-
```
|
|
90
|
-
externals: ['newrelic']
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
### Added
|
|
94
|
-
|
|
95
|
-
The `DeleteRequest` model.
|
|
96
|
-
|
|
97
|
-
## [5.1.10] - 2023-01-13
|
|
98
|
-
|
|
99
|
-
### Changed
|
|
100
|
-
|
|
101
|
-
The error middleware logs `4xx` errors with log level `WARN` (previously `INFO`).
|
|
102
|
-
|
|
103
|
-
## [5.1.9] - 2023-01-04
|
|
104
|
-
|
|
105
|
-
### Changed
|
|
106
|
-
|
|
107
|
-
Upgraded jsonwebtoken version to 9.0.0 and aws-sdk to version 2.1287.0
|
|
108
|
-
|
|
109
|
-
## [5.1.8] - 2022-12-15
|
|
110
|
-
|
|
111
|
-
### Changed
|
|
112
|
-
|
|
113
|
-
Removed client_secret from API response
|
|
114
|
-
|
|
115
|
-
## [5.1.7] - 2022-12-13
|
|
116
|
-
|
|
117
|
-
### Changed
|
|
118
|
-
|
|
119
|
-
Add timeout option in HttpClient. If no value is provided the default is no timeout.
|
|
120
|
-
|
|
121
|
-
## [5.1.6] - 2022-11-30
|
|
122
|
-
|
|
123
|
-
### Changed
|
|
124
|
-
|
|
125
|
-
Removed logging of client_secret
|
|
126
|
-
|
|
127
|
-
## [5.1.5] - 2022-11-15
|
|
128
|
-
|
|
129
|
-
### Changed
|
|
130
|
-
|
|
131
|
-
The fix described in 5.1.4 missed one instance where the bug can occur. This change covers all known instances.
|
|
132
|
-
|
|
133
|
-
## [5.1.4] - 2022-11-08
|
|
134
|
-
|
|
135
|
-
### Changed
|
|
136
|
-
|
|
137
|
-
Using `baseURL` in the axios config without specifying the full URL resulted in an error in the exception handling. So the `AxiosError` was thrown instead of a customer `ClientException`.
|
|
138
|
-
|
|
139
|
-
## [5.1.3] - 2022-10-03
|
|
140
|
-
|
|
141
|
-
### Changed
|
|
142
|
-
|
|
143
|
-
RequestLogger now logs only `Host`, 'User-Agent`, `orion-correlation-id-parent`, `orion-correlation-id-root` headers.
|
|
144
|
-
|
|
145
|
-
## [5.1.2] - 2022-09-14
|
|
146
|
-
|
|
147
|
-
### Changed
|
|
148
|
-
|
|
149
|
-
Properties `stageVariables`, `isBase64Encoded` and `route` from openapi-factory are available in the Typescript definitions.
|
|
150
|
-
|
|
151
|
-
## [5.1.1] - 2022-09-05
|
|
152
|
-
|
|
153
|
-
### Changed
|
|
154
|
-
|
|
155
|
-
HttpApi payload version 2.0 events supported for openApiWrapper.
|
|
156
|
-
|
|
157
|
-
## [5.1.0] - 2022-09-05
|
|
158
|
-
|
|
159
|
-
### Changed
|
|
160
|
-
|
|
161
|
-
Dependencies aren't pinned to a fixed version to allow users of the library to independently upgrade minor (devDependencies) and patch (dependencies) versions. This will simplify fixing security alerts faster than in this library, for example by applying `npm audit fix`.
|
|
162
|
-
|
|
163
|
-
## [5.0.0] - 2022-02-22
|
|
164
|
-
|
|
165
|
-
### Changed
|
|
166
|
-
|
|
167
|
-
- **[Breaking change]** `TokenProvider` was replaced by more specific `KmsTokenProvider` class.
|
|
168
|
-
The functionality and interface remains the same, the imports need to be changed.
|
|
169
|
-
|
|
170
|
-
### Added
|
|
171
|
-
|
|
172
|
-
- New `SecretsManagerTokenProvider` that relies on AWS Secrets Manager to retrieve client ID and client secret.
|
|
173
|
-
The advantage of using AWS Secrets Manager is that it can be supplied with a secret rotation function.
|
|
174
|
-
|
|
175
|
-
## [4.1.5] - 2022-02-10
|
|
176
|
-
|
|
177
|
-
### Changed
|
|
178
|
-
|
|
179
|
-
`ClientException` now maps `HTTP 422` client responses to `HTTP 422` server responses (was `HTTP 503` before).
|
|
180
|
-
|
|
181
|
-
## [4.1.2] - 2021-12-02
|
|
182
|
-
|
|
183
|
-
### Changed
|
|
184
|
-
|
|
185
|
-
Expose the `Location`, `Access-Control-Allow-Origin` and `orion-correlation-id-root` headers
|
|
186
|
-
|
|
187
|
-
## [4.1.1] - 2021-11-22
|
|
188
|
-
|
|
189
|
-
### Fixed
|
|
190
|
-
|
|
191
|
-
- `ApiResponse` default content-type header was renamed to `Content-Type` to overwrite the default header
|
|
192
|
-
of [openapi-factory.js](https://github.com/Rhosys/openapi-factory.js/blob/release/5.2/src/response.js#L15)
|
|
193
|
-
- Also upgraded `openapi-factory.js` to get support of over-writing response headers
|
|
194
|
-
|
|
195
|
-
## [4.1.0] - 2021-11-22
|
|
196
|
-
|
|
197
|
-
### Changed
|
|
198
|
-
|
|
199
|
-
- `ApiResponse` default content-type header was changed from `application/links+json` to `application/hal+json`
|
|
200
|
-
|
|
201
|
-
## [4.0.0] - 2021-11-12
|
|
202
|
-
|
|
203
|
-
### Changed
|
|
204
|
-
|
|
205
|
-
- `HttpClient` the [retryAdapterEnhancer axios adapter](https://github.com/kuitos/axios-extensions#cacheadapterenhancer)
|
|
206
|
-
was replaced by the more flexible [axios-cache-adapter](https://github.com/RasCarlito/axios-cache-adapter).
|
|
207
|
-
- **[Breaking change]** `HttpClientOptions.cacheOptions` now accepts [extensive cache configuration](https://github.com/RasCarlito/axios-cache-adapter/blob/master/axios-cache-adapter.d.ts#L26).
|
|
208
|
-
- The cache is now partitioned by `canonical_id` JWT claim.
|
|
209
|
-
|
|
210
|
-
## [3.0.1] - 2021-09-13
|
|
211
|
-
|
|
212
|
-
### Fixed
|
|
213
|
-
|
|
214
|
-
- Downgraded Axios to 0.21.1 due to response interceptors not being applied correctly in 0.21.2. [There has been a fix to
|
|
215
|
-
axios but a version with the fix is not available yet.](https://github.com/axios/axios/commit/83ae3830e4070adbcdcdcdd6e8afbac568afd708)
|
|
216
|
-
|
|
217
|
-
## [3.0.0] - 2021-09-10
|
|
218
|
-
|
|
219
|
-
### Changed
|
|
220
|
-
|
|
221
|
-
- `HttpClient` the [retryAdapterEnhancer axios adapter](https://github.com/kuitos/axios-extensions#retryadapterenhancer)
|
|
222
|
-
was replaced by the more flexible [retry-axios interceptor](https://github.com/JustinBeckwith/retry-axios).
|
|
223
|
-
- **[Breaking change]** `HttpClientOptions.retryOptions` now accepts [extensive retry configuration](https://github.com/JustinBeckwith/retry-axios/blob/v2.6.0/src/index.ts#L11)
|
|
224
|
-
such as specifying HTTP status codes that should be retried.
|
|
225
|
-
- **[Breaking change]** All HTTP status codes are no longer retried by default. The new default are these ranges:
|
|
226
|
-
- [100, 199] Informational, request still processing
|
|
227
|
-
- [429, 429] Too Many Requests
|
|
228
|
-
- [500, 599] Server errors
|
|
229
|
-
|
|
230
|
-
## [2.2.2] - 2021-07-08
|
|
231
|
-
|
|
232
|
-
### Fixed bugs
|
|
233
|
-
|
|
234
|
-
- Some HTTP error log statements were throwing exceptions. This was due to accessing `error.request.headers[orionCorrelationIdRoot]`
|
|
235
|
-
from Axios error object, where the `headers` object was `undefined`. The correct field was `error.config.headers`.
|
|
236
|
-
|
|
237
|
-
## [2.2.1] - 2021-07-08
|
|
238
|
-
|
|
239
|
-
### Changed
|
|
240
|
-
|
|
241
|
-
- `HttpClient` logs additional request data (query parameters, body).
|
|
242
|
-
|
|
243
|
-
### Added
|
|
244
|
-
|
|
245
|
-
- `HttpClientOptions` now accepts `logOptions` object that allows enabling informational request and **response (new)** logs.
|
|
246
|
-
|
|
247
|
-
```js
|
|
248
|
-
{
|
|
249
|
-
logOptions: {
|
|
250
|
-
enabledLogs: [HttpLogType.requests, HttpLogType.responses];
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
## [2.1.0] - 2021-03-11
|
|
256
|
-
|
|
257
|
-
### Changed
|
|
258
|
-
|
|
259
|
-
- ClientException propagates the original status code and details through multiple services. E.g. instead of `error.detials?.data.details?.userDefinedProp` use `error.details?.userDefinedProp`
|
|
260
|
-
|
|
261
|
-
## [2.0.0] - 2021-03-08
|
|
262
|
-
|
|
263
|
-
### Changed
|
|
264
|
-
|
|
265
|
-
- ClientException no longer wraps details in an `error` property. Instead of `error.details?.error.userDefinedProp` use `error.details?.userDefinedProp`
|
|
266
|
-
|
|
267
|
-
## [1.2.0] - 2021-02-16
|
|
268
|
-
|
|
269
|
-
### Updated
|
|
270
|
-
|
|
271
|
-
- [IMPORTANT!] HttpClient throws serialized Axios errors through ClientExceptions.
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createDebounceRequestAdapter = void 0;
|
|
4
|
-
// adopted from https://github.com/RasCarlito/axios-cache-adapter/issues/231#issuecomment-880288436
|
|
5
|
-
function createDebounceRequestAdapter(requestAdapter, requestKeyProvider) {
|
|
6
|
-
const runningRequests = {};
|
|
7
|
-
return (req) => {
|
|
8
|
-
const cacheKey = requestKeyProvider(req);
|
|
9
|
-
// Add the request to runningRequests. If it is already there, drop the duplicated request.
|
|
10
|
-
if (!runningRequests[cacheKey]) {
|
|
11
|
-
runningRequests[cacheKey] = requestAdapter(req);
|
|
12
|
-
}
|
|
13
|
-
// Return the response promise
|
|
14
|
-
return runningRequests[cacheKey].finally(() => {
|
|
15
|
-
// Finally, delete the request from the runningRequests whether there's error or not
|
|
16
|
-
delete runningRequests[cacheKey];
|
|
17
|
-
});
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
exports.createDebounceRequestAdapter = createDebounceRequestAdapter;
|