@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,485 @@
|
|
|
1
|
+
/**************************************************************************
|
|
2
|
+
* (C) Copyright ModusBox Inc. 2020 - 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
|
+
* Steven Oderayi - steven.oderayi@modusbox.com *
|
|
9
|
+
**************************************************************************/
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
const util = require('util');
|
|
14
|
+
const { uuid } = require('uuidv4');
|
|
15
|
+
const StateMachine = require('javascript-state-machine');
|
|
16
|
+
const { MojaloopRequests } = require('@mojaloop/sdk-standard-components');
|
|
17
|
+
const shared = require('./lib/shared');
|
|
18
|
+
const { BackendError } = require('./common');
|
|
19
|
+
|
|
20
|
+
const stateEnum = {
|
|
21
|
+
'ERROR_OCCURRED': 'ERROR_OCCURRED',
|
|
22
|
+
'COMPLETED': 'COMPLETED',
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Models the state machine and operations required for performing an outbound bulk quote request
|
|
28
|
+
*/
|
|
29
|
+
class OutboundBulkQuotesModel {
|
|
30
|
+
constructor(config) {
|
|
31
|
+
this._cache = config.cache;
|
|
32
|
+
this._logger = config.logger;
|
|
33
|
+
this._requestProcessingTimeoutSeconds = config.requestProcessingTimeoutSeconds;
|
|
34
|
+
this._dfspId = config.dfspId;
|
|
35
|
+
this._expirySeconds = config.expirySeconds;
|
|
36
|
+
this._rejectExpiredQuoteResponses = config.rejectExpiredQuoteResponses;
|
|
37
|
+
|
|
38
|
+
this._requests = new MojaloopRequests({
|
|
39
|
+
logger: this._logger,
|
|
40
|
+
peerEndpoint: config.peerEndpoint,
|
|
41
|
+
bulkQuotesEndpoint: config.bulkQuotesEndpoint,
|
|
42
|
+
dfspId: config.dfspId,
|
|
43
|
+
tls: config.tls,
|
|
44
|
+
jwsSign: config.jwsSign,
|
|
45
|
+
jwsSigningKey: config.jwsSigningKey,
|
|
46
|
+
wso2: config.wso2,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Initializes the internal state machine object
|
|
52
|
+
*/
|
|
53
|
+
_initStateMachine (initState) {
|
|
54
|
+
this.stateMachine = new StateMachine({
|
|
55
|
+
init: initState,
|
|
56
|
+
transitions: [
|
|
57
|
+
{ name: 'requestBulkQuote', from: 'start', to: 'succeeded' },
|
|
58
|
+
{ name: 'getBulkQuote', to: 'succeeded' },
|
|
59
|
+
{ name: 'error', from: '*', to: 'errored' },
|
|
60
|
+
],
|
|
61
|
+
methods: {
|
|
62
|
+
onTransition: this._handleTransition.bind(this),
|
|
63
|
+
onAfterTransition: this._afterTransition.bind(this),
|
|
64
|
+
onPendingTransition: (transition, from, to) => {
|
|
65
|
+
// allow transitions to 'error' state while other transitions are in progress
|
|
66
|
+
if(transition !== 'error') {
|
|
67
|
+
throw new Error(`Transition requested while another transition is in progress: ${transition} from: ${from} to: ${to}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
return this.stateMachine[initState];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Updates the internal state representation to reflect that of the state machine itself
|
|
78
|
+
*/
|
|
79
|
+
_afterTransition() {
|
|
80
|
+
this._logger.log(`State machine transitioned: ${this.data.currentState} -> ${this.stateMachine.state}`);
|
|
81
|
+
this.data.currentState = this.stateMachine.state;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Initializes the bulk quotes model
|
|
86
|
+
*
|
|
87
|
+
* @param data {object} - The inbound API POST /bulkQuotes request body
|
|
88
|
+
*/
|
|
89
|
+
async initialize(data) {
|
|
90
|
+
this.data = data;
|
|
91
|
+
|
|
92
|
+
// add a bulkQuoteId if one is not present e.g. on first submission
|
|
93
|
+
if(!this.data.hasOwnProperty('bulkQuoteId')) {
|
|
94
|
+
this.data.bulkQuoteId = uuid();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// initialize the state machine to its starting state
|
|
98
|
+
if(!this.data.hasOwnProperty('currentState')) {
|
|
99
|
+
this.data.currentState = 'start';
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
this._initStateMachine(this.data.currentState);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Handles state machine transitions
|
|
107
|
+
*/
|
|
108
|
+
async _handleTransition(lifecycle, ...args) {
|
|
109
|
+
this._logger.log(`Bulk quote ${this.data.bulkQuoteId} is transitioning from ${lifecycle.from} to ${lifecycle.to} in response to ${lifecycle.transition}`);
|
|
110
|
+
|
|
111
|
+
switch(lifecycle.transition) {
|
|
112
|
+
case 'init':
|
|
113
|
+
return;
|
|
114
|
+
|
|
115
|
+
case 'requestBulkQuote':
|
|
116
|
+
return this._requestBulkQuote();
|
|
117
|
+
|
|
118
|
+
case 'getBulkQuote':
|
|
119
|
+
return this._getBulkQuote(this.data.bulkQuoteId);
|
|
120
|
+
|
|
121
|
+
case 'error':
|
|
122
|
+
this._logger.log(`State machine is erroring with error: ${util.inspect(args)}`);
|
|
123
|
+
this.data.lastError = args[0] || new Error('unspecified error');
|
|
124
|
+
break;
|
|
125
|
+
|
|
126
|
+
default:
|
|
127
|
+
throw new Error(`Unhandled state transition for bulk quote ${this.data.bulkQuoteId}: ${util.inspect(args)}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Requests a bulk quote
|
|
133
|
+
* Starts the quotes resolution process by sending a POST /bulkQuotes request to the switch;
|
|
134
|
+
* then waits for a notification from the cache that the quotes response has been received.
|
|
135
|
+
*/
|
|
136
|
+
async _requestBulkQuote() {
|
|
137
|
+
// eslint-disable-next-line no-async-promise-executor
|
|
138
|
+
return new Promise(async (resolve, reject) => {
|
|
139
|
+
// create a bulk quote request
|
|
140
|
+
const bulkQuote = this._buildBulkQuoteRequest();
|
|
141
|
+
|
|
142
|
+
// listen for events on the bulkQuoteId
|
|
143
|
+
const bulkQuoteKey = `bulkQuote_${bulkQuote.bulkQuoteId}`;
|
|
144
|
+
|
|
145
|
+
// hook up a subscriber to handle response messages
|
|
146
|
+
const subId = await this._cache.subscribe(bulkQuoteKey, (cn, msg, subId) => {
|
|
147
|
+
try {
|
|
148
|
+
let error;
|
|
149
|
+
let message = JSON.parse(msg);
|
|
150
|
+
|
|
151
|
+
if (message.type === 'bulkQuoteResponse') {
|
|
152
|
+
if (this._rejectExpiredQuoteResponses) {
|
|
153
|
+
const now = new Date().toISOString();
|
|
154
|
+
if (now > bulkQuote.expiration) {
|
|
155
|
+
const msg = 'Bulk quote response missed expiry deadline';
|
|
156
|
+
error = new BackendError(msg, 504);
|
|
157
|
+
this._logger.error(`${msg}: system time=${now} > expiration time=${bulkQuote.expiration}`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
} else if (message.type === 'bulkQuoteResponseError') {
|
|
161
|
+
error = new BackendError(`Got an error response requesting bulk quote: ${util.inspect(message.data, { depth: Infinity })}`, 500);
|
|
162
|
+
error.mojaloopError = message.data;
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
this._logger.push({ message }).log(`Ignoring cache notification for bulk quote ${bulkQuoteKey}. Unknown message type ${message.type}.`);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// cancel the timeout handler
|
|
170
|
+
clearTimeout(timeout);
|
|
171
|
+
|
|
172
|
+
// stop listening for bulk quote resolution messages
|
|
173
|
+
// no need to await for the unsubscribe to complete.
|
|
174
|
+
// we dont really care if the unsubscribe fails but we should log it regardless
|
|
175
|
+
this._cache.unsubscribe(bulkQuoteKey, subId).catch(e => {
|
|
176
|
+
this._logger.log(`Error unsubscribing (in callback) ${bulkQuoteKey} ${subId}: ${e.stack || util.inspect(e)}`);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
if (error) {
|
|
180
|
+
return reject(error);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const bulkQuoteResponseBody = message.data;
|
|
184
|
+
this._logger.push({ bulkQuoteResponseBody }).log('Bulk quote response received');
|
|
185
|
+
|
|
186
|
+
return resolve(bulkQuoteResponseBody);
|
|
187
|
+
}
|
|
188
|
+
catch (err) {
|
|
189
|
+
return reject(err);
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// set up a timeout for the request
|
|
194
|
+
const timeout = setTimeout(() => {
|
|
195
|
+
const err = new BackendError(`Timeout requesting bulk quote ${this.data.bulkQuoteId}`, 504);
|
|
196
|
+
|
|
197
|
+
// we dont really care if the unsubscribe fails but we should log it regardless
|
|
198
|
+
this._cache.unsubscribe(bulkQuoteKey, subId).catch(e => {
|
|
199
|
+
this._logger.log(`Error unsubscribing (in timeout handler) ${bulkQuoteKey} ${subId}: ${e.stack || util.inspect(e)}`);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
return reject(err);
|
|
203
|
+
}, this._requestProcessingTimeoutSeconds * 1000);
|
|
204
|
+
|
|
205
|
+
// now we have a timeout handler and a cache subscriber hooked up we can fire off
|
|
206
|
+
// a POST /bulkQuotes request to the switch
|
|
207
|
+
try {
|
|
208
|
+
const res = await this._requests.postBulkQuotes(bulkQuote, this.data.individualQuotes[0].to.fspId);
|
|
209
|
+
this._logger.push({ res }).log('Bulk quote request sent to peer');
|
|
210
|
+
}
|
|
211
|
+
catch (err) {
|
|
212
|
+
// cancel the timout and unsubscribe before rejecting the promise
|
|
213
|
+
clearTimeout(timeout);
|
|
214
|
+
|
|
215
|
+
// we dont really care if the unsubscribe fails but we should log it regardless
|
|
216
|
+
this._cache.unsubscribe(bulkQuoteKey, subId).catch(e => {
|
|
217
|
+
this._logger.log(`Error unsubscribing (in error handler) ${bulkQuoteKey} ${subId}: ${e.stack || util.inspect(e)}`);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
return reject(err);
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Constructs a bulk quote request payload based on current state
|
|
227
|
+
*
|
|
228
|
+
* @returns {object} - the bulk quote request object
|
|
229
|
+
*/
|
|
230
|
+
_buildBulkQuoteRequest() {
|
|
231
|
+
const bulkQuoteRequest = {
|
|
232
|
+
bulkQuoteId: this.data.bulkQuoteId,
|
|
233
|
+
payer: shared.internalPartyToMojaloopParty(this.data.from, this._dfspId),
|
|
234
|
+
expiration: this._getExpirationTimestamp(),
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
this.data.geoCode && (bulkQuoteRequest.geoCode = this.data.geoCode);
|
|
238
|
+
|
|
239
|
+
if (this.data.extensions && this.data.extensions.length > 0) {
|
|
240
|
+
bulkQuoteRequest.extensionList = {
|
|
241
|
+
extension: this.data.extensions
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
bulkQuoteRequest.individualQuotes = this.data.individualQuotes.map((individualQuote) => {
|
|
246
|
+
const quoteId = individualQuote.quoteId || uuid();
|
|
247
|
+
const quote = {
|
|
248
|
+
quoteId: quoteId,
|
|
249
|
+
transactionId: individualQuote.transactionId || quoteId,
|
|
250
|
+
payee: shared.internalPartyToMojaloopParty(individualQuote.to, individualQuote.to.fspId),
|
|
251
|
+
amountType: individualQuote.amountType,
|
|
252
|
+
amount: {
|
|
253
|
+
currency: individualQuote.currency,
|
|
254
|
+
amount: individualQuote.amount
|
|
255
|
+
},
|
|
256
|
+
transactionType: {
|
|
257
|
+
scenario: individualQuote.transactionType,
|
|
258
|
+
// TODO: support payee initiated txns?
|
|
259
|
+
initiator: 'PAYER',
|
|
260
|
+
// TODO: defaulting to CONSUMER initiator type should
|
|
261
|
+
// be replaced with a required element on the incoming
|
|
262
|
+
// API request
|
|
263
|
+
initiatorType: this.data.from.type || 'CONSUMER'
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
individualQuote.note && (quote.note = individualQuote.note);
|
|
268
|
+
|
|
269
|
+
if (individualQuote.extensions && individualQuote.extensions.length > 0) {
|
|
270
|
+
bulkQuoteRequest.extensionList = {
|
|
271
|
+
extension: individualQuote.extensions
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return quote;
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
return bulkQuoteRequest;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Get bulk quote details by sending GET /bulkQuotes/{ID} request to the switch
|
|
283
|
+
*/
|
|
284
|
+
async _getBulkQuote(bulkQuoteId) {
|
|
285
|
+
// eslint-disable-next-line no-async-promise-executor
|
|
286
|
+
return new Promise(async (resolve, reject) => {
|
|
287
|
+
const bulkQuoteKey = `bulkQuote_${bulkQuoteId}`;
|
|
288
|
+
|
|
289
|
+
// hook up a subscriber to handle response messages
|
|
290
|
+
const subId = await this._cache.subscribe(bulkQuoteKey, (cn, msg, subId) => {
|
|
291
|
+
try {
|
|
292
|
+
let error;
|
|
293
|
+
let message = JSON.parse(msg);
|
|
294
|
+
|
|
295
|
+
if (message.type === 'bulkQuoteError') {
|
|
296
|
+
error = new BackendError(`Got an error response retrieving bulk quote: ${util.inspect(message.data, { depth: Infinity })}`, 500);
|
|
297
|
+
error.mojaloopError = message.data;
|
|
298
|
+
} else if (message.type !== 'bulkQuoteResponse') {
|
|
299
|
+
this._logger.push({ message }).log(`Ignoring cache notification for bulk quote ${bulkQuoteKey}. Uknokwn message type ${message.type}.`);
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// cancel the timeout handler
|
|
304
|
+
clearTimeout(timeout);
|
|
305
|
+
|
|
306
|
+
// stop listening for bulk quote response messages
|
|
307
|
+
this._cache.unsubscribe(bulkQuoteKey, subId).catch(e => {
|
|
308
|
+
this._logger.log(`Error unsubscribing (in callback) ${bulkQuoteKey} ${subId}: ${e.stack || util.inspect(e)}`);
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
if (error) {
|
|
312
|
+
return reject(error);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const bulkQuote = message.data;
|
|
316
|
+
this._logger.push({ bulkQuote }).log('Bulk quote response received');
|
|
317
|
+
|
|
318
|
+
return resolve(bulkQuote);
|
|
319
|
+
}
|
|
320
|
+
catch(err) {
|
|
321
|
+
return reject(err);
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
// set up a timeout for the resolution
|
|
326
|
+
const timeout = setTimeout(() => {
|
|
327
|
+
const err = new BackendError(`Timeout getting bulk quote ${bulkQuoteId}`, 504);
|
|
328
|
+
|
|
329
|
+
// we dont really care if the unsubscribe fails but we should log it regardless
|
|
330
|
+
this._cache.unsubscribe(bulkQuoteKey, subId).catch(e => {
|
|
331
|
+
this._logger.log(`Error unsubscribing (in timeout handler) ${bulkQuoteKey} ${subId}: ${e.stack || util.inspect(e)}`);
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
return reject(err);
|
|
335
|
+
}, this._requestProcessingTimeoutSeconds * 1000);
|
|
336
|
+
|
|
337
|
+
// now we have a timeout handler and a cache subscriber hooked up we can fire off
|
|
338
|
+
// a GET /bulkQuotes/{ID} request to the switch
|
|
339
|
+
try {
|
|
340
|
+
const res = await this._requests.getBulkQuotes(bulkQuoteId);
|
|
341
|
+
this._logger.push({ peer: res }).log('Bulk quote lookup sent to peer');
|
|
342
|
+
}
|
|
343
|
+
catch(err) {
|
|
344
|
+
// cancel the timout and unsubscribe before rejecting the promise
|
|
345
|
+
clearTimeout(timeout);
|
|
346
|
+
|
|
347
|
+
// we dont really care if the unsubscribe fails but we should log it regardless
|
|
348
|
+
this._cache.unsubscribe(bulkQuoteKey, subId).catch(e => {
|
|
349
|
+
this._logger.log(`Error unsubscribing ${bulkQuoteKey} ${subId}: ${e.stack || util.inspect(e)}`);
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
return reject(err);
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Returns an ISO-8601 format timestamp n-seconds in the future for expiration of a bulk quote API object,
|
|
359
|
+
* where n is equal to our config setting "expirySeconds"
|
|
360
|
+
*
|
|
361
|
+
* @returns {string} - ISO-8601 format future expiration timestamp
|
|
362
|
+
*/
|
|
363
|
+
_getExpirationTimestamp() {
|
|
364
|
+
let now = new Date();
|
|
365
|
+
return new Date(now.getTime() + (this._expirySeconds * 1000)).toISOString();
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Returns an object representing the final state of the bulk quote suitable for the outbound API
|
|
370
|
+
*
|
|
371
|
+
* @returns {object} - Response representing the result of the bulk quoting process
|
|
372
|
+
*/
|
|
373
|
+
getResponse() {
|
|
374
|
+
// we want to project some of our internal state into a more useful
|
|
375
|
+
// representation to return to the SDK API consumer
|
|
376
|
+
let resp = { ...this.data };
|
|
377
|
+
|
|
378
|
+
switch(this.data.currentState) {
|
|
379
|
+
case 'succeeded':
|
|
380
|
+
resp.currentState = stateEnum.COMPLETED;
|
|
381
|
+
break;
|
|
382
|
+
|
|
383
|
+
case 'errored':
|
|
384
|
+
resp.currentState = stateEnum.ERROR_OCCURRED;
|
|
385
|
+
break;
|
|
386
|
+
|
|
387
|
+
default:
|
|
388
|
+
this._logger.log(`Bulk quote model response being returned from an unexpected state: ${this.data.currentState}. Returning ERROR_OCCURRED state`);
|
|
389
|
+
resp.currentState = stateEnum.ERROR_OCCURRED;
|
|
390
|
+
break;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
return resp;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Persists the model state to cache for reinstantiation at a later point
|
|
398
|
+
*/
|
|
399
|
+
async _save() {
|
|
400
|
+
try {
|
|
401
|
+
this.data.currentState = this.stateMachine.state;
|
|
402
|
+
const res = await this._cache.set(`bulkQuoteModel_${this.data.bulkQuoteId}`, this.data);
|
|
403
|
+
this._logger.push({ res }).log('Persisted bulk quote model in cache');
|
|
404
|
+
}
|
|
405
|
+
catch(err) {
|
|
406
|
+
this._logger.push({ err }).log('Error saving bulk quote model');
|
|
407
|
+
throw err;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Loads a bulk quote model from cache for resumption of the bulk quote process
|
|
413
|
+
*
|
|
414
|
+
* @param bulkQuoteId {string} - UUID bulkQuoteId of the model to load from cache
|
|
415
|
+
*/
|
|
416
|
+
async load(bulkQuoteId) {
|
|
417
|
+
try {
|
|
418
|
+
const data = await this._cache.get(`bulkQuoteModel_${bulkQuoteId}`);
|
|
419
|
+
if(!data) {
|
|
420
|
+
throw new Error(`No cached data found for bulkQuoteId: ${bulkQuoteId}`);
|
|
421
|
+
}
|
|
422
|
+
await this.initialize(data);
|
|
423
|
+
this._logger.push({ cache: this.data }).log('Bulk quote model loaded from cached state');
|
|
424
|
+
}
|
|
425
|
+
catch(err) {
|
|
426
|
+
this._logger.push({ err }).log('Error loading bulk quote model');
|
|
427
|
+
throw err;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Returns a promise that resolves when the state machine has reached a terminal state
|
|
433
|
+
*/
|
|
434
|
+
async run() {
|
|
435
|
+
try {
|
|
436
|
+
// run transitions based on incoming state
|
|
437
|
+
switch(this.data.currentState) {
|
|
438
|
+
case 'start':
|
|
439
|
+
await this.stateMachine.requestBulkQuote();
|
|
440
|
+
this._logger.log(`Quotes resolved for bulk quote ${this.data.bulkQuoteId}`);
|
|
441
|
+
break;
|
|
442
|
+
|
|
443
|
+
case 'getBulkQuote':
|
|
444
|
+
await this.stateMachine.getBulkQuote();
|
|
445
|
+
this._logger.log(`Get bulk quote ${this.data.bulkQuoteId} has been completed`);
|
|
446
|
+
break;
|
|
447
|
+
|
|
448
|
+
case 'succeeded':
|
|
449
|
+
// all steps complete so return
|
|
450
|
+
this._logger.log('Bulk quoting completed successfully');
|
|
451
|
+
await this._save();
|
|
452
|
+
return this.getResponse();
|
|
453
|
+
|
|
454
|
+
case 'errored':
|
|
455
|
+
// stopped in errored state
|
|
456
|
+
this._logger.log('State machine in errored state');
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// now call ourselves recursively to deal with the next transition
|
|
461
|
+
this._logger.log(`Bulk quote model state machine transition completed in state: ${this.stateMachine.state}. Recursing to handle next transition.`);
|
|
462
|
+
return this.run();
|
|
463
|
+
}
|
|
464
|
+
catch(err) {
|
|
465
|
+
this._logger.log(`Error running bulk quote model: ${util.inspect(err)}`);
|
|
466
|
+
|
|
467
|
+
// as this function is recursive, we dont want to error the state machine multiple times
|
|
468
|
+
if(this.data.currentState !== 'errored') {
|
|
469
|
+
// err should not have a bulkQuoteState property here!
|
|
470
|
+
if(err.bulkQuoteState) {
|
|
471
|
+
this._logger.log(`State machine is broken: ${util.inspect(err)}`);
|
|
472
|
+
}
|
|
473
|
+
// transition to errored state
|
|
474
|
+
await this.stateMachine.error(err);
|
|
475
|
+
|
|
476
|
+
// avoid circular ref between bulkQuoteState.lastError and err
|
|
477
|
+
err.bulkQuoteState = JSON.parse(JSON.stringify(this.getResponse()));
|
|
478
|
+
}
|
|
479
|
+
throw err;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
module.exports = OutboundBulkQuotesModel;
|