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