@mojaloop/sdk-scheme-adapter 11.18.8
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/.env.example +140 -0
- package/.eslintignore +2 -0
- package/.eslintrc.json +30 -0
- package/.nvmrc +1 -0
- package/.versionrc +15 -0
- package/CHANGELOG.md +118 -0
- package/InboundServer/api.yaml +3594 -0
- package/InboundServer/api_template.yaml +69 -0
- package/InboundServer/handlers.js +940 -0
- package/InboundServer/index.js +205 -0
- package/InboundServer/middlewares.js +426 -0
- package/OAuthTestServer/index.js +66 -0
- package/OAuthTestServer/model.js +70 -0
- package/OutboundServer/api.yaml +2732 -0
- package/OutboundServer/api_interfaces/index.d.ts +117 -0
- package/OutboundServer/api_interfaces/openapi.d.ts +1475 -0
- package/OutboundServer/api_template/components/parameters/bulkQuoteId.yaml +9 -0
- package/OutboundServer/api_template/components/parameters/bulkTransferId.yaml +9 -0
- package/OutboundServer/api_template/components/parameters/requestToPayTransactionId.yaml +9 -0
- package/OutboundServer/api_template/components/parameters/transferId.yaml +9 -0
- package/OutboundServer/api_template/components/responses/accountsCreationCompleted.yaml +5 -0
- package/OutboundServer/api_template/components/responses/accountsCreationError.yaml +5 -0
- package/OutboundServer/api_template/components/responses/accountsCreationTimeout.yaml +5 -0
- package/OutboundServer/api_template/components/responses/authorizationPostSuccess.yaml +5 -0
- package/OutboundServer/api_template/components/responses/authorizationsServerError.yaml +5 -0
- package/OutboundServer/api_template/components/responses/bulkQuoteBadRequest.yaml +5 -0
- package/OutboundServer/api_template/components/responses/bulkQuoteServerError.yaml +5 -0
- package/OutboundServer/api_template/components/responses/bulkQuoteSuccess.yaml +5 -0
- package/OutboundServer/api_template/components/responses/bulkQuoteTimeout.yaml +5 -0
- package/OutboundServer/api_template/components/responses/bulkTransferBadRequest.yaml +5 -0
- package/OutboundServer/api_template/components/responses/bulkTransferServerError.yaml +5 -0
- package/OutboundServer/api_template/components/responses/bulkTransferSuccess.yaml +5 -0
- package/OutboundServer/api_template/components/responses/bulkTransferTimeout.yaml +5 -0
- package/OutboundServer/api_template/components/responses/partiesByIdError404.yaml +9 -0
- package/OutboundServer/api_template/components/responses/partiesByIdSuccess.yaml +5 -0
- package/OutboundServer/api_template/components/responses/quotesPostSuccess.yaml +5 -0
- package/OutboundServer/api_template/components/responses/quotesServerError.yaml +5 -0
- package/OutboundServer/api_template/components/responses/requestToPaySuccess.yaml +5 -0
- package/OutboundServer/api_template/components/responses/requestToPayTransferBadRequest.yaml +5 -0
- package/OutboundServer/api_template/components/responses/requestToPayTransferSuccess.yaml +5 -0
- package/OutboundServer/api_template/components/responses/simpleTransfersPostSuccess.yaml +5 -0
- package/OutboundServer/api_template/components/responses/simpleTransfersServerError.yaml +5 -0
- package/OutboundServer/api_template/components/responses/transferBadRequest.yaml +5 -0
- package/OutboundServer/api_template/components/responses/transferServerError.yaml +5 -0
- package/OutboundServer/api_template/components/responses/transferSuccess.yaml +5 -0
- package/OutboundServer/api_template/components/responses/transferTimeout.yaml +5 -0
- package/OutboundServer/api_template/components/schemas/accountCreationStatus.yaml +18 -0
- package/OutboundServer/api_template/components/schemas/accountsCreationState.yaml +4 -0
- package/OutboundServer/api_template/components/schemas/accountsRequest.yaml +20 -0
- package/OutboundServer/api_template/components/schemas/accountsResponse.yaml +15 -0
- package/OutboundServer/api_template/components/schemas/async2SyncCurrentState.yaml +5 -0
- package/OutboundServer/api_template/components/schemas/authorizationsPostRequest.yaml +15 -0
- package/OutboundServer/api_template/components/schemas/authorizationsPostResponse.yaml +19 -0
- package/OutboundServer/api_template/components/schemas/bulkQuoteErrorResponse.yaml +8 -0
- package/OutboundServer/api_template/components/schemas/bulkQuoteRequest.yaml +26 -0
- package/OutboundServer/api_template/components/schemas/bulkQuoteResponse.yaml +21 -0
- package/OutboundServer/api_template/components/schemas/bulkQuoteStatus.yaml +4 -0
- package/OutboundServer/api_template/components/schemas/bulkQuoteStatusResponse.yaml +17 -0
- package/OutboundServer/api_template/components/schemas/bulkTransferErrorResponse.yaml +8 -0
- package/OutboundServer/api_template/components/schemas/bulkTransferRequest.yaml +26 -0
- package/OutboundServer/api_template/components/schemas/bulkTransferResponse.yaml +16 -0
- package/OutboundServer/api_template/components/schemas/bulkTransferStatus.yaml +4 -0
- package/OutboundServer/api_template/components/schemas/bulkTransferStatusResponse.yaml +17 -0
- package/OutboundServer/api_template/components/schemas/errorAccountsResponse.yaml +8 -0
- package/OutboundServer/api_template/components/schemas/errorAuthorizationsResponse.yaml +3 -0
- package/OutboundServer/api_template/components/schemas/errorQuotesResponse.yaml +9 -0
- package/OutboundServer/api_template/components/schemas/errorResponse.yaml +8 -0
- package/OutboundServer/api_template/components/schemas/errorSimpleTransfersResponse.yaml +3 -0
- package/OutboundServer/api_template/components/schemas/errorTransferResponse.yaml +8 -0
- package/OutboundServer/api_template/components/schemas/extensionListEmptiable.yaml +6 -0
- package/OutboundServer/api_template/components/schemas/individualQuote.yaml +32 -0
- package/OutboundServer/api_template/components/schemas/individualQuoteResult.yaml +28 -0
- package/OutboundServer/api_template/components/schemas/individualTransfer.yaml +32 -0
- package/OutboundServer/api_template/components/schemas/individualTransferFulfilment.yaml +13 -0
- package/OutboundServer/api_template/components/schemas/individualTransferResult.yaml +41 -0
- package/OutboundServer/api_template/components/schemas/mojaloopError.yaml +5 -0
- package/OutboundServer/api_template/components/schemas/mojaloopTransactionRequestState.yaml +2 -0
- package/OutboundServer/api_template/components/schemas/partiesByIdResponse.yaml +13 -0
- package/OutboundServer/api_template/components/schemas/quote.yaml +3 -0
- package/OutboundServer/api_template/components/schemas/quoteError.yaml +16 -0
- package/OutboundServer/api_template/components/schemas/quotesPostRequest.yaml +13 -0
- package/OutboundServer/api_template/components/schemas/quotesPostResponse.yaml +48 -0
- package/OutboundServer/api_template/components/schemas/requestToPayRequest.yaml +39 -0
- package/OutboundServer/api_template/components/schemas/requestToPayResponse.yaml +41 -0
- package/OutboundServer/api_template/components/schemas/requestToPayTransferRequest.yaml +42 -0
- package/OutboundServer/api_template/components/schemas/requestToPayTransferResponse.yaml +58 -0
- package/OutboundServer/api_template/components/schemas/simpleTransferServerError.yaml +5 -0
- package/OutboundServer/api_template/components/schemas/simpleTransfersPostRequest.yaml +12 -0
- package/OutboundServer/api_template/components/schemas/simpleTransfersPostResponse.yaml +11 -0
- package/OutboundServer/api_template/components/schemas/transactionType.yaml +4 -0
- package/OutboundServer/api_template/components/schemas/transferContinuationAcceptOTP.yaml +9 -0
- package/OutboundServer/api_template/components/schemas/transferContinuationAcceptParty.yaml +8 -0
- package/OutboundServer/api_template/components/schemas/transferContinuationAcceptQuote.yaml +9 -0
- package/OutboundServer/api_template/components/schemas/transferError.yaml +16 -0
- package/OutboundServer/api_template/components/schemas/transferFulfilment.yaml +3 -0
- package/OutboundServer/api_template/components/schemas/transferParty.yaml +40 -0
- package/OutboundServer/api_template/components/schemas/transferRequest.yaml +37 -0
- package/OutboundServer/api_template/components/schemas/transferResponse.yaml +58 -0
- package/OutboundServer/api_template/components/schemas/transferStatus.yaml +6 -0
- package/OutboundServer/api_template/components/schemas/transferStatusResponse.yaml +13 -0
- package/OutboundServer/api_template/health.yaml +12 -0
- package/OutboundServer/api_template/openapi.yaml +55 -0
- package/OutboundServer/api_template/paths/accounts.yaml +26 -0
- package/OutboundServer/api_template/paths/authorizations.yaml +19 -0
- package/OutboundServer/api_template/paths/bulkQuotes.yaml +23 -0
- package/OutboundServer/api_template/paths/bulkQuotes_bulkQuoteId.yaml +24 -0
- package/OutboundServer/api_template/paths/bulkTransfers.yaml +23 -0
- package/OutboundServer/api_template/paths/bulkTransfers_bulkTransferId.yaml +24 -0
- package/OutboundServer/api_template/paths/parties_Type_ID.yaml +20 -0
- package/OutboundServer/api_template/paths/parties_Type_ID_SubId.yaml +22 -0
- package/OutboundServer/api_template/paths/quotes.yaml +20 -0
- package/OutboundServer/api_template/paths/requestToPay.yaml +22 -0
- package/OutboundServer/api_template/paths/requestToPayTransfer.yaml +57 -0
- package/OutboundServer/api_template/paths/requestToPayTransfer_requestToPayTransactionId.yaml +34 -0
- package/OutboundServer/api_template/paths/simpleTransfers.yaml +19 -0
- package/OutboundServer/api_template/paths/transfers.yaml +55 -0
- package/OutboundServer/api_template/paths/transfers_transferId.yaml +58 -0
- package/OutboundServer/handlers.js +622 -0
- package/OutboundServer/index.js +137 -0
- package/OutboundServer/middlewares.js +67 -0
- package/TestServer/api.yaml +62 -0
- package/TestServer/handlers.js +63 -0
- package/TestServer/index.js +215 -0
- package/audit-resolve.json +65 -0
- package/babel.config.js +3 -0
- package/config.js +158 -0
- package/index.d.ts +1 -0
- package/index.js +149 -0
- package/jest.config.js +15 -0
- package/lib/api/index.js +12 -0
- package/lib/cache.js +352 -0
- package/lib/check.js +25 -0
- package/lib/model/AccountsModel.js +396 -0
- package/lib/model/Async2SyncModel.js +283 -0
- package/lib/model/AuthorizationsModel.js +86 -0
- package/lib/model/InboundTransfersModel.js +730 -0
- package/lib/model/OutboundBulkQuotesModel.js +485 -0
- package/lib/model/OutboundBulkTransfersModel.js +479 -0
- package/lib/model/OutboundRequestToPayModel.js +517 -0
- package/lib/model/OutboundRequestToPayTransferModel.js +893 -0
- package/lib/model/OutboundTransfersModel.js +823 -0
- package/lib/model/PartiesModel.js +70 -0
- package/lib/model/ProxyModel/MatchRules/Expression.js +48 -0
- package/lib/model/ProxyModel/MatchRules/Headers.js +65 -0
- package/lib/model/ProxyModel/MatchRules/MatchRule.js +27 -0
- package/lib/model/ProxyModel/MatchRules/Path.js +36 -0
- package/lib/model/ProxyModel/MatchRules/Query.js +65 -0
- package/lib/model/ProxyModel/MatchRules/index.js +19 -0
- package/lib/model/ProxyModel/Route.js +82 -0
- package/lib/model/ProxyModel/configSchema.json +118 -0
- package/lib/model/ProxyModel/index.js +138 -0
- package/lib/model/QuotesModel.js +94 -0
- package/lib/model/TransfersModel.js +81 -0
- package/lib/model/common/BackendError.js +26 -0
- package/lib/model/common/PersistentStateMachine.js +93 -0
- package/lib/model/common/index.js +18 -0
- package/lib/model/index.js +43 -0
- package/lib/model/lib/deferredJob.js +113 -0
- package/lib/model/lib/index.js +9 -0
- package/lib/model/lib/requests/backendRequests.js +227 -0
- package/lib/model/lib/requests/common.js +76 -0
- package/lib/model/lib/requests/index.js +19 -0
- package/lib/model/lib/shared.js +468 -0
- package/lib/randomphrase/index.js +21 -0
- package/lib/randomphrase/words.json +3397 -0
- package/lib/router.js +28 -0
- package/lib/validate.js +205 -0
- package/package.json +102 -0
- package/test/__mocks__/@mojaloop/sdk-standard-components.js +152 -0
- package/test/__mocks__/javascript-state-machine.js +21 -0
- package/test/__mocks__/redis.js +49 -0
- package/test/__mocks__/uuidv4.js +16 -0
- package/test/config/integration.env +136 -0
- package/test/integration/lib/Outbound/authorizations.test.js +58 -0
- package/test/integration/lib/Outbound/data/authorizationsPostRequest.json +43 -0
- package/test/integration/lib/Outbound/data/quotesPostRequest.json +52 -0
- package/test/integration/lib/Outbound/data/transfersPostRequest.json +24 -0
- package/test/integration/lib/Outbound/parties.test.js +28 -0
- package/test/integration/lib/Outbound/quotes.test.js +58 -0
- package/test/integration/lib/Outbound/simpleTransfers.test.js +67 -0
- package/test/integration/lib/cache.test.js +80 -0
- package/test/integration/testEnv.js +7 -0
- package/test/unit/InboundServer.test.js +443 -0
- package/test/unit/TestServer.test.js +394 -0
- package/test/unit/api/accounts/accounts.test.js +128 -0
- package/test/unit/api/accounts/data/postAccountsBody.json +7 -0
- package/test/unit/api/accounts/data/postAccountsErrorMojaloopResponse.json +25 -0
- package/test/unit/api/accounts/data/postAccountsErrorTimeoutResponse.json +19 -0
- package/test/unit/api/accounts/data/postAccountsSuccessResponse.json +17 -0
- package/test/unit/api/accounts/data/postAccountsSuccessResponseWithError1.json +21 -0
- package/test/unit/api/accounts/data/postAccountsSuccessResponseWithError2.json +21 -0
- package/test/unit/api/accounts/utils.js +65 -0
- package/test/unit/api/proxy/data/proxyConfig.yaml +82 -0
- package/test/unit/api/proxy/data/requestBody.json +22 -0
- package/test/unit/api/proxy/data/requestHeaders.json +5 -0
- package/test/unit/api/proxy/data/requestQuery.json +6 -0
- package/test/unit/api/proxy/data/responseBody.json +21 -0
- package/test/unit/api/proxy/data/responseHeaders.json +5 -0
- package/test/unit/api/proxy/proxy.test.js +220 -0
- package/test/unit/api/proxy/utils.js +79 -0
- package/test/unit/api/transfers/data/getTransfersCommittedResponse.json +21 -0
- package/test/unit/api/transfers/data/getTransfersErrorNotFound.json +17 -0
- package/test/unit/api/transfers/data/postQuotesBody.json +52 -0
- package/test/unit/api/transfers/data/postTransfersBadBody.json +17 -0
- package/test/unit/api/transfers/data/postTransfersBody.json +24 -0
- package/test/unit/api/transfers/data/postTransfersErrorMojaloopResponse.json +53 -0
- package/test/unit/api/transfers/data/postTransfersErrorTimeoutResponse.json +47 -0
- package/test/unit/api/transfers/data/postTransfersSimpleBody.json +26 -0
- package/test/unit/api/transfers/data/postTransfersSuccessResponse.json +101 -0
- package/test/unit/api/transfers/data/putPartiesBody.json +20 -0
- package/test/unit/api/transfers/data/putQuotesBody.json +37 -0
- package/test/unit/api/transfers/data/putTransfersBody.json +17 -0
- package/test/unit/api/transfers/transfers.test.js +191 -0
- package/test/unit/api/transfers/utils.js +183 -0
- package/test/unit/api/utils.js +75 -0
- package/test/unit/config.test.js +119 -0
- package/test/unit/data/commonHttpHeaders.json +6 -0
- package/test/unit/data/defaultConfig.json +58 -0
- package/test/unit/data/postQuotesBody.json +52 -0
- package/test/unit/data/putParticipantsBody.json +12 -0
- package/test/unit/data/putPartiesBody.json +20 -0
- package/test/unit/data/testFile.json +29 -0
- package/test/unit/data/testFile.yaml +14 -0
- package/test/unit/inboundApi/data/mockArguments.json +117 -0
- package/test/unit/inboundApi/data/mockTransactionRequest.json +42 -0
- package/test/unit/inboundApi/handlers.test.js +799 -0
- package/test/unit/index.test.js +55 -0
- package/test/unit/lib/cache.test.js +146 -0
- package/test/unit/lib/model/AccountsModel.test.js +121 -0
- package/test/unit/lib/model/AuthorizationsModel.test.js +460 -0
- package/test/unit/lib/model/InboundTransfersModel.test.js +628 -0
- package/test/unit/lib/model/OutboundBulkQuotesModel.test.js +249 -0
- package/test/unit/lib/model/OutboundBulkTransfersModel.test.js +244 -0
- package/test/unit/lib/model/OutboundRequestToPayModel.test.js +166 -0
- package/test/unit/lib/model/OutboundRequestToPayTransferModel.test.js +245 -0
- package/test/unit/lib/model/OutboundTransfersModel.test.js +836 -0
- package/test/unit/lib/model/PartiesModel.test.js +468 -0
- package/test/unit/lib/model/QuotesModel.test.js +470 -0
- package/test/unit/lib/model/TransfersModel.test.js +474 -0
- package/test/unit/lib/model/common/PersistentStateMachine.test.js +179 -0
- package/test/unit/lib/model/data/authorizationsResponse.json +13 -0
- package/test/unit/lib/model/data/bulkQuoteRequest.json +27 -0
- package/test/unit/lib/model/data/bulkQuoteResponse.json +35 -0
- package/test/unit/lib/model/data/bulkTransferFulfil.json +13 -0
- package/test/unit/lib/model/data/bulkTransferRequest.json +29 -0
- package/test/unit/lib/model/data/defaultConfig.json +47 -0
- package/test/unit/lib/model/data/getBulkTransfersBackendResponse.json +42 -0
- package/test/unit/lib/model/data/getBulkTransfersMojaloopResponse.json +22 -0
- package/test/unit/lib/model/data/getTransfersBackendResponse.json +34 -0
- package/test/unit/lib/model/data/getTransfersMojaloopResponse.json +17 -0
- package/test/unit/lib/model/data/mockArguments.json +131 -0
- package/test/unit/lib/model/data/mockTxnRequestsArguments.json +63 -0
- package/test/unit/lib/model/data/notificationToPayee.json +10 -0
- package/test/unit/lib/model/data/payeeParty.json +16 -0
- package/test/unit/lib/model/data/putAuthorizationsResponse.json +10 -0
- package/test/unit/lib/model/data/putQuotesResponse.json +33 -0
- package/test/unit/lib/model/data/putTransfersResponse.json +5 -0
- package/test/unit/lib/model/data/quoteResponse.json +31 -0
- package/test/unit/lib/model/data/requestToPayRequest.json +20 -0
- package/test/unit/lib/model/data/requestToPayTransferRequest.json +27 -0
- package/test/unit/lib/model/data/transactionRequestResponse.json +18 -0
- package/test/unit/lib/model/data/transferFulfil.json +8 -0
- package/test/unit/lib/model/data/transferRequest.json +26 -0
- package/test/unit/lib/model/mockedLibRequests.js +74 -0
- package/test/unit/mockLogger.js +39 -0
- package/test/unit/outboundApi/data/bulkQuoteRequest.json +28 -0
- package/test/unit/outboundApi/data/bulkTransferRequest.json +28 -0
- package/test/unit/outboundApi/data/mockBulkQuoteError.json +45 -0
- package/test/unit/outboundApi/data/mockBulkTransferError.json +48 -0
- package/test/unit/outboundApi/data/mockError.json +41 -0
- package/test/unit/outboundApi/data/mockGetPartiesError.json +4 -0
- package/test/unit/outboundApi/data/mockRequestToPayError.json +32 -0
- package/test/unit/outboundApi/data/mockRequestToPayTransferError.json +39 -0
- package/test/unit/outboundApi/data/requestToPay.json +21 -0
- package/test/unit/outboundApi/data/requestToPayTransferRequest.json +20 -0
- package/test/unit/outboundApi/data/transferRequest.json +21 -0
- package/test/unit/outboundApi/handlers.test.js +986 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"decisions": {
|
|
3
|
+
"1556|@mojaloop/central-services-shared>widdershins>node-fetch": {
|
|
4
|
+
"decision": "fix",
|
|
5
|
+
"madeAt": 1624032509533
|
|
6
|
+
},
|
|
7
|
+
"1589|@mojaloop/central-services-shared>rc>ini": {
|
|
8
|
+
"decision": "fix",
|
|
9
|
+
"madeAt": 1624032519962
|
|
10
|
+
},
|
|
11
|
+
"1654|@mojaloop/central-services-shared>widdershins>swagger2openapi>yargs>y18n": {
|
|
12
|
+
"decision": "fix",
|
|
13
|
+
"madeAt": 1624032531602
|
|
14
|
+
},
|
|
15
|
+
"1654|@mojaloop/central-services-shared>widdershins>yargs>y18n": {
|
|
16
|
+
"decision": "fix",
|
|
17
|
+
"madeAt": 1624032531602
|
|
18
|
+
},
|
|
19
|
+
"1673|@mojaloop/central-services-shared>openapi-backend>lodash": {
|
|
20
|
+
"decision": "fix",
|
|
21
|
+
"madeAt": 1624032541954
|
|
22
|
+
},
|
|
23
|
+
"1673|@mojaloop/central-services-shared>shins>sanitize-html>lodash": {
|
|
24
|
+
"decision": "fix",
|
|
25
|
+
"madeAt": 1624032541954
|
|
26
|
+
},
|
|
27
|
+
"1673|koa2-oauth-server>00unidentified>lodash": {
|
|
28
|
+
"decision": "fix",
|
|
29
|
+
"madeAt": 1624032544849
|
|
30
|
+
},
|
|
31
|
+
"1500|@mojaloop/central-services-shared>widdershins>yargs>yargs-parser": {
|
|
32
|
+
"decision": "ignore",
|
|
33
|
+
"madeAt": 1631785234887,
|
|
34
|
+
"expiresAt": 1632390016480
|
|
35
|
+
},
|
|
36
|
+
"1675|@mojaloop/central-services-shared>shins>sanitize-html": {
|
|
37
|
+
"decision": "ignore",
|
|
38
|
+
"madeAt": 1631785236962,
|
|
39
|
+
"expiresAt": 1632390016480
|
|
40
|
+
},
|
|
41
|
+
"1676|@mojaloop/central-services-shared>shins>sanitize-html": {
|
|
42
|
+
"decision": "ignore",
|
|
43
|
+
"madeAt": 1631785236962,
|
|
44
|
+
"expiresAt": 1632390016480
|
|
45
|
+
},
|
|
46
|
+
"1766|@mojaloop/central-services-shared>widdershins>urijs": {
|
|
47
|
+
"decision": "fix",
|
|
48
|
+
"madeAt": 1627405029179
|
|
49
|
+
},
|
|
50
|
+
"1779|@mojaloop/event-sdk>grpc>@mapbox/node-pre-gyp>tar": {
|
|
51
|
+
"decision": "fix",
|
|
52
|
+
"madeAt": 1631785230676
|
|
53
|
+
},
|
|
54
|
+
"1780|@mojaloop/event-sdk>grpc>@mapbox/node-pre-gyp>tar": {
|
|
55
|
+
"decision": "fix",
|
|
56
|
+
"madeAt": 1631785230676
|
|
57
|
+
},
|
|
58
|
+
"1781|@mojaloop/event-sdk>grpc>@mapbox/node-pre-gyp>tar": {
|
|
59
|
+
"decision": "fix",
|
|
60
|
+
"madeAt": 1631785230676
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"rules": {},
|
|
64
|
+
"version": 1
|
|
65
|
+
}
|
package/babel.config.js
ADDED
package/config.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**************************************************************************
|
|
2
|
+
* (C) Copyright ModusBox Inc. 2019 - All rights reserved. *
|
|
3
|
+
* *
|
|
4
|
+
* This file is made available under the terms of the license agreement *
|
|
5
|
+
* specified in the corresponding source code repository. *
|
|
6
|
+
* *
|
|
7
|
+
* ORIGINAL AUTHOR: *
|
|
8
|
+
* James Bush - james.bush@modusbox.com *
|
|
9
|
+
**************************************************************************/
|
|
10
|
+
'use strict';
|
|
11
|
+
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
require('dotenv').config();
|
|
14
|
+
const { from } = require('env-var');
|
|
15
|
+
const yaml = require('js-yaml');
|
|
16
|
+
|
|
17
|
+
function getFileContent (path) {
|
|
18
|
+
if (!fs.existsSync(path)) {
|
|
19
|
+
throw new Error('File doesn\'t exist');
|
|
20
|
+
}
|
|
21
|
+
return fs.readFileSync(path);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Gets Resources versions from enviromental variable RESOURCES_VERSIONS
|
|
26
|
+
* should be string in format: "resouceOneName=1.0,resourceTwoName=1.1"
|
|
27
|
+
*/
|
|
28
|
+
function getVersionFromConfig (resourceString) {
|
|
29
|
+
const resourceVersionMap = {};
|
|
30
|
+
resourceString
|
|
31
|
+
.split(',')
|
|
32
|
+
.forEach(e => e.split('=')
|
|
33
|
+
.reduce((p, c) => {
|
|
34
|
+
resourceVersionMap[p] = {
|
|
35
|
+
contentVersion: c,
|
|
36
|
+
acceptVersion: c.split('.')[0],
|
|
37
|
+
};
|
|
38
|
+
}));
|
|
39
|
+
return resourceVersionMap;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function parseResourceVersions (resourceString) {
|
|
43
|
+
if (!resourceString) return {};
|
|
44
|
+
const resourceFormatRegex = /(([A-Za-z])\w*)=([0-9]+).([0-9]+)([^;:|],*)/g;
|
|
45
|
+
const noSpResources = resourceString.replace(/\s/g,'');
|
|
46
|
+
if (!resourceFormatRegex.test(noSpResources)) {
|
|
47
|
+
throw new Error('Resource versions format should be in format: "resouceOneName=1.0,resourceTwoName=1.1"');
|
|
48
|
+
}
|
|
49
|
+
return getVersionFromConfig(noSpResources);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const env = from(process.env, {
|
|
53
|
+
asFileContent: (path) => getFileContent(path),
|
|
54
|
+
asFileListContent: (pathList) => pathList.split(',').map((path) => getFileContent(path)),
|
|
55
|
+
asYamlConfig: (path) => yaml.load(getFileContent(path)),
|
|
56
|
+
asResourceVersions: (resourceString) => parseResourceVersions(resourceString),
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
module.exports = {
|
|
60
|
+
__parseResourceVersion: parseResourceVersions,
|
|
61
|
+
mutualTLS: {
|
|
62
|
+
inboundRequests: {
|
|
63
|
+
enabled: env.get('INBOUND_MUTUAL_TLS_ENABLED').default('false').asBool(),
|
|
64
|
+
creds: {
|
|
65
|
+
ca: env.get('IN_CA_CERT_PATH').asFileListContent(),
|
|
66
|
+
cert: env.get('IN_SERVER_CERT_PATH').asFileContent(),
|
|
67
|
+
key: env.get('IN_SERVER_KEY_PATH').asFileContent(),
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
outboundRequests: {
|
|
71
|
+
enabled: env.get('OUTBOUND_MUTUAL_TLS_ENABLED').default('false').asBool(),
|
|
72
|
+
creds: {
|
|
73
|
+
ca: env.get('OUT_CA_CERT_PATH').asFileListContent(),
|
|
74
|
+
cert: env.get('OUT_CLIENT_CERT_PATH').asFileContent(),
|
|
75
|
+
key: env.get('OUT_CLIENT_KEY_PATH').asFileContent(),
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
inboundServerPort: env.get('INBOUND_LISTEN_PORT').default('4000').asPortNumber(),
|
|
80
|
+
outboundServerPort: env.get('OUTBOUND_LISTEN_PORT').default('4001').asPortNumber(),
|
|
81
|
+
testServerPort: env.get('TEST_LISTEN_PORT').default('4002').asPortNumber(),
|
|
82
|
+
peerEndpoint: env.get('PEER_ENDPOINT').required().asString(),
|
|
83
|
+
alsEndpoint: env.get('ALS_ENDPOINT').asString(),
|
|
84
|
+
quotesEndpoint: env.get('QUOTES_ENDPOINT').asString(),
|
|
85
|
+
bulkQuotesEndpoint: env.get('BULK_QUOTES_ENDPOINT').asString(),
|
|
86
|
+
transactionRequestsEndpoint: env.get('TRANSACTION_REQUESTS_ENDPOINT').asString(),
|
|
87
|
+
transfersEndpoint: env.get('TRANSFERS_ENDPOINT').asString(),
|
|
88
|
+
bulkTransfersEndpoint: env.get('BULK_TRANSFERS_ENDPOINT').asString(),
|
|
89
|
+
backendEndpoint: env.get('BACKEND_ENDPOINT').required().asString(),
|
|
90
|
+
|
|
91
|
+
dfspId: env.get('DFSP_ID').default('mojaloop').asString(),
|
|
92
|
+
ilpSecret: env.get('ILP_SECRET').default('mojaloop-sdk').asString(),
|
|
93
|
+
checkIlp: env.get('CHECK_ILP').default('true').asBool(),
|
|
94
|
+
expirySeconds: env.get('EXPIRY_SECONDS').default('60').asIntPositive(),
|
|
95
|
+
|
|
96
|
+
autoAcceptQuotes: env.get('AUTO_ACCEPT_QUOTES').default('true').asBool(),
|
|
97
|
+
autoAcceptParty: env.get('AUTO_ACCEPT_PARTY').default('true').asBool(),
|
|
98
|
+
autoAcceptR2PBusinessQuotes: env.get('AUTO_ACCEPT_R2P_BUSINESS_QUOTES').default('false').asBool(),
|
|
99
|
+
autoAcceptR2PDeviceQuotes: env.get('AUTO_ACCEPT_R2P_DEVICE_QUOTES').default('true').asBool(),
|
|
100
|
+
autoAcceptR2PDeviceOTP: env.get('AUTO_ACCEPT_R2P_DEVICE_OTP').default('false').asBool(),
|
|
101
|
+
autoAcceptParticipantsPut: env.get('AUTO_ACCEPT_PARTICIPANTS_PUT').default('false').asBool(),
|
|
102
|
+
|
|
103
|
+
/* TODO: high-risk transactions can require additional clearing check */
|
|
104
|
+
// enableClearingCheck: env.get('ENABLE_CLEARING_CHECK').default('false').asBool(),
|
|
105
|
+
|
|
106
|
+
useQuoteSourceFSPAsTransferPayeeFSP: env.get('USE_QUOTE_SOURCE_FSP_AS_TRANSFER_PAYEE_FSP').default('false').asBool(),
|
|
107
|
+
|
|
108
|
+
// Getting secrets from files instead of environment variables reduces the likelihood of
|
|
109
|
+
// accidental leakage.
|
|
110
|
+
|
|
111
|
+
validateInboundJws: env.get('VALIDATE_INBOUND_JWS').default('true').asBool(),
|
|
112
|
+
validateInboundPutPartiesJws: env.get('VALIDATE_INBOUND_PUT_PARTIES_JWS').default('false').asBool(),
|
|
113
|
+
jwsSign: env.get('JWS_SIGN').default('true').asBool(),
|
|
114
|
+
jwsSignPutParties: env.get('JWS_SIGN_PUT_PARTIES').default('false').asBool(),
|
|
115
|
+
jwsSigningKey: env.get('JWS_SIGNING_KEY_PATH').asFileContent(),
|
|
116
|
+
jwsVerificationKeysDirectory: env.get('JWS_VERIFICATION_KEYS_DIRECTORY').asString(),
|
|
117
|
+
cacheConfig: {
|
|
118
|
+
host: env.get('CACHE_HOST').required().asString(),
|
|
119
|
+
port: env.get('CACHE_PORT').required().asPortNumber(),
|
|
120
|
+
},
|
|
121
|
+
enableTestFeatures: env.get('ENABLE_TEST_FEATURES').default('false').asBool(),
|
|
122
|
+
oauthTestServer: {
|
|
123
|
+
enabled: env.get('ENABLE_OAUTH_TOKEN_ENDPOINT').default('false').asBool(),
|
|
124
|
+
clientKey: env.get('OAUTH_TOKEN_ENDPOINT_CLIENT_KEY').asString(),
|
|
125
|
+
clientSecret: env.get('OAUTH_TOKEN_ENDPOINT_CLIENT_SECRET').asString(),
|
|
126
|
+
listenPort: env.get('OAUTH_TOKEN_ENDPOINT_LISTEN_PORT').asPortNumber(),
|
|
127
|
+
},
|
|
128
|
+
wso2: {
|
|
129
|
+
auth: {
|
|
130
|
+
staticToken: env.get('WSO2_BEARER_TOKEN').asString(),
|
|
131
|
+
tokenEndpoint: env.get('OAUTH_TOKEN_ENDPOINT').asString(),
|
|
132
|
+
clientKey: env.get('OAUTH_CLIENT_KEY').asString(),
|
|
133
|
+
clientSecret: env.get('OAUTH_CLIENT_SECRET').asString(),
|
|
134
|
+
refreshSeconds: env.get('OAUTH_REFRESH_SECONDS').default('60').asIntPositive(),
|
|
135
|
+
},
|
|
136
|
+
requestAuthFailureRetryTimes: env.get('WSO2_AUTH_FAILURE_REQUEST_RETRIES').default('0').asIntPositive(),
|
|
137
|
+
},
|
|
138
|
+
rejectExpiredQuoteResponses: env.get('REJECT_EXPIRED_QUOTE_RESPONSES').default('false').asBool(),
|
|
139
|
+
rejectTransfersOnExpiredQuotes: env.get('REJECT_TRANSFERS_ON_EXPIRED_QUOTES').default('false').asBool(),
|
|
140
|
+
rejectExpiredTransferFulfils: env.get('REJECT_EXPIRED_TRANSFER_FULFILS').default('false').asBool(),
|
|
141
|
+
|
|
142
|
+
requestProcessingTimeoutSeconds: env.get('REQUEST_PROCESSING_TIMEOUT_SECONDS').default('30').asIntPositive(),
|
|
143
|
+
|
|
144
|
+
logIndent: env.get('LOG_INDENT').default('2').asIntPositive(),
|
|
145
|
+
|
|
146
|
+
allowTransferWithoutQuote: env.get('ALLOW_TRANSFER_WITHOUT_QUOTE').default('false').asBool(),
|
|
147
|
+
|
|
148
|
+
// for outbound transfers, allows an extensionList item in an error respone to be used instead
|
|
149
|
+
// of the primary error code when setting the statusCode property on the synchronous response
|
|
150
|
+
// to the DFSP backend. This is useful if an intermediary such as FXP returns underlying error
|
|
151
|
+
// codes in error extensionLists.
|
|
152
|
+
outboundErrorStatusCodeExtensionKey: env.get('OUTBOUND_ERROR_STATUSCODE_EXTENSION_KEY').asString(),
|
|
153
|
+
|
|
154
|
+
proxyConfig: env.get('PROXY_CONFIG_PATH').asYamlConfig(),
|
|
155
|
+
reserveNotification: env.get('RESERVE_NOTIFICATION').default('false').asBool(),
|
|
156
|
+
// resourceVersions config should be string in format: "resouceOneName=1.0,resourceTwoName=1.1"
|
|
157
|
+
resourceVersions: env.get('RESOURCE_VERSIONS').default('').asResourceVersions()
|
|
158
|
+
};
|
package/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * as OutboundAPI from './OutboundServer/api_interfaces';
|
package/index.js
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**************************************************************************
|
|
2
|
+
* (C) Copyright ModusBox Inc. 2019 - All rights reserved. *
|
|
3
|
+
* *
|
|
4
|
+
* This file is made available under the terms of the license agreement *
|
|
5
|
+
* specified in the corresponding source code repository. *
|
|
6
|
+
* *
|
|
7
|
+
* ORIGINAL AUTHOR: *
|
|
8
|
+
* James Bush - james.bush@modusbox.com *
|
|
9
|
+
**************************************************************************/
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
const { hostname } = require('os');
|
|
14
|
+
const config = require('./config');
|
|
15
|
+
const EventEmitter = require('events');
|
|
16
|
+
|
|
17
|
+
const InboundServer = require('./InboundServer');
|
|
18
|
+
const OutboundServer = require('./OutboundServer');
|
|
19
|
+
const OAuthTestServer = require('./OAuthTestServer');
|
|
20
|
+
const TestServer = require('./TestServer');
|
|
21
|
+
|
|
22
|
+
// import things we want to expose e.g. for unit tests and users who dont want to use the entire
|
|
23
|
+
// scheme adapter as a service
|
|
24
|
+
const InboundServerMiddleware = require('./InboundServer/middlewares.js');
|
|
25
|
+
const OutboundServerMiddleware = require('./OutboundServer/middlewares.js');
|
|
26
|
+
const Router = require('./lib/router');
|
|
27
|
+
const Validate = require('./lib/validate');
|
|
28
|
+
const RandomPhrase = require('./lib/randomphrase');
|
|
29
|
+
const Cache = require('./lib/cache');
|
|
30
|
+
const { Logger } = require('@mojaloop/sdk-standard-components');
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Class that creates and manages http servers that expose the scheme adapter APIs.
|
|
34
|
+
*/
|
|
35
|
+
class Server extends EventEmitter {
|
|
36
|
+
constructor(conf, logger) {
|
|
37
|
+
super({ captureExceptions: true });
|
|
38
|
+
this.conf = conf;
|
|
39
|
+
this.logger = logger;
|
|
40
|
+
this.cache = new Cache({
|
|
41
|
+
...conf.cacheConfig,
|
|
42
|
+
logger: this.logger.push({ component: 'cache' }),
|
|
43
|
+
enableTestFeatures: conf.enableTestFeatures,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
this.inboundServer = new InboundServer(
|
|
47
|
+
this.conf,
|
|
48
|
+
this.logger.push({ app: 'mojaloop-sdk-inbound-api' }),
|
|
49
|
+
this.cache
|
|
50
|
+
);
|
|
51
|
+
this.inboundServer.on('error', (...args) => {
|
|
52
|
+
this.logger.push({ args }).log('Unhandled error in Inbound Server');
|
|
53
|
+
this.emit('error', 'Unhandled error in Inbound Server');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
this.outboundServer = new OutboundServer(
|
|
57
|
+
this.conf,
|
|
58
|
+
this.logger.push({ app: 'mojaloop-sdk-outbound-api' }),
|
|
59
|
+
this.cache
|
|
60
|
+
);
|
|
61
|
+
this.outboundServer.on('error', (...args) => {
|
|
62
|
+
this.logger.push({ args }).log('Unhandled error in Outbound Server');
|
|
63
|
+
this.emit('error', 'Unhandled error in Outbound Server');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
this.oauthTestServer = new OAuthTestServer({
|
|
67
|
+
clientKey: this.conf.oauthTestServer.clientKey,
|
|
68
|
+
clientSecret: this.conf.oauthTestServer.clientSecret,
|
|
69
|
+
port: this.conf.oauthTestServer.listenPort,
|
|
70
|
+
logger: this.logger.push({ app: 'mojaloop-sdk-oauth-test-server' }),
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
this.testServer = new TestServer({
|
|
74
|
+
port: this.conf.testServerPort,
|
|
75
|
+
logger: this.logger.push({ app: 'mojaloop-sdk-test-api' }),
|
|
76
|
+
cache: this.cache,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async start() {
|
|
81
|
+
await this.cache.connect();
|
|
82
|
+
|
|
83
|
+
const startTestServer = this.conf.enableTestFeatures ? this.testServer.start() : null;
|
|
84
|
+
const startOauthTestServer = this.conf.oauthTestServer.enabled
|
|
85
|
+
? this.oauthTestServer.start()
|
|
86
|
+
: null;
|
|
87
|
+
await Promise.all([
|
|
88
|
+
this.inboundServer.start(),
|
|
89
|
+
this.outboundServer.start(),
|
|
90
|
+
startTestServer,
|
|
91
|
+
startOauthTestServer,
|
|
92
|
+
]);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
stop() {
|
|
96
|
+
return Promise.all([
|
|
97
|
+
this.inboundServer.stop(),
|
|
98
|
+
this.outboundServer.stop(),
|
|
99
|
+
this.oauthTestServer.stop(),
|
|
100
|
+
this.testServer.stop(),
|
|
101
|
+
]);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
if(require.main === module) {
|
|
107
|
+
(async () => {
|
|
108
|
+
// this module is main i.e. we were started as a server;
|
|
109
|
+
// not used in unit test or "require" scenarios
|
|
110
|
+
const logger = new Logger.Logger({
|
|
111
|
+
context: {
|
|
112
|
+
// If we're running from a Mojaloop helm chart deployment, we'll have a SIM_NAME
|
|
113
|
+
simulator: process.env['SIM_NAME'],
|
|
114
|
+
hostname: hostname(),
|
|
115
|
+
},
|
|
116
|
+
stringify: Logger.buildStringify({ space: config.logIndent }),
|
|
117
|
+
});
|
|
118
|
+
const svr = new Server(config, logger);
|
|
119
|
+
svr.on('error', (err) => {
|
|
120
|
+
logger.push({ err }).log('Unhandled server error');
|
|
121
|
+
process.exit(1);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// handle SIGTERM to exit gracefully
|
|
125
|
+
process.on('SIGTERM', async () => {
|
|
126
|
+
logger.log('SIGTERM received. Shutting down APIs...');
|
|
127
|
+
await svr.stop();
|
|
128
|
+
process.exit(0);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
svr.start().catch(err => {
|
|
132
|
+
logger.push({ err }).log('Error starting server');
|
|
133
|
+
process.exit(1);
|
|
134
|
+
});
|
|
135
|
+
})();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
// export things we want to expose e.g. for unit tests and users who dont want to use the entire
|
|
140
|
+
// scheme adapter as a service
|
|
141
|
+
module.exports = {
|
|
142
|
+
Cache,
|
|
143
|
+
InboundServerMiddleware,
|
|
144
|
+
OutboundServerMiddleware,
|
|
145
|
+
RandomPhrase,
|
|
146
|
+
Router,
|
|
147
|
+
Server,
|
|
148
|
+
Validate,
|
|
149
|
+
};
|
package/jest.config.js
ADDED
package/lib/api/index.js
ADDED
package/lib/cache.js
ADDED
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
/**************************************************************************
|
|
2
|
+
* (C) Copyright ModusBox Inc. 2019 - All rights reserved. *
|
|
3
|
+
* *
|
|
4
|
+
* This file is made available under the terms of the license agreement *
|
|
5
|
+
* specified in the corresponding source code repository. *
|
|
6
|
+
* *
|
|
7
|
+
* ORIGINAL AUTHOR: *
|
|
8
|
+
* James Bush - james.bush@modusbox.com *
|
|
9
|
+
**************************************************************************/
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
const util = require('util');
|
|
14
|
+
const redis = require('redis');
|
|
15
|
+
|
|
16
|
+
const CONN_ST = {
|
|
17
|
+
CONNECTED: 'CONNECTED',
|
|
18
|
+
CONNECTING: 'CONNECTING',
|
|
19
|
+
DISCONNECTED: 'DISCONNECTED',
|
|
20
|
+
DISCONNECTING: 'DISCONNECTING',
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* A shared cache abstraction over a REDIS distributed key/value store
|
|
25
|
+
*/
|
|
26
|
+
class Cache {
|
|
27
|
+
constructor(config) {
|
|
28
|
+
this._config = config;
|
|
29
|
+
|
|
30
|
+
if(!config.host || !config.port || !config.logger) {
|
|
31
|
+
throw new Error('Cache config requires host, port and logger properties');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
this._logger = config.logger;
|
|
35
|
+
|
|
36
|
+
// a redis connection to handle get, set and publish operations
|
|
37
|
+
this._client = null;
|
|
38
|
+
|
|
39
|
+
// connection/disconnection logic
|
|
40
|
+
this._connectionState = CONN_ST.DISCONNECTED;
|
|
41
|
+
|
|
42
|
+
// a redis connection to handle subscribe operations and published message routing
|
|
43
|
+
// Note that REDIS docs suggest a client that is in SUBSCRIBE mode
|
|
44
|
+
// should not have any other commands executed against it.
|
|
45
|
+
// see: https://redis.io/topics/pubsub
|
|
46
|
+
this._subscriptionClient = null;
|
|
47
|
+
|
|
48
|
+
// a 'hashmap like' callback map
|
|
49
|
+
this._callbacks = {};
|
|
50
|
+
|
|
51
|
+
// tag each callback with an Id so we can gracefully unsubscribe and not leak resources
|
|
52
|
+
this._callbackId = 0;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Connects to a redis server and waits for ready events
|
|
57
|
+
* Note: We create two connections. One for get, set and publish commands
|
|
58
|
+
* and another for subscribe commands. We do this as we are not supposed
|
|
59
|
+
* to issue any non-pub/sub related commands on a connection used for sub
|
|
60
|
+
* See: https://redis.io/topics/pubsub
|
|
61
|
+
*/
|
|
62
|
+
async connect() {
|
|
63
|
+
switch(this._connectionState) {
|
|
64
|
+
case CONN_ST.CONNECTED:
|
|
65
|
+
return;
|
|
66
|
+
case CONN_ST.CONNECTING:
|
|
67
|
+
await this._inProgressConnection;
|
|
68
|
+
return;
|
|
69
|
+
case CONN_ST.DISCONNECTED:
|
|
70
|
+
break;
|
|
71
|
+
case CONN_ST.DISCONNECTING:
|
|
72
|
+
// TODO: should this be an error?
|
|
73
|
+
// If we're disconnecting, we'll let that finish first
|
|
74
|
+
await this._inProgressDisconnection;
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
this._connectionState = CONN_ST.CONNECTING;
|
|
79
|
+
this._inProgressConnection = Promise.all([this._getClient(), this._getClient()]);
|
|
80
|
+
[this._client, this._subscriptionClient] = await this._inProgressConnection;
|
|
81
|
+
|
|
82
|
+
// hook up our sub message handler
|
|
83
|
+
this._subscriptionClient.on('message', this._onMessage.bind(this));
|
|
84
|
+
|
|
85
|
+
this._inProgressConnection = null;
|
|
86
|
+
this._connectionState = CONN_ST.CONNECTED;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Configure Redis to emit keyevent events. This corresponds to the application test mode, and
|
|
91
|
+
* enables us to listen for changes on callback_* and request_* keys.
|
|
92
|
+
* Docs: https://redis.io/topics/notifications
|
|
93
|
+
*/
|
|
94
|
+
async setTestMode(enable) {
|
|
95
|
+
// See for modes: https://redis.io/topics/notifications#configuration
|
|
96
|
+
// This mode, 'Es$' is:
|
|
97
|
+
// E Keyevent events, published with __keyevent@<db>__ prefix.
|
|
98
|
+
// s Set commands
|
|
99
|
+
// $ String commands
|
|
100
|
+
const mode = enable ? 'Es$' : '';
|
|
101
|
+
this._logger
|
|
102
|
+
.push({ 'notify-keyspace-events': mode })
|
|
103
|
+
.log('REDIS client Configured to emit keyspace-events');
|
|
104
|
+
this._client.config('SET', 'notify-keyspace-events', mode);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async disconnect() {
|
|
108
|
+
switch(this._connectionState) {
|
|
109
|
+
case CONN_ST.CONNECTED:
|
|
110
|
+
break;
|
|
111
|
+
case CONN_ST.CONNECTING:
|
|
112
|
+
// TODO: should this be an error?
|
|
113
|
+
// If we're connecting, we'll let that finish first
|
|
114
|
+
await this._inProgressConnection;
|
|
115
|
+
break;
|
|
116
|
+
case CONN_ST.DISCONNECTED:
|
|
117
|
+
return;
|
|
118
|
+
case CONN_ST.DISCONNECTING:
|
|
119
|
+
await this._inProgressDisconnection;
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
this._connectionState = CONN_ST.DISCONNECTING;
|
|
123
|
+
this._inProgressDisconnection = Promise.all([
|
|
124
|
+
new Promise(resolve => this._client.quit(resolve)),
|
|
125
|
+
new Promise(resolve => this._subscriptionClient.quit(resolve))
|
|
126
|
+
]);
|
|
127
|
+
this._client = null;
|
|
128
|
+
this._subscriptionClient = null;
|
|
129
|
+
await this._inProgressDisconnection;
|
|
130
|
+
this._inProgressDisconnection = null;
|
|
131
|
+
this._connectionState = CONN_ST.DISCONNECTED;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Subscribes to a channel
|
|
137
|
+
*
|
|
138
|
+
* @param channel {string} - The channel name to subscribe to
|
|
139
|
+
* @param callback {function} - Callback function to be executed when messages arrive on the specified channel
|
|
140
|
+
* @returns {Promise} - Promise that resolves with an integer callback Id to submit in unsubscribe request
|
|
141
|
+
*/
|
|
142
|
+
async subscribe(channel, callback) {
|
|
143
|
+
return new Promise((resolve, reject) => {
|
|
144
|
+
this._subscriptionClient.subscribe(channel, (err) => {
|
|
145
|
+
if(err) {
|
|
146
|
+
this._logger.log(`Error subscribing to channel ${channel}: ${err.stack || util.inspect(err)}`);
|
|
147
|
+
return reject(err);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
this._logger.log(`Subscribed to cache pub/sub channel ${channel}`);
|
|
151
|
+
|
|
152
|
+
if(!this._callbacks[channel]) {
|
|
153
|
+
// if this is the first subscriber for this channel we init the hashmap
|
|
154
|
+
this._callbacks[channel] = {};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// get an id for this callback
|
|
158
|
+
const id = this._callbackId++;
|
|
159
|
+
|
|
160
|
+
// store the callback against the channel/id
|
|
161
|
+
this._callbacks[channel][id] = callback;
|
|
162
|
+
|
|
163
|
+
// return the id we gave the callback
|
|
164
|
+
return resolve(id);
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Unsubscribes a callback from a channel
|
|
172
|
+
*
|
|
173
|
+
* @param channel {string} - name of the channel to unsubscribe from
|
|
174
|
+
* @param callbackId {integer} - id of the callback to remove
|
|
175
|
+
*/
|
|
176
|
+
async unsubscribe(channel, callbackId) {
|
|
177
|
+
return new Promise((resolve, reject) => {
|
|
178
|
+
if(this._callbacks[channel] && this._callbacks[channel][callbackId]) {
|
|
179
|
+
delete this._callbacks[channel][callbackId];
|
|
180
|
+
this._logger.log(`Cache unsubscribed callbackId ${callbackId} from channel ${channel}`);
|
|
181
|
+
|
|
182
|
+
if(Object.keys(this._callbacks[channel]).length < 1) {
|
|
183
|
+
//no more callbacks for this channel
|
|
184
|
+
delete this._callbacks[channel];
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return resolve();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// we should not be asked to unsubscribe from a subscription we do not have. Raise this as a promise
|
|
191
|
+
// rejection so it can be spotted. It may indiate a logic bug somewhere else
|
|
192
|
+
this._logger.log(`Cache not subscribed to channel ${channel} for callbackId ${callbackId}`);
|
|
193
|
+
return reject(new Error(`Channel ${channel} does not have a callback with id ${callbackId} subscribed`));
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Handler for published messages
|
|
200
|
+
*/
|
|
201
|
+
async _onMessage(channel, msg) {
|
|
202
|
+
if(this._callbacks[channel]) {
|
|
203
|
+
// we have some callbacks to make
|
|
204
|
+
Object.keys(this._callbacks[channel]).forEach(k => {
|
|
205
|
+
this._logger.log(`Cache message received on channel ${channel}. Making callback with id ${k}`);
|
|
206
|
+
|
|
207
|
+
// call the callback with the channel name, message and callbackId...
|
|
208
|
+
// ...(which is useful for unsubscribe)
|
|
209
|
+
try {
|
|
210
|
+
this._callbacks[channel][k](channel, msg, k);
|
|
211
|
+
} catch (err) {
|
|
212
|
+
this._logger
|
|
213
|
+
.push({ callbackId: k, err })
|
|
214
|
+
.log('Unhandled error in cache subscription handler');
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Returns a new redis client
|
|
223
|
+
*
|
|
224
|
+
* @returns {object} - a connected REDIS client
|
|
225
|
+
* */
|
|
226
|
+
async _getClient() {
|
|
227
|
+
return new Promise((resolve, reject) => {
|
|
228
|
+
const client = redis.createClient(this._config);
|
|
229
|
+
|
|
230
|
+
client.on('error', (err) => {
|
|
231
|
+
this._logger.push({ err }).log('REDIS client Error');
|
|
232
|
+
return reject(err);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
client.on('reconnecting', (err) => {
|
|
236
|
+
this._logger.push({ err }).log('REDIS client Reconnecting');
|
|
237
|
+
return reject(err);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
client.on('subscribe', (channel, count) => {
|
|
241
|
+
this._logger.push({ channel, count }).log('REDIS client subscribe');
|
|
242
|
+
// On a subscribe event, ensure that testFeatures are enabled.
|
|
243
|
+
// This is required here in the advent of a disconnect/reconnect event. Redis client will re-subscribe all subscriptions, but previously enabledTestFeatures will be lost.
|
|
244
|
+
// Handling this on the on subscribe event will ensure its always configured.
|
|
245
|
+
if (this._config.enableTestFeatures) {
|
|
246
|
+
this.setTestMode(true);
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
client.on('ready', () => {
|
|
251
|
+
this._logger.log(`REDIS client ready at: ${this._config.host}:${this._config.port}`);
|
|
252
|
+
return resolve(client);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
client.on('connect', () => {
|
|
256
|
+
this._logger.log(`REDIS client connected at: ${this._config.host}:${this._config.port}`);
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Publishes the specified message to the specified channel
|
|
264
|
+
*
|
|
265
|
+
* @param channelName {string} - channel name to publish to
|
|
266
|
+
* @param value - any type that will be converted to a JSON string (unless it is already a string) and published as the message
|
|
267
|
+
* @returns {Promise} - Promise that will resolve with redis replies or reject with an error
|
|
268
|
+
*/
|
|
269
|
+
async publish(channelName, value) {
|
|
270
|
+
return new Promise((resolve, reject) => {
|
|
271
|
+
if(typeof(value) !== 'string') {
|
|
272
|
+
// ALWAYS publish string values
|
|
273
|
+
value = JSON.stringify(value);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// note that we publish on the non-SUBSCRIBE connection
|
|
277
|
+
this._client.publish(channelName, value, (err, replies) => {
|
|
278
|
+
if(err) {
|
|
279
|
+
this._logger.push({ channelName, err }).log(`Error publishing to channel ${channelName}`);
|
|
280
|
+
return reject(err);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
this._logger.push({ channelName, value }).log(`Published to channel ${channelName}`);
|
|
284
|
+
return resolve(replies);
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Sets a value in the cache
|
|
292
|
+
*
|
|
293
|
+
* @param key {string} - cache key
|
|
294
|
+
* @param value {stirng} - cache value
|
|
295
|
+
*/
|
|
296
|
+
async set(key, value) {
|
|
297
|
+
return new Promise((resolve, reject) => {
|
|
298
|
+
//if we are given an object, turn it into a string
|
|
299
|
+
if(typeof(value) !== 'string') {
|
|
300
|
+
value = JSON.stringify(value);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
this._client.set(key, value, (err, replies) => {
|
|
304
|
+
if(err) {
|
|
305
|
+
this._logger.push({ key, value, err }).log(`Error setting cache key: ${key}`);
|
|
306
|
+
return reject(err);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
this._logger.push({ key, value, replies }).log(`Set cache key: ${key}`);
|
|
310
|
+
return resolve(replies);
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Gets a value from the cache
|
|
317
|
+
*
|
|
318
|
+
* @param key {string} - cache key
|
|
319
|
+
*/
|
|
320
|
+
async get(key) {
|
|
321
|
+
return new Promise((resolve, reject) => {
|
|
322
|
+
this._client.get(key, (err, value) => {
|
|
323
|
+
if(err) {
|
|
324
|
+
this._logger.push({ key, err }).log(`Error getting cache key: ${key}`);
|
|
325
|
+
return reject(err);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
this._logger.push({ key, value }).log(`Got cache key: ${key}`);
|
|
329
|
+
|
|
330
|
+
if(typeof(value) === 'string') {
|
|
331
|
+
try {
|
|
332
|
+
value = JSON.parse(value);
|
|
333
|
+
}
|
|
334
|
+
catch(err) {
|
|
335
|
+
this._logger.push({ err }).log('Error parsing JSON cache value');
|
|
336
|
+
return reject(err);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
return resolve(value);
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Define constants on the prototype, but prevent a user of the cache from overwriting them for all
|
|
347
|
+
// instances
|
|
348
|
+
Object.defineProperty(Cache.prototype, 'CALLBACK_PREFIX', { value: 'callback_', writable: false });
|
|
349
|
+
Object.defineProperty(Cache.prototype, 'REQUEST_PREFIX', { value: 'request_', writable: false });
|
|
350
|
+
Object.defineProperty(Cache.prototype, 'EVENT_SET', { value: '__keyevent@0__:set', writable: false });
|
|
351
|
+
|
|
352
|
+
module.exports = Cache;
|