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