@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.
Files changed (277) hide show
  1. package/.env.example +140 -0
  2. package/.eslintignore +2 -0
  3. package/.eslintrc.json +30 -0
  4. package/.nvmrc +1 -0
  5. package/.versionrc +15 -0
  6. package/CHANGELOG.md +118 -0
  7. package/InboundServer/api.yaml +3594 -0
  8. package/InboundServer/api_template.yaml +69 -0
  9. package/InboundServer/handlers.js +940 -0
  10. package/InboundServer/index.js +205 -0
  11. package/InboundServer/middlewares.js +426 -0
  12. package/OAuthTestServer/index.js +66 -0
  13. package/OAuthTestServer/model.js +70 -0
  14. package/OutboundServer/api.yaml +2732 -0
  15. package/OutboundServer/api_interfaces/index.d.ts +117 -0
  16. package/OutboundServer/api_interfaces/openapi.d.ts +1475 -0
  17. package/OutboundServer/api_template/components/parameters/bulkQuoteId.yaml +9 -0
  18. package/OutboundServer/api_template/components/parameters/bulkTransferId.yaml +9 -0
  19. package/OutboundServer/api_template/components/parameters/requestToPayTransactionId.yaml +9 -0
  20. package/OutboundServer/api_template/components/parameters/transferId.yaml +9 -0
  21. package/OutboundServer/api_template/components/responses/accountsCreationCompleted.yaml +5 -0
  22. package/OutboundServer/api_template/components/responses/accountsCreationError.yaml +5 -0
  23. package/OutboundServer/api_template/components/responses/accountsCreationTimeout.yaml +5 -0
  24. package/OutboundServer/api_template/components/responses/authorizationPostSuccess.yaml +5 -0
  25. package/OutboundServer/api_template/components/responses/authorizationsServerError.yaml +5 -0
  26. package/OutboundServer/api_template/components/responses/bulkQuoteBadRequest.yaml +5 -0
  27. package/OutboundServer/api_template/components/responses/bulkQuoteServerError.yaml +5 -0
  28. package/OutboundServer/api_template/components/responses/bulkQuoteSuccess.yaml +5 -0
  29. package/OutboundServer/api_template/components/responses/bulkQuoteTimeout.yaml +5 -0
  30. package/OutboundServer/api_template/components/responses/bulkTransferBadRequest.yaml +5 -0
  31. package/OutboundServer/api_template/components/responses/bulkTransferServerError.yaml +5 -0
  32. package/OutboundServer/api_template/components/responses/bulkTransferSuccess.yaml +5 -0
  33. package/OutboundServer/api_template/components/responses/bulkTransferTimeout.yaml +5 -0
  34. package/OutboundServer/api_template/components/responses/partiesByIdError404.yaml +9 -0
  35. package/OutboundServer/api_template/components/responses/partiesByIdSuccess.yaml +5 -0
  36. package/OutboundServer/api_template/components/responses/quotesPostSuccess.yaml +5 -0
  37. package/OutboundServer/api_template/components/responses/quotesServerError.yaml +5 -0
  38. package/OutboundServer/api_template/components/responses/requestToPaySuccess.yaml +5 -0
  39. package/OutboundServer/api_template/components/responses/requestToPayTransferBadRequest.yaml +5 -0
  40. package/OutboundServer/api_template/components/responses/requestToPayTransferSuccess.yaml +5 -0
  41. package/OutboundServer/api_template/components/responses/simpleTransfersPostSuccess.yaml +5 -0
  42. package/OutboundServer/api_template/components/responses/simpleTransfersServerError.yaml +5 -0
  43. package/OutboundServer/api_template/components/responses/transferBadRequest.yaml +5 -0
  44. package/OutboundServer/api_template/components/responses/transferServerError.yaml +5 -0
  45. package/OutboundServer/api_template/components/responses/transferSuccess.yaml +5 -0
  46. package/OutboundServer/api_template/components/responses/transferTimeout.yaml +5 -0
  47. package/OutboundServer/api_template/components/schemas/accountCreationStatus.yaml +18 -0
  48. package/OutboundServer/api_template/components/schemas/accountsCreationState.yaml +4 -0
  49. package/OutboundServer/api_template/components/schemas/accountsRequest.yaml +20 -0
  50. package/OutboundServer/api_template/components/schemas/accountsResponse.yaml +15 -0
  51. package/OutboundServer/api_template/components/schemas/async2SyncCurrentState.yaml +5 -0
  52. package/OutboundServer/api_template/components/schemas/authorizationsPostRequest.yaml +15 -0
  53. package/OutboundServer/api_template/components/schemas/authorizationsPostResponse.yaml +19 -0
  54. package/OutboundServer/api_template/components/schemas/bulkQuoteErrorResponse.yaml +8 -0
  55. package/OutboundServer/api_template/components/schemas/bulkQuoteRequest.yaml +26 -0
  56. package/OutboundServer/api_template/components/schemas/bulkQuoteResponse.yaml +21 -0
  57. package/OutboundServer/api_template/components/schemas/bulkQuoteStatus.yaml +4 -0
  58. package/OutboundServer/api_template/components/schemas/bulkQuoteStatusResponse.yaml +17 -0
  59. package/OutboundServer/api_template/components/schemas/bulkTransferErrorResponse.yaml +8 -0
  60. package/OutboundServer/api_template/components/schemas/bulkTransferRequest.yaml +26 -0
  61. package/OutboundServer/api_template/components/schemas/bulkTransferResponse.yaml +16 -0
  62. package/OutboundServer/api_template/components/schemas/bulkTransferStatus.yaml +4 -0
  63. package/OutboundServer/api_template/components/schemas/bulkTransferStatusResponse.yaml +17 -0
  64. package/OutboundServer/api_template/components/schemas/errorAccountsResponse.yaml +8 -0
  65. package/OutboundServer/api_template/components/schemas/errorAuthorizationsResponse.yaml +3 -0
  66. package/OutboundServer/api_template/components/schemas/errorQuotesResponse.yaml +9 -0
  67. package/OutboundServer/api_template/components/schemas/errorResponse.yaml +8 -0
  68. package/OutboundServer/api_template/components/schemas/errorSimpleTransfersResponse.yaml +3 -0
  69. package/OutboundServer/api_template/components/schemas/errorTransferResponse.yaml +8 -0
  70. package/OutboundServer/api_template/components/schemas/extensionListEmptiable.yaml +6 -0
  71. package/OutboundServer/api_template/components/schemas/individualQuote.yaml +32 -0
  72. package/OutboundServer/api_template/components/schemas/individualQuoteResult.yaml +28 -0
  73. package/OutboundServer/api_template/components/schemas/individualTransfer.yaml +32 -0
  74. package/OutboundServer/api_template/components/schemas/individualTransferFulfilment.yaml +13 -0
  75. package/OutboundServer/api_template/components/schemas/individualTransferResult.yaml +41 -0
  76. package/OutboundServer/api_template/components/schemas/mojaloopError.yaml +5 -0
  77. package/OutboundServer/api_template/components/schemas/mojaloopTransactionRequestState.yaml +2 -0
  78. package/OutboundServer/api_template/components/schemas/partiesByIdResponse.yaml +13 -0
  79. package/OutboundServer/api_template/components/schemas/quote.yaml +3 -0
  80. package/OutboundServer/api_template/components/schemas/quoteError.yaml +16 -0
  81. package/OutboundServer/api_template/components/schemas/quotesPostRequest.yaml +13 -0
  82. package/OutboundServer/api_template/components/schemas/quotesPostResponse.yaml +48 -0
  83. package/OutboundServer/api_template/components/schemas/requestToPayRequest.yaml +39 -0
  84. package/OutboundServer/api_template/components/schemas/requestToPayResponse.yaml +41 -0
  85. package/OutboundServer/api_template/components/schemas/requestToPayTransferRequest.yaml +42 -0
  86. package/OutboundServer/api_template/components/schemas/requestToPayTransferResponse.yaml +58 -0
  87. package/OutboundServer/api_template/components/schemas/simpleTransferServerError.yaml +5 -0
  88. package/OutboundServer/api_template/components/schemas/simpleTransfersPostRequest.yaml +12 -0
  89. package/OutboundServer/api_template/components/schemas/simpleTransfersPostResponse.yaml +11 -0
  90. package/OutboundServer/api_template/components/schemas/transactionType.yaml +4 -0
  91. package/OutboundServer/api_template/components/schemas/transferContinuationAcceptOTP.yaml +9 -0
  92. package/OutboundServer/api_template/components/schemas/transferContinuationAcceptParty.yaml +8 -0
  93. package/OutboundServer/api_template/components/schemas/transferContinuationAcceptQuote.yaml +9 -0
  94. package/OutboundServer/api_template/components/schemas/transferError.yaml +16 -0
  95. package/OutboundServer/api_template/components/schemas/transferFulfilment.yaml +3 -0
  96. package/OutboundServer/api_template/components/schemas/transferParty.yaml +40 -0
  97. package/OutboundServer/api_template/components/schemas/transferRequest.yaml +37 -0
  98. package/OutboundServer/api_template/components/schemas/transferResponse.yaml +58 -0
  99. package/OutboundServer/api_template/components/schemas/transferStatus.yaml +6 -0
  100. package/OutboundServer/api_template/components/schemas/transferStatusResponse.yaml +13 -0
  101. package/OutboundServer/api_template/health.yaml +12 -0
  102. package/OutboundServer/api_template/openapi.yaml +55 -0
  103. package/OutboundServer/api_template/paths/accounts.yaml +26 -0
  104. package/OutboundServer/api_template/paths/authorizations.yaml +19 -0
  105. package/OutboundServer/api_template/paths/bulkQuotes.yaml +23 -0
  106. package/OutboundServer/api_template/paths/bulkQuotes_bulkQuoteId.yaml +24 -0
  107. package/OutboundServer/api_template/paths/bulkTransfers.yaml +23 -0
  108. package/OutboundServer/api_template/paths/bulkTransfers_bulkTransferId.yaml +24 -0
  109. package/OutboundServer/api_template/paths/parties_Type_ID.yaml +20 -0
  110. package/OutboundServer/api_template/paths/parties_Type_ID_SubId.yaml +22 -0
  111. package/OutboundServer/api_template/paths/quotes.yaml +20 -0
  112. package/OutboundServer/api_template/paths/requestToPay.yaml +22 -0
  113. package/OutboundServer/api_template/paths/requestToPayTransfer.yaml +57 -0
  114. package/OutboundServer/api_template/paths/requestToPayTransfer_requestToPayTransactionId.yaml +34 -0
  115. package/OutboundServer/api_template/paths/simpleTransfers.yaml +19 -0
  116. package/OutboundServer/api_template/paths/transfers.yaml +55 -0
  117. package/OutboundServer/api_template/paths/transfers_transferId.yaml +58 -0
  118. package/OutboundServer/handlers.js +622 -0
  119. package/OutboundServer/index.js +137 -0
  120. package/OutboundServer/middlewares.js +67 -0
  121. package/TestServer/api.yaml +62 -0
  122. package/TestServer/handlers.js +63 -0
  123. package/TestServer/index.js +215 -0
  124. package/audit-resolve.json +65 -0
  125. package/babel.config.js +3 -0
  126. package/config.js +158 -0
  127. package/index.d.ts +1 -0
  128. package/index.js +149 -0
  129. package/jest.config.js +15 -0
  130. package/lib/api/index.js +12 -0
  131. package/lib/cache.js +352 -0
  132. package/lib/check.js +25 -0
  133. package/lib/model/AccountsModel.js +396 -0
  134. package/lib/model/Async2SyncModel.js +283 -0
  135. package/lib/model/AuthorizationsModel.js +86 -0
  136. package/lib/model/InboundTransfersModel.js +730 -0
  137. package/lib/model/OutboundBulkQuotesModel.js +485 -0
  138. package/lib/model/OutboundBulkTransfersModel.js +479 -0
  139. package/lib/model/OutboundRequestToPayModel.js +517 -0
  140. package/lib/model/OutboundRequestToPayTransferModel.js +893 -0
  141. package/lib/model/OutboundTransfersModel.js +823 -0
  142. package/lib/model/PartiesModel.js +70 -0
  143. package/lib/model/ProxyModel/MatchRules/Expression.js +48 -0
  144. package/lib/model/ProxyModel/MatchRules/Headers.js +65 -0
  145. package/lib/model/ProxyModel/MatchRules/MatchRule.js +27 -0
  146. package/lib/model/ProxyModel/MatchRules/Path.js +36 -0
  147. package/lib/model/ProxyModel/MatchRules/Query.js +65 -0
  148. package/lib/model/ProxyModel/MatchRules/index.js +19 -0
  149. package/lib/model/ProxyModel/Route.js +82 -0
  150. package/lib/model/ProxyModel/configSchema.json +118 -0
  151. package/lib/model/ProxyModel/index.js +138 -0
  152. package/lib/model/QuotesModel.js +94 -0
  153. package/lib/model/TransfersModel.js +81 -0
  154. package/lib/model/common/BackendError.js +26 -0
  155. package/lib/model/common/PersistentStateMachine.js +93 -0
  156. package/lib/model/common/index.js +18 -0
  157. package/lib/model/index.js +43 -0
  158. package/lib/model/lib/deferredJob.js +113 -0
  159. package/lib/model/lib/index.js +9 -0
  160. package/lib/model/lib/requests/backendRequests.js +227 -0
  161. package/lib/model/lib/requests/common.js +76 -0
  162. package/lib/model/lib/requests/index.js +19 -0
  163. package/lib/model/lib/shared.js +468 -0
  164. package/lib/randomphrase/index.js +21 -0
  165. package/lib/randomphrase/words.json +3397 -0
  166. package/lib/router.js +28 -0
  167. package/lib/validate.js +205 -0
  168. package/package.json +102 -0
  169. package/test/__mocks__/@mojaloop/sdk-standard-components.js +152 -0
  170. package/test/__mocks__/javascript-state-machine.js +21 -0
  171. package/test/__mocks__/redis.js +49 -0
  172. package/test/__mocks__/uuidv4.js +16 -0
  173. package/test/config/integration.env +136 -0
  174. package/test/integration/lib/Outbound/authorizations.test.js +58 -0
  175. package/test/integration/lib/Outbound/data/authorizationsPostRequest.json +43 -0
  176. package/test/integration/lib/Outbound/data/quotesPostRequest.json +52 -0
  177. package/test/integration/lib/Outbound/data/transfersPostRequest.json +24 -0
  178. package/test/integration/lib/Outbound/parties.test.js +28 -0
  179. package/test/integration/lib/Outbound/quotes.test.js +58 -0
  180. package/test/integration/lib/Outbound/simpleTransfers.test.js +67 -0
  181. package/test/integration/lib/cache.test.js +80 -0
  182. package/test/integration/testEnv.js +7 -0
  183. package/test/unit/InboundServer.test.js +443 -0
  184. package/test/unit/TestServer.test.js +394 -0
  185. package/test/unit/api/accounts/accounts.test.js +128 -0
  186. package/test/unit/api/accounts/data/postAccountsBody.json +7 -0
  187. package/test/unit/api/accounts/data/postAccountsErrorMojaloopResponse.json +25 -0
  188. package/test/unit/api/accounts/data/postAccountsErrorTimeoutResponse.json +19 -0
  189. package/test/unit/api/accounts/data/postAccountsSuccessResponse.json +17 -0
  190. package/test/unit/api/accounts/data/postAccountsSuccessResponseWithError1.json +21 -0
  191. package/test/unit/api/accounts/data/postAccountsSuccessResponseWithError2.json +21 -0
  192. package/test/unit/api/accounts/utils.js +65 -0
  193. package/test/unit/api/proxy/data/proxyConfig.yaml +82 -0
  194. package/test/unit/api/proxy/data/requestBody.json +22 -0
  195. package/test/unit/api/proxy/data/requestHeaders.json +5 -0
  196. package/test/unit/api/proxy/data/requestQuery.json +6 -0
  197. package/test/unit/api/proxy/data/responseBody.json +21 -0
  198. package/test/unit/api/proxy/data/responseHeaders.json +5 -0
  199. package/test/unit/api/proxy/proxy.test.js +220 -0
  200. package/test/unit/api/proxy/utils.js +79 -0
  201. package/test/unit/api/transfers/data/getTransfersCommittedResponse.json +21 -0
  202. package/test/unit/api/transfers/data/getTransfersErrorNotFound.json +17 -0
  203. package/test/unit/api/transfers/data/postQuotesBody.json +52 -0
  204. package/test/unit/api/transfers/data/postTransfersBadBody.json +17 -0
  205. package/test/unit/api/transfers/data/postTransfersBody.json +24 -0
  206. package/test/unit/api/transfers/data/postTransfersErrorMojaloopResponse.json +53 -0
  207. package/test/unit/api/transfers/data/postTransfersErrorTimeoutResponse.json +47 -0
  208. package/test/unit/api/transfers/data/postTransfersSimpleBody.json +26 -0
  209. package/test/unit/api/transfers/data/postTransfersSuccessResponse.json +101 -0
  210. package/test/unit/api/transfers/data/putPartiesBody.json +20 -0
  211. package/test/unit/api/transfers/data/putQuotesBody.json +37 -0
  212. package/test/unit/api/transfers/data/putTransfersBody.json +17 -0
  213. package/test/unit/api/transfers/transfers.test.js +191 -0
  214. package/test/unit/api/transfers/utils.js +183 -0
  215. package/test/unit/api/utils.js +75 -0
  216. package/test/unit/config.test.js +119 -0
  217. package/test/unit/data/commonHttpHeaders.json +6 -0
  218. package/test/unit/data/defaultConfig.json +58 -0
  219. package/test/unit/data/postQuotesBody.json +52 -0
  220. package/test/unit/data/putParticipantsBody.json +12 -0
  221. package/test/unit/data/putPartiesBody.json +20 -0
  222. package/test/unit/data/testFile.json +29 -0
  223. package/test/unit/data/testFile.yaml +14 -0
  224. package/test/unit/inboundApi/data/mockArguments.json +117 -0
  225. package/test/unit/inboundApi/data/mockTransactionRequest.json +42 -0
  226. package/test/unit/inboundApi/handlers.test.js +799 -0
  227. package/test/unit/index.test.js +55 -0
  228. package/test/unit/lib/cache.test.js +146 -0
  229. package/test/unit/lib/model/AccountsModel.test.js +121 -0
  230. package/test/unit/lib/model/AuthorizationsModel.test.js +460 -0
  231. package/test/unit/lib/model/InboundTransfersModel.test.js +628 -0
  232. package/test/unit/lib/model/OutboundBulkQuotesModel.test.js +249 -0
  233. package/test/unit/lib/model/OutboundBulkTransfersModel.test.js +244 -0
  234. package/test/unit/lib/model/OutboundRequestToPayModel.test.js +166 -0
  235. package/test/unit/lib/model/OutboundRequestToPayTransferModel.test.js +245 -0
  236. package/test/unit/lib/model/OutboundTransfersModel.test.js +836 -0
  237. package/test/unit/lib/model/PartiesModel.test.js +468 -0
  238. package/test/unit/lib/model/QuotesModel.test.js +470 -0
  239. package/test/unit/lib/model/TransfersModel.test.js +474 -0
  240. package/test/unit/lib/model/common/PersistentStateMachine.test.js +179 -0
  241. package/test/unit/lib/model/data/authorizationsResponse.json +13 -0
  242. package/test/unit/lib/model/data/bulkQuoteRequest.json +27 -0
  243. package/test/unit/lib/model/data/bulkQuoteResponse.json +35 -0
  244. package/test/unit/lib/model/data/bulkTransferFulfil.json +13 -0
  245. package/test/unit/lib/model/data/bulkTransferRequest.json +29 -0
  246. package/test/unit/lib/model/data/defaultConfig.json +47 -0
  247. package/test/unit/lib/model/data/getBulkTransfersBackendResponse.json +42 -0
  248. package/test/unit/lib/model/data/getBulkTransfersMojaloopResponse.json +22 -0
  249. package/test/unit/lib/model/data/getTransfersBackendResponse.json +34 -0
  250. package/test/unit/lib/model/data/getTransfersMojaloopResponse.json +17 -0
  251. package/test/unit/lib/model/data/mockArguments.json +131 -0
  252. package/test/unit/lib/model/data/mockTxnRequestsArguments.json +63 -0
  253. package/test/unit/lib/model/data/notificationToPayee.json +10 -0
  254. package/test/unit/lib/model/data/payeeParty.json +16 -0
  255. package/test/unit/lib/model/data/putAuthorizationsResponse.json +10 -0
  256. package/test/unit/lib/model/data/putQuotesResponse.json +33 -0
  257. package/test/unit/lib/model/data/putTransfersResponse.json +5 -0
  258. package/test/unit/lib/model/data/quoteResponse.json +31 -0
  259. package/test/unit/lib/model/data/requestToPayRequest.json +20 -0
  260. package/test/unit/lib/model/data/requestToPayTransferRequest.json +27 -0
  261. package/test/unit/lib/model/data/transactionRequestResponse.json +18 -0
  262. package/test/unit/lib/model/data/transferFulfil.json +8 -0
  263. package/test/unit/lib/model/data/transferRequest.json +26 -0
  264. package/test/unit/lib/model/mockedLibRequests.js +74 -0
  265. package/test/unit/mockLogger.js +39 -0
  266. package/test/unit/outboundApi/data/bulkQuoteRequest.json +28 -0
  267. package/test/unit/outboundApi/data/bulkTransferRequest.json +28 -0
  268. package/test/unit/outboundApi/data/mockBulkQuoteError.json +45 -0
  269. package/test/unit/outboundApi/data/mockBulkTransferError.json +48 -0
  270. package/test/unit/outboundApi/data/mockError.json +41 -0
  271. package/test/unit/outboundApi/data/mockGetPartiesError.json +4 -0
  272. package/test/unit/outboundApi/data/mockRequestToPayError.json +32 -0
  273. package/test/unit/outboundApi/data/mockRequestToPayTransferError.json +39 -0
  274. package/test/unit/outboundApi/data/requestToPay.json +21 -0
  275. package/test/unit/outboundApi/data/requestToPayTransferRequest.json +20 -0
  276. package/test/unit/outboundApi/data/transferRequest.json +21 -0
  277. 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;