@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
package/lib/check.js ADDED
@@ -0,0 +1,25 @@
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
+ * Matt Kingston - matt.kingston@modusbox.com *
9
+ **************************************************************************/
10
+
11
+ // This module maps all methods on Node assert to non-throwing "check" functions which return true
12
+ // if the assertion succeeded and false otherwise
13
+
14
+ const assert = require('assert').strict;
15
+
16
+ module.exports = Object.fromEntries(
17
+ Object.entries(assert).map(([k, f]) => [k, (...args) => {
18
+ try {
19
+ f.bind(assert)(...args);
20
+ return true;
21
+ } catch (err) {
22
+ return false;
23
+ }
24
+ }])
25
+ );
@@ -0,0 +1,396 @@
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
+ * Yevhen Kyriukha - yevhen.kyriukha@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, Errors } = 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
+ /**
26
+ * Models the state machine and operations required for performing an outbound transfer
27
+ */
28
+ class AccountsModel {
29
+ constructor(config) {
30
+ this._cache = config.cache;
31
+ this._logger = config.logger;
32
+ this._requestProcessingTimeoutSeconds = config.requestProcessingTimeoutSeconds;
33
+ this._dfspId = config.dfspId;
34
+
35
+ this._requests = new MojaloopRequests({
36
+ logger: this._logger,
37
+ peerEndpoint: config.alsEndpoint,
38
+ dfspId: config.dfspId,
39
+ tls: config.tls,
40
+ jwsSign: config.jwsSign,
41
+ jwsSigningKey: config.jwsSigningKey,
42
+ wso2: config.wso2,
43
+ });
44
+ }
45
+
46
+
47
+ /**
48
+ * Initializes the internal state machine object
49
+ */
50
+ _initStateMachine (initState) {
51
+ this._stateMachine = new StateMachine({
52
+ init: initState,
53
+ transitions: [
54
+ { name: 'createAccounts', from: 'start', to: 'succeeded' },
55
+ { name: 'error', from: '*', to: 'errored' },
56
+ ],
57
+ methods: {
58
+ onTransition: this._handleTransition.bind(this),
59
+ onAfterTransition: this._afterTransition.bind(this),
60
+ onPendingTransition: (transition, from, to) => {
61
+ // allow transitions to 'error' state while other transitions are in progress
62
+ if(transition !== 'error') {
63
+ throw new BackendError(`Transition requested while another transition is in progress: ${transition} from: ${from} to: ${to}`, 500);
64
+ }
65
+ }
66
+ }
67
+ });
68
+
69
+ return this._stateMachine[initState];
70
+ }
71
+
72
+
73
+ /**
74
+ * Updates the internal state representation to reflect that of the state machine itself
75
+ */
76
+ _afterTransition() {
77
+ this._logger.log(`State machine transitioned: ${this._data.currentState} -> ${this._stateMachine.state}`);
78
+ this._data.currentState = this._stateMachine.state;
79
+ }
80
+
81
+
82
+ /**
83
+ * Initializes the accounts model
84
+ *
85
+ * @param data {object} - The outbound API POST /accounts request body
86
+ */
87
+ async initialize(data) {
88
+ this._data = data;
89
+
90
+ // add a modelId if one is not present e.g. on first submission
91
+ if(!this._data.hasOwnProperty('modelId')) {
92
+ this._data.modelId = uuid();
93
+ }
94
+
95
+ // initialize the transfer state machine to its starting state
96
+ if(!this._data.hasOwnProperty('currentState')) {
97
+ this._data.currentState = 'start';
98
+ }
99
+
100
+ if(!this._data.hasOwnProperty('response')) {
101
+ this._data.response = [];
102
+ }
103
+
104
+ this._initStateMachine(this._data.currentState);
105
+ }
106
+
107
+
108
+ /**
109
+ * Handles state machine transitions
110
+ */
111
+ async _handleTransition(lifecycle, ...args) {
112
+ this._logger.log(`Request ${this._data.requestId} is transitioning from ${lifecycle.from} to ${lifecycle.to} in response to ${lifecycle.transition}`);
113
+
114
+ switch(lifecycle.transition) {
115
+ case 'init':
116
+ return;
117
+
118
+ case 'createAccounts':
119
+ return this._createAccounts();
120
+
121
+ case 'error':
122
+ this._logger.log(`State machine is erroring with error: ${util.inspect(args)}`);
123
+ this._data.lastError = args[0] || new BackendError('unspecified error', 500);
124
+ break;
125
+
126
+ default:
127
+ this._logger.log(`Unhandled state transition for request ${this._data.requestId}`);
128
+ }
129
+ }
130
+
131
+
132
+ async _executeCreateAccountsRequest(request) {
133
+ // eslint-disable-next-line no-async-promise-executor
134
+ return new Promise(async (resolve, reject) => {
135
+ const requestKey = `ac_${request.requestId}`;
136
+
137
+ const subId = await this._cache.subscribe(requestKey, async (cn, msg, subId) => {
138
+ try {
139
+ let error;
140
+ let message = JSON.parse(msg);
141
+
142
+ if (message.type === 'accountsCreationErrorResponse') {
143
+ error = new BackendError(`Got an error response creating accounts: ${util.inspect(message.data)}`, 500);
144
+ error.mojaloopError = message.data;
145
+ } else if (message.type !== 'accountsCreationSuccessfulResponse') {
146
+ this._logger.push({ message }).log(
147
+ `Ignoring cache notification for request ${requestKey}. ` +
148
+ `Unknown message type ${message.type}.`
149
+ );
150
+ return;
151
+ }
152
+
153
+ // cancel the timeout handler
154
+ clearTimeout(timeout);
155
+
156
+ // stop listening for account creation response messages.
157
+ // no need to await for the unsubscribe to complete.
158
+ // we dont really care if the unsubscribe fails but we should log it regardless
159
+ this._cache.unsubscribe(requestKey, subId).catch(e => {
160
+ this._logger.log(`Error unsubscribing (in callback) ${requestKey} ${subId}: ${e.stack || util.inspect(e)}`);
161
+ });
162
+
163
+ if (error) {
164
+ return reject(error);
165
+ }
166
+
167
+ const response = message.data;
168
+ this._logger.push({ response }).log('Account creation response received');
169
+ return resolve(response);
170
+ }
171
+ catch(err) {
172
+ return reject(err);
173
+ }
174
+ });
175
+
176
+ // set up a timeout for the request
177
+ const timeout = setTimeout(() => {
178
+ const err = new BackendError(`Timeout waiting for response to account creation request ${request.requestId}`, 504);
179
+
180
+ // we dont really care if the unsubscribe fails but we should log it regardless
181
+ this._cache.unsubscribe(requestKey, subId).catch(e => {
182
+ this._logger.log(`Error unsubscribing (in timeout handler) ${requestKey} ${subId}: ${e.stack || util.inspect(e)}`);
183
+ });
184
+
185
+ return reject(err);
186
+ }, this._requestProcessingTimeoutSeconds * 1000);
187
+
188
+ // now we have a timeout handler and a cache subscriber hooked up we can fire off
189
+ // a POST /participants request to the switch
190
+ try {
191
+ const res = await this._requests.postParticipants(request);
192
+ this._logger.push({ res }).log('Account creation request sent to peer');
193
+ }
194
+ catch(err) {
195
+ // cancel the timout and unsubscribe before rejecting the promise
196
+ clearTimeout(timeout);
197
+
198
+ // we dont really care if the unsubscribe fails but we should log it regardless
199
+ this._cache.unsubscribe(requestKey, subId).catch(e => {
200
+ this._logger.log(`Error unsubscribing (in error handler) ${requestKey} ${subId}: ${e.stack || util.inspect(e)}`);
201
+ });
202
+
203
+ return reject(err);
204
+ }
205
+ });
206
+ }
207
+
208
+
209
+ async _createAccounts() {
210
+ const requests = this._buildRequests();
211
+ for await (let request of requests) {
212
+ const response = await this._executeCreateAccountsRequest(request);
213
+ this._data.response.push(...this._buildClientResponse(response));
214
+ }
215
+ }
216
+
217
+ _buildClientResponse(response) {
218
+ return response.partyList.map(party => ({
219
+ idType: party.partyId.partyIdType,
220
+ idValue: party.partyId.partyIdentifier,
221
+ idSubValue: party.partyId.partySubIdOrType,
222
+ ...!response.currency && {
223
+ error: {
224
+ statusCode: Errors.MojaloopApiErrorCodes.CLIENT_ERROR.code,
225
+ message: 'Provided currency not supported',
226
+ }
227
+ },
228
+ ...party.errorInformation && {
229
+ error: {
230
+ statusCode: party.errorInformation.errorCode,
231
+ message: party.errorInformation.errorDescription,
232
+ },
233
+ },
234
+ }));
235
+ }
236
+
237
+
238
+ /**
239
+ * Builds accounts creation requests payload from current state
240
+ *
241
+ * @returns {Array} - the account creation requests
242
+ */
243
+ _buildRequests() {
244
+ const MAX_ITEMS_PER_REQUEST = 10000; // As per API Spec 6.2.2.2 (partyList field)
245
+
246
+ const requests = [];
247
+ for (let account of this._data.accounts) {
248
+ let request = requests.find(req =>
249
+ req.currency === account.currency && (req.partyList.length < MAX_ITEMS_PER_REQUEST));
250
+ if (!request) {
251
+ request = {
252
+ requestId: uuid(),
253
+ partyList: [],
254
+ currency: account.currency,
255
+ };
256
+ requests.push(request);
257
+ }
258
+ request.partyList.push({
259
+ partyIdType: account.idType,
260
+ partyIdentifier: account.idValue,
261
+ partySubIdOrType: account.idSubValue,
262
+ fspId: this._dfspId,
263
+ });
264
+ }
265
+ return requests;
266
+ }
267
+
268
+ /**
269
+ * Returns an object representing the final state of the transfer suitable for the outbound API
270
+ *
271
+ * @returns {object} - Response representing the result of the transfer process
272
+ */
273
+ getResponse() {
274
+ // we want to project some of our internal state into a more useful
275
+ // representation to return to the SDK API consumer
276
+ const resp = { ...this._data };
277
+
278
+ switch(this._data.currentState) {
279
+ case 'succeeded':
280
+ resp.currentState = stateEnum.COMPLETED;
281
+ break;
282
+
283
+ case 'errored':
284
+ resp.currentState = stateEnum.ERROR_OCCURRED;
285
+ break;
286
+
287
+ default:
288
+ this._logger.log(
289
+ `Account model response being returned from an unexpected state: ${this._data.currentState}. ` +
290
+ 'Returning ERROR_OCCURRED state'
291
+ );
292
+ resp.currentState = stateEnum.ERROR_OCCURRED;
293
+ break;
294
+ }
295
+
296
+ return resp;
297
+ }
298
+
299
+
300
+ /**
301
+ * Persists the model state to cache for reinitialisation at a later point
302
+ */
303
+ async _save() {
304
+ try {
305
+ this._data.currentState = this._stateMachine.state;
306
+ const res = await this._cache.set(`accountModel_${this._data.modelId}`, this._data);
307
+ this._logger.push({ res }).log('Persisted account model in cache');
308
+ }
309
+ catch(err) {
310
+ this._logger.push({ err }).log('Error saving account model');
311
+ throw err;
312
+ }
313
+ }
314
+
315
+
316
+ /**
317
+ * Loads an accounts model from cache for resumption of the accounts management process
318
+ *
319
+ * @param modelId {string} - UUID of the model to load from cache
320
+ */
321
+ async load(modelId) {
322
+ try {
323
+ const data = await this._cache.get(`accountModel_${modelId}`);
324
+ if(!data) {
325
+ throw new BackendError(`No cached data found for account model with id: ${modelId}`, 500);
326
+ }
327
+ await this.initialize(data);
328
+ this._logger.push({ cache: this._data }).log('Account model loaded from cached state');
329
+ }
330
+ catch(err) {
331
+ this._logger.push({ err }).log('Error loading account model');
332
+ throw err;
333
+ }
334
+ }
335
+
336
+
337
+ /**
338
+ * Returns a promise that resolves when the state machine has reached a terminal state
339
+ */
340
+ async run() {
341
+ try {
342
+ // run transitions based on incoming state
343
+ switch(this._data.currentState) {
344
+ case 'start': {
345
+ await this._stateMachine.createAccounts();
346
+ const accounts = this._data.response;
347
+ const failCount = accounts.filter((account) => account.error).length;
348
+ const successCount = this._data.response.length - failCount;
349
+ this._logger.log(`Accounts created: ${successCount} succeeded, ${failCount} failed`);
350
+ // if (failCount > 0) {
351
+ // throw new BackendError(`Failed to create ${failCount} account(s)`, 500);
352
+ // }
353
+ break;
354
+ }
355
+
356
+ case 'succeeded':
357
+ // all steps complete so return
358
+ this._logger.log('Accounts creation completed');
359
+ await this._save();
360
+ return this.getResponse();
361
+
362
+ case 'errored':
363
+ // stopped in errored state
364
+ this._logger.log('State machine in errored state');
365
+ return;
366
+ }
367
+
368
+ // now call ourslves recursively to deal with the next transition
369
+ this._logger.log(
370
+ `Account model state machine transition completed in state: ${this._stateMachine.state}. ` +
371
+ 'Handling next transition.'
372
+ );
373
+ return this.run();
374
+ }
375
+ catch(err) {
376
+ this._logger.log(`Error running account model: ${util.inspect(err)}`);
377
+
378
+ // as this function is recursive, we dont want to error the state machine multiple times
379
+ if(this._data.currentState !== 'errored') {
380
+ // err should not have a executionState property here!
381
+ if(err.executionState) {
382
+ this._logger.log(`State machine is broken: ${util.inspect(err)}`);
383
+ }
384
+ // transition to errored state
385
+ await this._stateMachine.error(err);
386
+
387
+ // avoid circular ref between executionState.lastError and err
388
+ err.executionState = JSON.parse(JSON.stringify(this.getResponse()));
389
+ }
390
+ throw err;
391
+ }
392
+ }
393
+ }
394
+
395
+
396
+ module.exports = AccountsModel;
@@ -0,0 +1,283 @@
1
+ /**************************************************************************
2
+ * (C) Copyright ModusBox Inc. 2021 - 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
+ * Paweł Marzec - pawel.marzec@modusbox.com *
9
+ **************************************************************************/
10
+
11
+ 'use strict';
12
+ const util = require('util');
13
+
14
+ const PSM = require('./common').PersistentStateMachine;
15
+ const MojaloopRequests = require('@mojaloop/sdk-standard-components').MojaloopRequests;
16
+ const deferredJob = require('./lib').deferredJob;
17
+
18
+ function generate({
19
+ /**
20
+ * @name channelNameMethod
21
+ * @description generates the pub/sub channel name
22
+ * @param {object} args - the arguments passed as object, same as passed to `run, triggerDeferredJob, generateKey` method
23
+ * @returns {string} - the pub/sub channel name
24
+ */
25
+ channelNameMethod,
26
+
27
+ /**
28
+ * @name requestActionMethod
29
+ * @description invokes the call to switch
30
+ * @param {object} requests - MojaloopRequests instance
31
+ * @param {array} args - the arguments passed as object to `run` method
32
+ */
33
+ requestActionMethod,
34
+
35
+ /**
36
+ * @name argsValidationMethod
37
+ * @description makes validation of args object, invoked in `run, triggerDeferredJob, generateKey` methods to ensure everything is going well
38
+ * @param {object} requests - MojaloopRequests instance
39
+ * @param {array} args - the arguments passed as object to `run` method
40
+ */
41
+ argsValidationMethod,
42
+
43
+ /**
44
+ * @name reformatMessageMethod
45
+ * @description reformats message received from PUB/SUB channel, it is optional method, if not specified identify function is used by default
46
+ * @param {object} message - message received
47
+ * @returns {object} - reformatted message
48
+ */
49
+ reformatMessageMethod,
50
+
51
+ // the name of the model, used for logging
52
+ modelName
53
+ }) {
54
+
55
+ // don't reformat message if method not specified
56
+ const reformatMessage = reformatMessageMethod || ((m) => m);
57
+
58
+ const specStateMachine = {
59
+ init: 'start',
60
+ transitions: [
61
+ { name: 'init', from: 'none', to: 'start' },
62
+ { name: 'requestAction', from: 'start', to: 'succeeded' },
63
+ { name: 'error', from: '*', to: 'errored' },
64
+ ],
65
+ methods: {
66
+ // workflow methods
67
+ run,
68
+ getResponse,
69
+
70
+ // specific transitions handlers methods
71
+ onRequestAction
72
+ }
73
+ };
74
+
75
+ /**
76
+ * @name run
77
+ * @description run the workflow logic
78
+ * @param {arguments} args - arguments
79
+ * @returns {Object} - the http response payload
80
+ */
81
+ async function run(args) {
82
+ // input validation, it should throws if any of args is invalid
83
+ argsValidationMethod(args);
84
+
85
+ const { data, logger } = this.context;
86
+ try {
87
+ // run transitions based on incoming state
88
+ switch (data.currentState) {
89
+ case 'start':
90
+ // the first transition is requestAction
91
+ await this.requestAction(args);
92
+ // don't await to finish the save
93
+ this.saveToCache();
94
+
95
+ // eslint-disable-next-line no-fallthrough
96
+ case 'succeeded':
97
+ // all steps complete so return
98
+ logger.log('Action called successfully');
99
+ return this.getResponse();
100
+
101
+ case 'errored':
102
+ // stopped in errored state
103
+ logger.log('State machine in errored state');
104
+ return;
105
+ }
106
+ } catch (err) {
107
+ logger.log(`Error running ${modelName} model: ${util.inspect(err)}`);
108
+
109
+ // as this function is recursive, we don't want to error the state machine multiple times
110
+ if (data.currentState !== 'errored') {
111
+ // err should not have a requestActionState property here!
112
+ if (err.requestActionState) {
113
+ logger.log('State machine is broken');
114
+ }
115
+ // transition to errored state
116
+ await this.error(err);
117
+
118
+ // avoid circular ref between requestActionState.lastError and err
119
+ err.requestActionState = JSON.parse(JSON.stringify(this.getResponse()));
120
+ }
121
+ throw err;
122
+ }
123
+ }
124
+
125
+ const mapCurrentState = {
126
+ start: 'WAITING_FOR_ACTION',
127
+ succeeded: 'COMPLETED',
128
+ errored: 'ERROR_OCCURRED'
129
+ };
130
+
131
+ /**
132
+ * @name getResponse
133
+ * @description returns the http response payload depending on which state machine is
134
+ * @returns {Object} - the http response payload
135
+ */
136
+ function getResponse() {
137
+ const { data, logger } = this.context;
138
+ let resp = { ...data };
139
+
140
+ // project some of our internal state into a more useful
141
+ // representation to return to the SDK API consumer
142
+ resp.currentState = mapCurrentState[data.currentState];
143
+
144
+ // handle unexpected state
145
+ if (!resp.currentState) {
146
+ logger.error(`${modelName} model response being returned from an unexpected state: ${data.currentState}. Returning ERROR_OCCURRED state`);
147
+ resp.currentState = mapCurrentState.errored;
148
+ }
149
+
150
+ return resp;
151
+ }
152
+ /**
153
+ * @name onRequestAction
154
+ * @description generates the pub/sub channel name
155
+ * @param {object} args - the arguments passed as object
156
+ * @returns {string} - the pub/sub channel name
157
+ */
158
+ async function onRequestAction(fsm, args) {
159
+ const { cache, logger } = this.context;
160
+ const { requests, config } = this.handlersContext;
161
+ logger.push({ args }).log('onRequestAction - arguments');
162
+
163
+ return deferredJob(cache, channelNameMethod(args))
164
+ .init(async (channel) => {
165
+ const res = await requestActionMethod(requests, args);
166
+ logger.push({ res, channel, args }).log('RequestAction call sent to peer, listening on response');
167
+ return res;
168
+ })
169
+ .job((message) => {
170
+ this.context.data = {
171
+ ...reformatMessage(message),
172
+ currentState: this.state
173
+ };
174
+ logger.push({ message }).log('requestActionMethod message received');
175
+ })
176
+ .wait(config.requestProcessingTimeoutSeconds * 1000);
177
+ }
178
+
179
+
180
+ /**
181
+ *
182
+ * @param {object} cache - the cache instance used to publish message
183
+ * @param {object} message - the message used to trigger deferred job
184
+ * @param {object} args - args passed to channelNameMethod
185
+ * @returns {Promise} - the promise which resolves when deferred job is invoked
186
+ */
187
+ async function triggerDeferredJob({ cache, message, args }) {
188
+ // input validation, it should throws if any of args is invalid
189
+ argsValidationMethod(args);
190
+
191
+ const cn = channelNameMethod(args);
192
+ return deferredJob(cache, cn).trigger(message);
193
+ }
194
+
195
+ /**
196
+ * @name generateKey
197
+ * @description generates the cache key used to store state machine
198
+ * @param {object} args - args passed to channelNameMethod
199
+
200
+ * @returns {string} - the cache key
201
+ */
202
+ function generateKey(args) {
203
+ // input validation, it should throws if any of args is invalid
204
+ argsValidationMethod(args);
205
+
206
+ return `key-${channelNameMethod(args)}`;
207
+ }
208
+
209
+
210
+ /**
211
+ * @name injectHandlersContext
212
+ * @description injects the config into state machine data, so it will be accessible to on transition notification handlers via `this.handlersContext`
213
+ * @param {Object} config - config to be injected into state machine data
214
+ * @returns {Object} - the altered specStateMachine
215
+ */
216
+ function injectHandlersContext(config) {
217
+ return {
218
+ ...specStateMachine,
219
+ data: {
220
+ handlersContext: {
221
+ modelName,
222
+ config: { ...config }, // injects config property
223
+ requests: new MojaloopRequests({
224
+ logger: config.logger,
225
+ peerEndpoint: config.peerEndpoint,
226
+ alsEndpoint: config.alsEndpoint,
227
+ quotesEndpoint: config.quotesEndpoint,
228
+ transfersEndpoint: config.transfersEndpoint,
229
+ transactionRequestsEndpoint: config.transactionRequestsEndpoint,
230
+ dfspId: config.dfspId,
231
+ tls: config.tls,
232
+ jwsSign: config.jwsSign,
233
+ jwsSignPutParties: config.jwsSignPutParties,
234
+ jwsSigningKey: config.jwsSigningKey,
235
+ wso2Auth: config.wso2Auth
236
+ })
237
+ }
238
+ }
239
+ };
240
+ }
241
+
242
+
243
+ /**
244
+ * @name create
245
+ * @description creates a new instance of state machine specified in specStateMachine ^
246
+ * @param {Object} data - payload data
247
+ * @param {String} key - the cache key where state machine will store the payload data after each transition
248
+ * @param {Object} config - the additional configuration for transition handlers
249
+ */
250
+ async function create(data, key, config) {
251
+ const spec = injectHandlersContext(config, specStateMachine);
252
+ return PSM.create(data, config.cache, key, config.logger, spec);
253
+ }
254
+
255
+
256
+ /**
257
+ * @name loadFromCache
258
+ * @description loads state machine from cache by given key and specify the additional config for transition handlers
259
+ * @param {String} key - the cache key used to retrieve the state machine from cache
260
+ * @param {Object} config - the additional configuration for transition handlers
261
+ */
262
+ async function loadFromCache(key, config) {
263
+ const customCreate = async (data, _cache, key) => create(data, key, config);
264
+ return PSM.loadFromCache(config.cache, key, config.logger, specStateMachine, customCreate);
265
+ }
266
+
267
+
268
+ return {
269
+ channelName: channelNameMethod,
270
+ triggerDeferredJob,
271
+ create,
272
+ generateKey,
273
+ loadFromCache,
274
+
275
+ // exports for testing purposes
276
+ mapCurrentState
277
+ };
278
+ }
279
+
280
+ module.exports = {
281
+ generate
282
+ };
283
+