node-server-engine 1.4.9 → 1.5.1
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/.commitlintrc.js +27 -0
- package/.releaserc.js +56 -0
- package/README.md +154 -2
- package/dist/const/tokenIssuer.js +1 -0
- package/dist/const/tokenIssuer.js.map +1 -1
- package/dist/db/models/SequelizeMeta.js +3 -1
- package/dist/db/models/SequelizeMeta.js.map +1 -1
- package/dist/entities/ElasticSearch/ElasticSearch.js +1 -2
- package/dist/entities/ElasticSearch/ElasticSearch.js.map +1 -1
- package/dist/entities/Sequelize/Sequelize.js +4 -1
- package/dist/entities/Sequelize/Sequelize.js.map +1 -1
- package/dist/entities/Server/Server.js +8 -11
- package/dist/entities/Server/Server.js.map +1 -1
- package/dist/entities/SocketClient/SocketClient.d.ts +4 -1
- package/dist/entities/SocketClient/SocketClient.js +5 -1
- package/dist/entities/SocketClient/SocketClient.js.map +1 -1
- package/dist/index.js +1 -2
- package/dist/index.js.map +1 -1
- package/dist/middleware/checkPermission/checkPermission.d.ts +1 -1
- package/dist/middleware/checkPermission/checkPermission.js +4 -2
- package/dist/middleware/checkPermission/checkPermission.js.map +1 -1
- package/dist/utils/createSortedObject/createSortedObject.d.ts +5 -1
- package/dist/utils/createSortedObject/createSortedObject.js +13 -4
- package/dist/utils/createSortedObject/createSortedObject.js.map +1 -1
- package/dist/utils/dbMigration/dbMigration.js +34 -15
- package/dist/utils/dbMigration/dbMigration.js.map +1 -1
- package/dist/utils/geminiFileUpload/geminiFileUpload.js +2 -1
- package/dist/utils/geminiFileUpload/geminiFileUpload.js.map +1 -1
- package/dist/utils/jwt/jwt.js +1 -1
- package/dist/utils/jwt/jwt.js.map +1 -1
- package/dist/utils/report/report.d.ts +9 -3
- package/dist/utils/report/report.js +29 -12
- package/dist/utils/report/report.js.map +1 -1
- package/dist/utils/secretManager/secretLoader.js +5 -1
- package/dist/utils/secretManager/secretLoader.js.map +1 -1
- package/dist/utils/semverCompare/semverCompare.d.ts +4 -4
- package/dist/utils/semverCompare/semverCompare.js +16 -18
- package/dist/utils/semverCompare/semverCompare.js.map +1 -1
- package/package.json +30 -19
- package/.env.example +0 -186
- package/.github/workflows/publish.yml +0 -41
- package/.prettierrc +0 -7
- package/Dockerfile +0 -31
- package/babel.config.json +0 -34
- package/eslint.config.mjs +0 -11
- package/src/const/DebeziumEvent.ts +0 -6
- package/src/const/LogSeverity.ts +0 -7
- package/src/const/index.ts +0 -5
- package/src/const/metrics.ts +0 -6
- package/src/const/mime.ts +0 -37
- package/src/const/tokenIssuer.ts +0 -7
- package/src/db/migrations/20241030121500-create-sequelize-meta-table.ts +0 -29
- package/src/db/models/SequelizeMeta.ts +0 -16
- package/src/db/models/index.ts +0 -2
- package/src/entities/ElasticSearch/ElasticSearch.ts +0 -103
- package/src/entities/ElasticSearch/ElasticSearch.types.ts +0 -7
- package/src/entities/ElasticSearch/index.ts +0 -2
- package/src/entities/Endpoint/Endpoint.test.ts +0 -248
- package/src/entities/Endpoint/Endpoint.ts +0 -221
- package/src/entities/Endpoint/Endpoint.types.ts +0 -181
- package/src/entities/Endpoint/index.ts +0 -2
- package/src/entities/EngineError/EngineError.ts +0 -28
- package/src/entities/EngineError/EngineError.types.ts +0 -15
- package/src/entities/EngineError/index.ts +0 -2
- package/src/entities/KeySet/KeySet.test.ts +0 -126
- package/src/entities/KeySet/KeySet.ts +0 -172
- package/src/entities/KeySet/KeySet.types.ts +0 -28
- package/src/entities/KeySet/index.ts +0 -2
- package/src/entities/LifecycleController/LifecycleController.test.ts +0 -48
- package/src/entities/LifecycleController/LifecycleController.ts +0 -24
- package/src/entities/LifecycleController/LifecycleController.types.ts +0 -5
- package/src/entities/LifecycleController/index.ts +0 -2
- package/src/entities/Localizator/Localizator.ts +0 -139
- package/src/entities/Localizator/Localizator.types.ts +0 -52
- package/src/entities/Localizator/guessNamingConvention/guessNamingConvention.test.ts +0 -165
- package/src/entities/Localizator/guessNamingConvention/guessNamingConvention.ts +0 -58
- package/src/entities/Localizator/guessNamingConvention/index.ts +0 -1
- package/src/entities/Localizator/index.ts +0 -2
- package/src/entities/MessageHandler/MessageHandler.test.ts +0 -130
- package/src/entities/MessageHandler/MessageHandler.ts +0 -72
- package/src/entities/MessageHandler/MessageHandler.types.ts +0 -15
- package/src/entities/MessageHandler/index.ts +0 -2
- package/src/entities/PubSub/PubSub.test.ts +0 -297
- package/src/entities/PubSub/PubSub.ts +0 -202
- package/src/entities/PubSub/PubSub.types.ts +0 -76
- package/src/entities/PubSub/handleMessage/handleMessage.test.ts +0 -102
- package/src/entities/PubSub/handleMessage/handleMessage.ts +0 -65
- package/src/entities/PubSub/handleMessage/index.ts +0 -1
- package/src/entities/PubSub/index.ts +0 -2
- package/src/entities/PushNotification/PushNotification.test.ts +0 -35
- package/src/entities/PushNotification/PushNotification.ts +0 -44
- package/src/entities/PushNotification/PushNotification.types.ts +0 -25
- package/src/entities/PushNotification/index.ts +0 -2
- package/src/entities/Redis/Redis.ts +0 -71
- package/src/entities/Redis/Redis.types.ts +0 -19
- package/src/entities/Redis/Redis.validate.ts +0 -17
- package/src/entities/Redis/index.ts +0 -2
- package/src/entities/Sequelize/Sequelize.config.ts +0 -59
- package/src/entities/Sequelize/Sequelize.test.ts +0 -21
- package/src/entities/Sequelize/Sequelize.ts +0 -75
- package/src/entities/Sequelize/Sequelize.types.ts +0 -10
- package/src/entities/Sequelize/Sequelize.validate.ts +0 -15
- package/src/entities/Sequelize/index.ts +0 -2
- package/src/entities/Server/Server.const.ts +0 -56
- package/src/entities/Server/Server.test.ts +0 -272
- package/src/entities/Server/Server.ts +0 -471
- package/src/entities/Server/Server.types.ts +0 -88
- package/src/entities/Server/index.ts +0 -2
- package/src/entities/SocketClient/SocketClient.test.ts +0 -392
- package/src/entities/SocketClient/SocketClient.ts +0 -401
- package/src/entities/SocketClient/SocketClient.types.ts +0 -62
- package/src/entities/SocketClient/index.ts +0 -2
- package/src/entities/SocketClient/parseMessage/index.ts +0 -1
- package/src/entities/SocketClient/parseMessage/parseMessage.test.ts +0 -20
- package/src/entities/SocketClient/parseMessage/parseMessage.ts +0 -26
- package/src/entities/Storage/Storage.test.ts +0 -58
- package/src/entities/Storage/Storage.ts +0 -219
- package/src/entities/Storage/Storage.types.ts +0 -57
- package/src/entities/Storage/index.ts +0 -2
- package/src/entities/ValidationError/ValidationError.ts +0 -34
- package/src/entities/ValidationError/index.ts +0 -1
- package/src/entities/WebError/WebError.test.ts +0 -18
- package/src/entities/WebError/WebError.ts +0 -23
- package/src/entities/WebError/WebError.types.ts +0 -9
- package/src/entities/WebError/index.ts +0 -2
- package/src/entities/index.ts +0 -16
- package/src/index.ts +0 -6
- package/src/metrics/debug.ts +0 -10
- package/src/metrics/index.ts +0 -2
- package/src/metrics/request.ts +0 -17
- package/src/middleware/authHmac/authHmac.test.ts +0 -86
- package/src/middleware/authHmac/authHmac.ts +0 -56
- package/src/middleware/authHmac/authHmac.types.ts +0 -7
- package/src/middleware/authHmac/index.ts +0 -2
- package/src/middleware/authJwt/authJwt.test.ts +0 -54
- package/src/middleware/authJwt/authJwt.ts +0 -55
- package/src/middleware/authJwt/authJwt.types.ts +0 -14
- package/src/middleware/authJwt/index.ts +0 -2
- package/src/middleware/authStatic/authStatic.test.ts +0 -36
- package/src/middleware/authStatic/authStatic.ts +0 -45
- package/src/middleware/authStatic/index.ts +0 -1
- package/src/middleware/authTls/authTls.test.ts +0 -138
- package/src/middleware/authTls/authTls.ts +0 -119
- package/src/middleware/authTls/authTls.types.ts +0 -13
- package/src/middleware/authTls/index.ts +0 -2
- package/src/middleware/checkPermission/checkPermission.ts +0 -33
- package/src/middleware/checkPermission/index.ts +0 -1
- package/src/middleware/error/error.test.ts +0 -60
- package/src/middleware/error/error.ts +0 -25
- package/src/middleware/error/index.ts +0 -1
- package/src/middleware/fileUploader/fileUploader.ts +0 -154
- package/src/middleware/fileUploader/fileUploader.types.ts +0 -35
- package/src/middleware/fileUploader/index.ts +0 -2
- package/src/middleware/fileUploader/isMultiPart/index.ts +0 -1
- package/src/middleware/fileUploader/isMultiPart/isMultipart.ts +0 -9
- package/src/middleware/fileUploader/validate/index.ts +0 -1
- package/src/middleware/fileUploader/validate/validate.ts +0 -27
- package/src/middleware/fourOhFour/fourOhFour.test.ts +0 -14
- package/src/middleware/fourOhFour/fourOhFour.ts +0 -13
- package/src/middleware/fourOhFour/index.ts +0 -1
- package/src/middleware/geminiFileUploader/geminiFileUploader.ts +0 -68
- package/src/middleware/geminiFileUploader/index.ts +0 -1
- package/src/middleware/healthCheck/healthCheck.test.ts +0 -12
- package/src/middleware/healthCheck/healthCheck.ts +0 -14
- package/src/middleware/healthCheck/index.ts +0 -1
- package/src/middleware/index.ts +0 -17
- package/src/middleware/metrics/index.ts +0 -1
- package/src/middleware/metrics/metrics.test.ts +0 -46
- package/src/middleware/metrics/metrics.ts +0 -24
- package/src/middleware/multiPartFileUploader/index.ts +0 -2
- package/src/middleware/multiPartFileUploader/multiPartFileUploader.ts +0 -249
- package/src/middleware/multiPartFileUploader/multiPartFileUploader.types.ts +0 -23
- package/src/middleware/multiPartFileUploader/validate/index.ts +0 -1
- package/src/middleware/multiPartFileUploader/validate/validate.ts +0 -27
- package/src/middleware/requestMetrics/index.ts +0 -1
- package/src/middleware/requestMetrics/requestMetrics.test.ts +0 -77
- package/src/middleware/requestMetrics/requestMetrics.ts +0 -20
- package/src/middleware/swaggerDocs/index.ts +0 -2
- package/src/middleware/swaggerDocs/swaggerDocs.ts +0 -114
- package/src/middleware/swaggerDocs/swaggerDocs.types.ts +0 -41
- package/src/middleware/userResolver/index.ts +0 -1
- package/src/middleware/userResolver/userResolver.ts +0 -53
- package/src/middleware/validate/index.ts +0 -1
- package/src/middleware/validate/validate.test.ts +0 -43
- package/src/middleware/validate/validate.ts +0 -25
- package/src/middleware/version/index.ts +0 -1
- package/src/middleware/version/version.test.ts +0 -23
- package/src/middleware/version/version.ts +0 -12
- package/src/utils/checkEnvironment/index.ts +0 -49
- package/src/utils/createHttpServer/createHttpServer.test.ts +0 -24
- package/src/utils/createHttpServer/createHttpServer.ts +0 -34
- package/src/utils/createHttpServer/createHttpServer.validate.ts +0 -17
- package/src/utils/createHttpServer/index.ts +0 -1
- package/src/utils/createSortedObject/createSortedObject.ts +0 -20
- package/src/utils/createSortedObject/createSortedObjet.test.ts +0 -47
- package/src/utils/createSortedObject/index.ts +0 -1
- package/src/utils/dbMigration/dbMigration.test.ts +0 -209
- package/src/utils/dbMigration/dbMigration.ts +0 -199
- package/src/utils/dbMigration/dbMigration.types.ts +0 -11
- package/src/utils/dbMigration/index.ts +0 -2
- package/src/utils/dbMigration/migrateMetadataTable/index.ts +0 -1
- package/src/utils/dbMigration/migrateMetadataTable/migrateMetadataTable.test.ts +0 -59
- package/src/utils/dbMigration/migrateMetadataTable/migrateMetadataTable.ts +0 -102
- package/src/utils/envAssert/envAssert.ts +0 -31
- package/src/utils/envAssert/index.ts +0 -1
- package/src/utils/envAssert/validators/hostList.ts +0 -17
- package/src/utils/envAssert/validators/index.ts +0 -5
- package/src/utils/envAssert/validators/ipList.ts +0 -13
- package/src/utils/envAssert/validators/path.ts +0 -13
- package/src/utils/envAssert/validators/string.ts +0 -9
- package/src/utils/envAssert/validators/stringList.ts +0 -13
- package/src/utils/filter/filter.ts +0 -17
- package/src/utils/filter/index.ts +0 -1
- package/src/utils/geminiFileUpload/geminiFileUpload.ts +0 -93
- package/src/utils/geminiFileUpload/index.ts +0 -1
- package/src/utils/hmac/checkEnv/checkEnv.test.ts +0 -18
- package/src/utils/hmac/checkEnv/checkEnv.ts +0 -9
- package/src/utils/hmac/checkEnv/index.ts +0 -1
- package/src/utils/hmac/createHmac/createHmac.ts +0 -33
- package/src/utils/hmac/createHmac/index.ts +0 -1
- package/src/utils/hmac/index.ts +0 -4
- package/src/utils/hmac/index.types.ts +0 -13
- package/src/utils/hmac/signPayload/index.ts +0 -1
- package/src/utils/hmac/signPayload/signPayload.ts +0 -16
- package/src/utils/hmac/verifySignature/index.ts +0 -1
- package/src/utils/hmac/verifySignature/verifySignature.ts +0 -24
- package/src/utils/index.ts +0 -29
- package/src/utils/jwt/index.ts +0 -2
- package/src/utils/jwt/jwt.test.ts +0 -28
- package/src/utils/jwt/jwt.ts +0 -182
- package/src/utils/jwt/jwt.types.ts +0 -21
- package/src/utils/report/index.ts +0 -3
- package/src/utils/report/output.ts +0 -45
- package/src/utils/report/output.types.ts +0 -70
- package/src/utils/report/report.test.ts +0 -133
- package/src/utils/report/report.ts +0 -133
- package/src/utils/report/report.types.ts +0 -13
- package/src/utils/request/index.ts +0 -1
- package/src/utils/request/request.test.ts +0 -26
- package/src/utils/request/request.ts +0 -48
- package/src/utils/secretManager/index.ts +0 -2
- package/src/utils/secretManager/secretLoader.ts +0 -62
- package/src/utils/secretManager/secretResolver.ts +0 -35
- package/src/utils/semverCompare/index.ts +0 -1
- package/src/utils/semverCompare/semverCompare.test.ts +0 -49
- package/src/utils/semverCompare/semverCompare.ts +0 -60
- package/src/utils/sendEmail/index.ts +0 -2
- package/src/utils/sendEmail/sendEmail.ts +0 -61
- package/src/utils/sendEmail/sendEmail.types.ts +0 -61
- package/src/utils/sendPush/index.ts +0 -2
- package/src/utils/sendPush/sendPush.ts +0 -20
- package/src/utils/sendPush/sendPush.types.ts +0 -25
- package/src/utils/tlsConfig/index.ts +0 -2
- package/src/utils/tlsConfig/tlsConfig.test.ts +0 -32
- package/src/utils/tlsConfig/tlsConfig.ts +0 -47
- package/src/utils/tlsConfig/tlsConfig.types.ts +0 -11
- package/src/utils/tlsConfig/tlsConfig.validate.ts +0 -25
- package/src/utils/tlsRequest/index.ts +0 -1
- package/src/utils/tlsRequest/tlsRequest.test.ts +0 -63
- package/src/utils/tlsRequest/tlsRequest.ts +0 -12
- package/src/utils/trimPathSlash/index.ts +0 -1
- package/src/utils/trimPathSlash/trimPathSlash.test.ts +0 -26
- package/src/utils/trimPathSlash/trimPathSlash.ts +0 -8
- package/test/env.ts +0 -53
- package/test/factory/index.ts +0 -8
- package/test/factory/sequelizeMeta.ts +0 -12
- package/test/hooks.ts +0 -35
- package/test/index.ts +0 -3
- package/test/stub/index.ts +0 -3
- package/test/stub/stubLocales.ts +0 -68
- package/test/stub/stubPubSub.ts +0 -7
- package/test/stub/stubStorage.ts +0 -77
- package/test/stub/stubStorage.types.ts +0 -11
- package/test/utils/app.ts +0 -31
- package/test/utils/index.ts +0 -1
- package/tsconfig.build.json +0 -5
- package/tsconfig.json +0 -32
package/src/utils/jwt/jwt.ts
DELETED
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
verify,
|
|
3
|
-
JwtHeader,
|
|
4
|
-
SigningKeyCallback,
|
|
5
|
-
Algorithm,
|
|
6
|
-
VerifyOptions,
|
|
7
|
-
sign
|
|
8
|
-
} from 'jsonwebtoken';
|
|
9
|
-
import { UserTokenPayload } from './jwt.types';
|
|
10
|
-
import { TokenIssuer } from 'const';
|
|
11
|
-
import { EngineError } from 'entities/EngineError';
|
|
12
|
-
import { KeySet } from 'entities/KeySet';
|
|
13
|
-
import { WebError } from 'entities/WebError';
|
|
14
|
-
import { assertEnvironment } from 'utils/checkEnvironment';
|
|
15
|
-
import { envAssert } from 'utils/envAssert';
|
|
16
|
-
import { reportDebug } from 'utils/report';
|
|
17
|
-
import { getSecretOrFile } from 'utils';
|
|
18
|
-
|
|
19
|
-
export let keySet: KeySet | undefined;
|
|
20
|
-
|
|
21
|
-
const namespace = 'engine:utils:jwt';
|
|
22
|
-
|
|
23
|
-
/** Initiate key fetching mechanism */
|
|
24
|
-
export async function initKeySets(): Promise<void> {
|
|
25
|
-
// Create the KeySet instance
|
|
26
|
-
// The auth-service fetches its key from its disk and not from a network request
|
|
27
|
-
// This prevents fetch failures the first time the auth-service is started
|
|
28
|
-
if (process.env.CHART === 'auth-service') {
|
|
29
|
-
if (!process.env.ECDSA_PUBLIC_KEY)
|
|
30
|
-
throw new EngineError({
|
|
31
|
-
message: 'Environment variable "ECDSA_PUBLIC_KEY" is not defined'
|
|
32
|
-
});
|
|
33
|
-
keySet = new KeySet(process.env.ECDSA_PUBLIC_KEY, { file: true });
|
|
34
|
-
} else {
|
|
35
|
-
if (process.env.AUTH_SERVICE_URL)
|
|
36
|
-
keySet = new KeySet(
|
|
37
|
-
`${process.env.AUTH_SERVICE_URL}/.well-known/jwks.json`,
|
|
38
|
-
{ internal: true }
|
|
39
|
-
);
|
|
40
|
-
else
|
|
41
|
-
keySet = new KeySet(`./keys/jwks.json`, { internal: true, file: true });
|
|
42
|
-
|
|
43
|
-
if (!keySet)
|
|
44
|
-
throw new EngineError({
|
|
45
|
-
message:
|
|
46
|
-
'Environment variable "AUTH_SERVICE_URL" is not defined or JWKS not found'
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
const promise = [keySet.init.bind(keySet)()];
|
|
50
|
-
|
|
51
|
-
await Promise.all(promise);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** Stop fetching keys regularly */
|
|
55
|
-
export function shutdownKeySets(): void {
|
|
56
|
-
if (keySet) keySet.shutdown.bind(keySet)();
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/** Get the JWT public key */
|
|
60
|
-
function jwtGetKey(header: JwtHeader, callback: SigningKeyCallback): void {
|
|
61
|
-
// In test environment we return the symmetric signature secret
|
|
62
|
-
if (process.env.NODE_ENV === 'test') {
|
|
63
|
-
callback(null, process.env.JWT_SECRET);
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
// Check that KeySet has been initialized
|
|
67
|
-
if (!keySet) {
|
|
68
|
-
callback(new EngineError({ message: 'KeySet has not been initialized' }));
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
// Check that the token has a key ID
|
|
72
|
-
if (!header.kid) {
|
|
73
|
-
callback(
|
|
74
|
-
new EngineError({
|
|
75
|
-
message:
|
|
76
|
-
'Token has not key ID in header, impossible to match with known key',
|
|
77
|
-
data: header
|
|
78
|
-
})
|
|
79
|
-
);
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// In test environment we use a symmetrical secret bases signature
|
|
84
|
-
if (process.env.NODE_ENV === 'test') {
|
|
85
|
-
callback(null, process.env.JWT_SECRET);
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
reportDebug({
|
|
89
|
-
namespace,
|
|
90
|
-
message: `Looking for key with ID "${header.kid}"`
|
|
91
|
-
});
|
|
92
|
-
// Go through each key set to check if the token's key is in one of them
|
|
93
|
-
let key;
|
|
94
|
-
try {
|
|
95
|
-
key = keySet.getKey(header.kid);
|
|
96
|
-
} catch (error: Error | null | unknown) {
|
|
97
|
-
callback(error instanceof Error ? error : null);
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (key) {
|
|
102
|
-
reportDebug({ namespace, message: `Found key with ID "${header.kid}"` });
|
|
103
|
-
callback(null, key);
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
callback(
|
|
107
|
-
new WebError({
|
|
108
|
-
statusCode: 401,
|
|
109
|
-
errorCode: 'unauthorized',
|
|
110
|
-
message: 'Could not find the tokens key in any key set',
|
|
111
|
-
data: { kid: header.kid }
|
|
112
|
-
})
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/** Verify an user Json Web Token */
|
|
117
|
-
export async function jwtVerify(
|
|
118
|
-
token: string,
|
|
119
|
-
issuer: TokenIssuer = TokenIssuer.AUTH_SERVICE
|
|
120
|
-
): Promise<UserTokenPayload> {
|
|
121
|
-
let algorithms = ['ES256' as Algorithm];
|
|
122
|
-
if (process.env.NODE_ENV === 'test') {
|
|
123
|
-
assertEnvironment({ JWT_SECRET: envAssert.isString() });
|
|
124
|
-
algorithms = ['HS256' as Algorithm];
|
|
125
|
-
}
|
|
126
|
-
assertEnvironment({
|
|
127
|
-
ACCESS_TOKEN_AUDIENCE: envAssert.isString(),
|
|
128
|
-
ACCESS_TOKEN_ISSUER: envAssert.isString(),
|
|
129
|
-
ACCESS_TOKEN_EXPIRATION_TIME: envAssert.isString()
|
|
130
|
-
});
|
|
131
|
-
let options: VerifyOptions | undefined;
|
|
132
|
-
|
|
133
|
-
switch (issuer) {
|
|
134
|
-
case TokenIssuer.AUTH_SERVICE:
|
|
135
|
-
options = {
|
|
136
|
-
audience: [process.env.ACCESS_TOKEN_AUDIENCE as string],
|
|
137
|
-
issuer: [process.env.ACCESS_TOKEN_ISSUER as string],
|
|
138
|
-
algorithms
|
|
139
|
-
};
|
|
140
|
-
break;
|
|
141
|
-
default:
|
|
142
|
-
throw new EngineError({
|
|
143
|
-
message: `Invalid token issuer specified "${issuer as string}"`
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
return new Promise<UserTokenPayload>((resolve, reject) => {
|
|
148
|
-
verify(token, jwtGetKey, options, (error, result) => {
|
|
149
|
-
if (error) {
|
|
150
|
-
reject(
|
|
151
|
-
new WebError({
|
|
152
|
-
errorCode: 'unauthorized',
|
|
153
|
-
statusCode: 401,
|
|
154
|
-
message: 'Invalid token supplied',
|
|
155
|
-
data: { token },
|
|
156
|
-
error: error instanceof Error ? error : undefined
|
|
157
|
-
})
|
|
158
|
-
);
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
resolve(result as UserTokenPayload);
|
|
162
|
-
});
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
export function generateJwtToken(user: unknown) {
|
|
167
|
-
const signOptions = {
|
|
168
|
-
algorithm: 'ES256' as const, // or 'ES256' as Algorithm
|
|
169
|
-
expiresIn: process.env.ACCESS_TOKEN_EXPIRATION_TIME
|
|
170
|
-
? parseInt(process.env.ACCESS_TOKEN_EXPIRATION_TIME, 10)
|
|
171
|
-
: undefined,
|
|
172
|
-
issuer: process.env.ACCESS_TOKEN_ISSUER,
|
|
173
|
-
audience: process.env.ACCESS_TOKEN_AUDIENCE,
|
|
174
|
-
keyid: Object.keys(keySet?.getKeys() ?? {})[0]
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
const privateKey = getSecretOrFile('PRIVATE_KEY_PATH');
|
|
178
|
-
|
|
179
|
-
const token = sign({ user }, privateKey, signOptions);
|
|
180
|
-
|
|
181
|
-
return token;
|
|
182
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
/** Content of the payload of user authentication tokens */
|
|
3
|
-
export interface UserTokenPayload {
|
|
4
|
-
/** ID of the user */
|
|
5
|
-
sub: string;
|
|
6
|
-
/** URL of the service that issued the token */
|
|
7
|
-
iss: string;
|
|
8
|
-
/** URL of the services that can consume the token */
|
|
9
|
-
aud: Array<string> | string;
|
|
10
|
-
/** Unique ID of the token */
|
|
11
|
-
jti: string;
|
|
12
|
-
/** ID of the device the token was issued to */
|
|
13
|
-
dev: string;
|
|
14
|
-
/** Timestamp at which the token will expire */
|
|
15
|
-
exp: number;
|
|
16
|
-
/** List of permissions that the user has (only on the admin API) */
|
|
17
|
-
per?: Array<string> | string;
|
|
18
|
-
/** User */
|
|
19
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
20
|
-
user: Record<string, any> | undefined;
|
|
21
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from 'crypto';
|
|
2
|
-
import callsite from 'callsite';
|
|
3
|
-
import stringify from 'json-stringify-safe';
|
|
4
|
-
import { LogEntry, FullLogEntry } from './output.types';
|
|
5
|
-
|
|
6
|
-
// NOTE: Server instance id (helpful for distributed logs)
|
|
7
|
-
export const INSTANCE_ID = randomUUID();
|
|
8
|
-
|
|
9
|
-
/** Add metadata to the log entry and print it */
|
|
10
|
-
export function output(data: LogEntry): FullLogEntry {
|
|
11
|
-
// Gather useful information about where the logger was called from
|
|
12
|
-
const call = callsite()[2];
|
|
13
|
-
|
|
14
|
-
// Append additional metadata to the log entry
|
|
15
|
-
const fullEntry: FullLogEntry = {
|
|
16
|
-
...data,
|
|
17
|
-
lineNumber: call.getLineNumber(),
|
|
18
|
-
fileName: call
|
|
19
|
-
.getFileName()
|
|
20
|
-
.replace(/.*node_modules\//, '')
|
|
21
|
-
.replace(/.*src\//, 'src/'),
|
|
22
|
-
serviceContext: {
|
|
23
|
-
service: process.env.CHART ?? process.env.HOSTNAME,
|
|
24
|
-
version: process.env.BUILD_ID,
|
|
25
|
-
environment: process.env.NODE_ENV,
|
|
26
|
-
instance: process.env.KUBERNETES_POD_NAME ?? INSTANCE_ID
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
const entry = stringify(fullEntry);
|
|
31
|
-
// Ignore when silenced
|
|
32
|
-
if (process.env.SILENCE_REPORT) return fullEntry;
|
|
33
|
-
|
|
34
|
-
if (process.env.NODE_ENV === 'test') {
|
|
35
|
-
// In test mode pretty print
|
|
36
|
-
console.dir(fullEntry, { depth: 5 });
|
|
37
|
-
} else if (data.statusCode && data.statusCode >= 500) {
|
|
38
|
-
// Log to stderr when it is a 500+ error
|
|
39
|
-
console.error(entry);
|
|
40
|
-
} else {
|
|
41
|
-
// Default to regular logging
|
|
42
|
-
console.log(entry);
|
|
43
|
-
}
|
|
44
|
-
return fullEntry;
|
|
45
|
-
}
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import { LogSeverity } from 'const';
|
|
2
|
-
|
|
3
|
-
/** Structure of the request reporting in log entries */
|
|
4
|
-
interface LogEntryRequest {
|
|
5
|
-
/** Requested URL */
|
|
6
|
-
url: string;
|
|
7
|
-
/** HTTP method used */
|
|
8
|
-
method: string;
|
|
9
|
-
/** Referer website */
|
|
10
|
-
referrer?: string;
|
|
11
|
-
/** User-Agent of the connected client */
|
|
12
|
-
userAgent?: string;
|
|
13
|
-
/** IP address of the client */
|
|
14
|
-
remoteIp: string | undefined;
|
|
15
|
-
/** HTTP status code returned to the client */
|
|
16
|
-
responseStatusCode?: number;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/** Structure of the context entry in log entries */
|
|
20
|
-
interface LogEntryContext {
|
|
21
|
-
/** User that made a request */
|
|
22
|
-
user?: string;
|
|
23
|
-
/** Info on the processed HTTP request */
|
|
24
|
-
httpRequest?: LogEntryRequest;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/** Structure of a log entry */
|
|
28
|
-
export interface LogEntry {
|
|
29
|
-
/** Severity level at which the entry should be logged */
|
|
30
|
-
severity: LogSeverity;
|
|
31
|
-
/** Type of error */
|
|
32
|
-
errorType?: string;
|
|
33
|
-
/** Human readable error message that will be logged */
|
|
34
|
-
message: string;
|
|
35
|
-
/** Stack-trace of the error */
|
|
36
|
-
|
|
37
|
-
stack_trace?: string;
|
|
38
|
-
/** Additional data that accompanies the error in the logs */
|
|
39
|
-
data?: unknown;
|
|
40
|
-
/** Machine readable error code that is returned to the client */
|
|
41
|
-
errorCode?: string;
|
|
42
|
-
/** HTTP status code that is returned to the client */
|
|
43
|
-
statusCode?: number;
|
|
44
|
-
/** GCP error type */
|
|
45
|
-
'@type'?: string;
|
|
46
|
-
/** Error context */
|
|
47
|
-
context?: LogEntryContext;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/** Stricture of a log entry context entry */
|
|
51
|
-
interface LogEntryServiceContext {
|
|
52
|
-
/** Name of the service in which the error ocurred */
|
|
53
|
-
service?: string;
|
|
54
|
-
/** Version of the service in which the error ocurred */
|
|
55
|
-
version?: string;
|
|
56
|
-
/** Environment in which the error ocurred (dev/staging/prod) */
|
|
57
|
-
environment?: string;
|
|
58
|
-
/** ID of the instance that got the error */
|
|
59
|
-
instance?: string;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/** Complete log entry with call-stack */
|
|
63
|
-
export interface FullLogEntry extends LogEntry {
|
|
64
|
-
/** Line at which the error was thrown */
|
|
65
|
-
lineNumber: number;
|
|
66
|
-
/** Name of the file in which the error was thrown */
|
|
67
|
-
fileName: string;
|
|
68
|
-
/** Context of the error */
|
|
69
|
-
serviceContext: LogEntryServiceContext;
|
|
70
|
-
}
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import { expect } from 'chai';
|
|
2
|
-
import { faker } from '@faker-js/faker';
|
|
3
|
-
import { describe, it, beforeEach } from 'mocha';
|
|
4
|
-
import { stub, match } from 'sinon';
|
|
5
|
-
import * as output from './output';
|
|
6
|
-
import { reportInfo, reportDebug, resetNamespaceSelector } from './report';
|
|
7
|
-
import { InfoLogEntry } from './report.types';
|
|
8
|
-
|
|
9
|
-
describe('Utils - Report', () => {
|
|
10
|
-
it('should collect filename and line number', () => {
|
|
11
|
-
let result = reportInfo('example');
|
|
12
|
-
expect(result.fileName).to.equal(
|
|
13
|
-
`${__dirname}/report.test.ts`.replace(/.*src\//, 'src/')
|
|
14
|
-
);
|
|
15
|
-
expect(result.severity).to.equal('INFO');
|
|
16
|
-
expect(result.message).to.equal('example');
|
|
17
|
-
|
|
18
|
-
const data: InfoLogEntry = { message: 'world' };
|
|
19
|
-
result = reportInfo(data);
|
|
20
|
-
expect(result.message).to.equal('world');
|
|
21
|
-
expect(result.fileName).to.equal(
|
|
22
|
-
`${__dirname}/report.test.ts`.replace(/.*src\//, 'src/')
|
|
23
|
-
);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
describe('Report Debug', () => {
|
|
27
|
-
let globalNamespace: string;
|
|
28
|
-
|
|
29
|
-
beforeEach(() => {
|
|
30
|
-
resetNamespaceSelector();
|
|
31
|
-
|
|
32
|
-
globalNamespace = faker.lorem.slug();
|
|
33
|
-
reportDebug.setNameSpace(globalNamespace);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
afterEach(() => {
|
|
37
|
-
reportDebug.clearNamespace();
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('should report when the namespace is specified', () => {
|
|
41
|
-
const namespace = faker.lorem.word();
|
|
42
|
-
process.env.DEBUG = globalNamespace + ':' + namespace;
|
|
43
|
-
const outputStub = stub(output, 'output');
|
|
44
|
-
const message = faker.lorem.sentence();
|
|
45
|
-
reportDebug({ message, namespace });
|
|
46
|
-
expect(outputStub).to.have.been.calledOnceWithExactly(
|
|
47
|
-
match({
|
|
48
|
-
message: `[${namespace}] ${message}`,
|
|
49
|
-
severity: 'DEBUG',
|
|
50
|
-
namespace: namespace
|
|
51
|
-
})
|
|
52
|
-
);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('should report when the namespace is specified with a wildcard', () => {
|
|
56
|
-
const namespace = faker.lorem.word();
|
|
57
|
-
process.env.DEBUG = globalNamespace + ':' + `${namespace}:*`;
|
|
58
|
-
const outputStub = stub(output, 'output');
|
|
59
|
-
const message = faker.lorem.sentence();
|
|
60
|
-
const prefix = faker.lorem.word();
|
|
61
|
-
const actualNamespace = `${namespace}:${prefix}`;
|
|
62
|
-
reportDebug({ message, namespace: actualNamespace });
|
|
63
|
-
expect(outputStub).to.have.been.calledOnceWithExactly(
|
|
64
|
-
match({
|
|
65
|
-
message: `[${prefix}] ${message}`,
|
|
66
|
-
severity: 'DEBUG',
|
|
67
|
-
namespace: actualNamespace
|
|
68
|
-
})
|
|
69
|
-
);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('should report when multiple namespaces are specified', () => {
|
|
73
|
-
const namespace = faker.lorem.word();
|
|
74
|
-
process.env.DEBUG =
|
|
75
|
-
globalNamespace +
|
|
76
|
-
':' +
|
|
77
|
-
`${namespace},${faker.lorem.word()},${faker.lorem.word()}`;
|
|
78
|
-
const outputStub = stub(output, 'output');
|
|
79
|
-
const message = faker.lorem.sentence();
|
|
80
|
-
reportDebug({ message, namespace });
|
|
81
|
-
expect(outputStub).to.have.been.calledOnceWithExactly(
|
|
82
|
-
match({
|
|
83
|
-
message: `[${namespace}] ${message}`,
|
|
84
|
-
severity: 'DEBUG',
|
|
85
|
-
namespace: namespace
|
|
86
|
-
})
|
|
87
|
-
);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it('should not report when another namespace is specified', () => {
|
|
91
|
-
process.env.DEBUG = faker.lorem.word();
|
|
92
|
-
const outputStub = stub(output, 'output');
|
|
93
|
-
reportDebug({
|
|
94
|
-
message: faker.lorem.sentence(),
|
|
95
|
-
namespace: faker.lorem.word()
|
|
96
|
-
});
|
|
97
|
-
expect(outputStub).to.not.have.been.called;
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it('should ignore the global namespace in server engine report debug', () => {
|
|
101
|
-
const namespace = 'engine';
|
|
102
|
-
process.env.DEBUG = `${namespace}:*`;
|
|
103
|
-
const prefix = faker.lorem.word();
|
|
104
|
-
const actualNamespace = `${namespace}:${prefix}`;
|
|
105
|
-
const outputStub = stub(output, 'output');
|
|
106
|
-
const message = faker.lorem.sentence();
|
|
107
|
-
reportDebug({ message, namespace: actualNamespace });
|
|
108
|
-
expect(outputStub).to.have.been.calledOnceWithExactly(
|
|
109
|
-
match({
|
|
110
|
-
message: `[${prefix}] ${message}`,
|
|
111
|
-
severity: 'DEBUG',
|
|
112
|
-
namespace: actualNamespace
|
|
113
|
-
})
|
|
114
|
-
);
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
it('should report with no global namespace', () => {
|
|
118
|
-
reportDebug.clearNamespace();
|
|
119
|
-
const namespace = faker.lorem.word();
|
|
120
|
-
process.env.DEBUG = namespace;
|
|
121
|
-
const outputStub = stub(output, 'output');
|
|
122
|
-
const message = faker.lorem.sentence();
|
|
123
|
-
reportDebug({ message, namespace });
|
|
124
|
-
expect(outputStub).to.have.been.calledOnceWithExactly(
|
|
125
|
-
match({
|
|
126
|
-
message: `[${namespace}] ${message}`,
|
|
127
|
-
severity: 'DEBUG',
|
|
128
|
-
namespace
|
|
129
|
-
})
|
|
130
|
-
);
|
|
131
|
-
});
|
|
132
|
-
});
|
|
133
|
-
});
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import { Request } from 'express';
|
|
2
|
-
import { output } from './output';
|
|
3
|
-
import { LogEntry, FullLogEntry } from './output.types';
|
|
4
|
-
import { DebugLogEntry, InfoLogEntry } from './report.types';
|
|
5
|
-
import { LogSeverity } from 'const';
|
|
6
|
-
import { EngineError } from 'entities/EngineError';
|
|
7
|
-
import { WebError } from 'entities/WebError';
|
|
8
|
-
import {} from 'middleware/authHmac/authHmac.types';
|
|
9
|
-
import {} from 'middleware/authJwt/authJwt.types';
|
|
10
|
-
import {} from 'middleware/authTls/authTls.types';
|
|
11
|
-
|
|
12
|
-
/** Default generic logger */
|
|
13
|
-
export function reportInfo(data: InfoLogEntry | string): FullLogEntry {
|
|
14
|
-
return output({
|
|
15
|
-
severity: LogSeverity.INFO,
|
|
16
|
-
...(typeof data === 'string' ? { message: data } : data)
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/** Error specific logger */
|
|
21
|
-
export function reportError(
|
|
22
|
-
error: unknown | Error | EngineError | WebError,
|
|
23
|
-
request?: Request
|
|
24
|
-
): FullLogEntry {
|
|
25
|
-
let logEntry: LogEntry =
|
|
26
|
-
error instanceof Error
|
|
27
|
-
? {
|
|
28
|
-
message: error.message,
|
|
29
|
-
stack_trace: error.stack,
|
|
30
|
-
severity: LogSeverity.ERROR
|
|
31
|
-
}
|
|
32
|
-
: {
|
|
33
|
-
message: 'An unknown error occurred',
|
|
34
|
-
data: error,
|
|
35
|
-
severity: LogSeverity.CRITICAL
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
if (error instanceof EngineError) {
|
|
39
|
-
logEntry = {
|
|
40
|
-
...logEntry,
|
|
41
|
-
severity: error.severity,
|
|
42
|
-
errorType: error.constructor.name,
|
|
43
|
-
data: error.data,
|
|
44
|
-
'@type':
|
|
45
|
-
!(error instanceof WebError) || error.statusCode >= 500
|
|
46
|
-
? 'type.googleapis.com/google.devtools.clouderrorreporting.v1beta1.ReportedErrorEvent'
|
|
47
|
-
: undefined
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (error instanceof WebError) {
|
|
52
|
-
logEntry = {
|
|
53
|
-
...logEntry,
|
|
54
|
-
errorCode: error.errorCode,
|
|
55
|
-
statusCode: error.statusCode
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (request) {
|
|
60
|
-
logEntry = {
|
|
61
|
-
...logEntry,
|
|
62
|
-
context: {
|
|
63
|
-
user:
|
|
64
|
-
request.user?.id ?? request.hosts?.join(','),
|
|
65
|
-
httpRequest: {
|
|
66
|
-
url: request.originalUrl,
|
|
67
|
-
method: request.method,
|
|
68
|
-
referrer: request.header('Referer'),
|
|
69
|
-
userAgent: request.header('User-Agent'),
|
|
70
|
-
remoteIp: request.ip,
|
|
71
|
-
responseStatusCode:
|
|
72
|
-
error instanceof WebError ? error.statusCode : undefined
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return output(logEntry);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export let NAMESPACE_SELECTORS: undefined | Array<RegExp>;
|
|
82
|
-
|
|
83
|
-
/** Generate the namespace selectors based on the debug variable */
|
|
84
|
-
function initNamespaceSelector(): void {
|
|
85
|
-
if (process.env.DEBUG)
|
|
86
|
-
NAMESPACE_SELECTORS = process.env.DEBUG.split(',').map(
|
|
87
|
-
(namespace) => new RegExp('^' + namespace.replace(/\*/g, '.*?') + '$')
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/** Reset the namespace selectors */
|
|
92
|
-
export function resetNamespaceSelector(): void {
|
|
93
|
-
NAMESPACE_SELECTORS = undefined;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
let debugNamespace: string | undefined;
|
|
97
|
-
|
|
98
|
-
/** Log a message for debugging purpose, based on a namespace specified in an environment variable */
|
|
99
|
-
export function reportDebug(data: DebugLogEntry): FullLogEntry | undefined {
|
|
100
|
-
if (!NAMESPACE_SELECTORS) initNamespaceSelector();
|
|
101
|
-
let { namespace } = data;
|
|
102
|
-
const isEngineReport = namespace ? namespace.startsWith('engine') : false;
|
|
103
|
-
|
|
104
|
-
if (debugNamespace && !isEngineReport) {
|
|
105
|
-
namespace = debugNamespace + ':' + namespace;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const match =
|
|
109
|
-
namespace &&
|
|
110
|
-
NAMESPACE_SELECTORS &&
|
|
111
|
-
NAMESPACE_SELECTORS.some((selector) => selector.test(namespace));
|
|
112
|
-
if (!match) return;
|
|
113
|
-
|
|
114
|
-
// Generate a prefix with the right most part of the namespace
|
|
115
|
-
const spaces = namespace.split(':');
|
|
116
|
-
const prefix = spaces[spaces.length - 1];
|
|
117
|
-
|
|
118
|
-
return output({
|
|
119
|
-
severity: LogSeverity.DEBUG,
|
|
120
|
-
...data,
|
|
121
|
-
message: `[${prefix}] ${data.message}`
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/** Specify a global debugging namespace */
|
|
126
|
-
reportDebug.setNameSpace = function (globalNamespace: string): void {
|
|
127
|
-
debugNamespace = globalNamespace;
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
/** Remove any defined global namespace */
|
|
131
|
-
reportDebug.clearNamespace = function (): void {
|
|
132
|
-
debugNamespace = undefined;
|
|
133
|
-
};
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { LogEntry } from './output.types';
|
|
2
|
-
import { LogSeverity } from 'const';
|
|
3
|
-
|
|
4
|
-
/** Structure of debug logs */
|
|
5
|
-
export interface DebugLogEntry extends Omit<LogEntry, 'severity'> {
|
|
6
|
-
/** Debugging namespace in which the entry is */
|
|
7
|
-
namespace: string;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export type InfoLogEntry = Omit<LogEntry, 'severity'> & {
|
|
11
|
-
/** Severity level of the entries */
|
|
12
|
-
severity?: LogSeverity;
|
|
13
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './request';
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { expect } from 'chai';
|
|
2
|
-
import { faker } from '@faker-js/faker';
|
|
3
|
-
import nock, { cleanAll } from 'nock';
|
|
4
|
-
import { request } from './request';
|
|
5
|
-
import { EngineError } from 'entities/EngineError';
|
|
6
|
-
|
|
7
|
-
describe('Utils - Request', function () {
|
|
8
|
-
afterEach(() => {
|
|
9
|
-
cleanAll();
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
it('should retry a request 5 times if network errors happen', async () => {
|
|
13
|
-
const url = faker.internet.url();
|
|
14
|
-
|
|
15
|
-
const scope = nock(url)
|
|
16
|
-
.get('/')
|
|
17
|
-
.times(5)
|
|
18
|
-
.replyWithError({ code: 'ECONNREFUSED' });
|
|
19
|
-
|
|
20
|
-
const promise = request({ url });
|
|
21
|
-
|
|
22
|
-
await expect(promise).to.eventually.be.rejectedWith(EngineError);
|
|
23
|
-
|
|
24
|
-
scope.isDone();
|
|
25
|
-
});
|
|
26
|
-
});
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import axios, { AxiosResponse, AxiosRequestConfig } from 'axios';
|
|
2
|
-
import { LogSeverity } from 'const';
|
|
3
|
-
import { EngineError, EngineErrorOptions } from 'entities/EngineError';
|
|
4
|
-
|
|
5
|
-
/** Make a http(s) request through axios with proper error handling */
|
|
6
|
-
export async function request<T = unknown>(
|
|
7
|
-
options: AxiosRequestConfig,
|
|
8
|
-
tryCount = 1
|
|
9
|
-
): Promise<AxiosResponse<T>> {
|
|
10
|
-
try {
|
|
11
|
-
return await axios.request<T, AxiosResponse<T>>(options);
|
|
12
|
-
} catch (error: unknown) {
|
|
13
|
-
// In case of networking error, we attempt to retry the connection if we did less than 5 attempts
|
|
14
|
-
if (
|
|
15
|
-
tryCount < 5 &&
|
|
16
|
-
axios.isAxiosError(error) &&
|
|
17
|
-
error.code &&
|
|
18
|
-
['ECONNABORTED', 'ECONNREFUSED', 'ECONNRESET', 'EAI_AGAIN'].includes(
|
|
19
|
-
error.code
|
|
20
|
-
)
|
|
21
|
-
) {
|
|
22
|
-
return request<T>(options, tryCount + 1);
|
|
23
|
-
}
|
|
24
|
-
const errorOptions: EngineErrorOptions = {
|
|
25
|
-
message:
|
|
26
|
-
error instanceof Error
|
|
27
|
-
? error.message
|
|
28
|
-
: typeof error === 'string'
|
|
29
|
-
? error
|
|
30
|
-
: 'An unknown error occurred',
|
|
31
|
-
severity: LogSeverity.CRITICAL,
|
|
32
|
-
data: { error }
|
|
33
|
-
};
|
|
34
|
-
if (error instanceof Error) {
|
|
35
|
-
errorOptions.message = error.message;
|
|
36
|
-
errorOptions.error = error;
|
|
37
|
-
}
|
|
38
|
-
// A response was returned but it was not a 200
|
|
39
|
-
if (axios.isAxiosError(error) && error.response) {
|
|
40
|
-
errorOptions.data = {
|
|
41
|
-
data: error.response.data as unknown,
|
|
42
|
-
status: error.response.status,
|
|
43
|
-
headers: error.response.headers as unknown
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
throw new EngineError(errorOptions);
|
|
47
|
-
}
|
|
48
|
-
}
|