account-lookup-service 15.5.0-iso.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (209) hide show
  1. package/.circleci/config.yml +11 -0
  2. package/.ncurc.yaml +6 -0
  3. package/.nvmrc +1 -0
  4. package/.nycrc.yml +20 -0
  5. package/.versionrc +15 -0
  6. package/CHANGELOG.md +330 -0
  7. package/CODEOWNERS +38 -0
  8. package/Dockerfile +45 -0
  9. package/LICENSE.md +10 -0
  10. package/README.md +252 -0
  11. package/audit-ci.jsonc +32 -0
  12. package/audit-resolve.json +161 -0
  13. package/config/default.json +109 -0
  14. package/config/knexfile.js +21 -0
  15. package/docker/account-lookup-service/default.json +106 -0
  16. package/docker/account-lookup-service/make-default-json.sh +5 -0
  17. package/docker/account-lookup-service/override.json +15 -0
  18. package/docker/central-ledger/default.json +458 -0
  19. package/docker/config-modifier/account-lookup-service.js +31 -0
  20. package/docker/kafka/consumer.properties +26 -0
  21. package/docker/kafka/producer.properties +45 -0
  22. package/docker/kafka/server.properties +143 -0
  23. package/docker/kafka/tools-log4j.properties +21 -0
  24. package/docker/mock-proxy/Dockerfile +15 -0
  25. package/docker/mock-proxy/package-lock.json +4986 -0
  26. package/docker/mock-proxy/package.json +24 -0
  27. package/docker/mock-proxy/src/config.ts +14 -0
  28. package/docker/mock-proxy/src/server.ts +94 -0
  29. package/docker/mock-proxy/src/utils.ts +29 -0
  30. package/docker/mock-proxy/tsconfig.json +24 -0
  31. package/docker/sql-init/01_permissions.sql +2 -0
  32. package/docker/sql-init-central-ledger/01_permissions.sql +2 -0
  33. package/docker/wait-for/wait-for-account-lookup-service.sh +10 -0
  34. package/docker/wait-for/wait-for-central-ledger.sh +11 -0
  35. package/docker/wait-for/wait-for-kafka.sh +7 -0
  36. package/docker/wait-for/wait-for-ml-api-adapter.sh +9 -0
  37. package/docker/wait-for/wait-for-mockserver.sh +20 -0
  38. package/docker/wait-for/wait-for-mysql-als.sh +14 -0
  39. package/docker/wait-for/wait-for-mysql-central-ledger.sh +11 -0
  40. package/docker/wait-for/wait-for-mysql.sh +11 -0
  41. package/docker/wait-for/wait-for-objstore.sh +12 -0
  42. package/docker/wait-for/wait-for.env +18 -0
  43. package/docker/wait-for/wait-for.sh +81 -0
  44. package/docker-compose.integration.yml +29 -0
  45. package/docker-compose.yml +243 -0
  46. package/jest-int.config.js +8 -0
  47. package/jest.config.js +16 -0
  48. package/jsdoc.json +38 -0
  49. package/migrations/01_currency.js +42 -0
  50. package/migrations/02_endpointType.js +43 -0
  51. package/migrations/03_endpointType-indexes.js +37 -0
  52. package/migrations/04_partyIdType.js +43 -0
  53. package/migrations/05_partyIdType-indexes.js +38 -0
  54. package/migrations/08_oracleEndpoint.js +51 -0
  55. package/migrations/09_oracleEndpoint-indexes.js +41 -0
  56. package/migrations/10_oracleEndpoint-remove-constraints.js +38 -0
  57. package/package.json +180 -0
  58. package/scripts/_wait4_all.js +143 -0
  59. package/scripts/test-functional.sh +76 -0
  60. package/secrets/jwsSigningKey.key +27 -0
  61. package/seeds/currency.js +765 -0
  62. package/seeds/endpointType.js +65 -0
  63. package/seeds/partyIdType.js +79 -0
  64. package/src/api/endpointcache.js +67 -0
  65. package/src/api/health.js +66 -0
  66. package/src/api/index.js +85 -0
  67. package/src/api/oracles/{ID}.js +100 -0
  68. package/src/api/oracles.js +96 -0
  69. package/src/api/participants/{ID}/error.js +44 -0
  70. package/src/api/participants/{ID}.js +44 -0
  71. package/src/api/participants/{Type}/{ID}/error.js +74 -0
  72. package/src/api/participants/{Type}/{ID}/{SubId}/error.js +68 -0
  73. package/src/api/participants/{Type}/{ID}/{SubId}.js +113 -0
  74. package/src/api/participants/{Type}/{ID}.js +133 -0
  75. package/src/api/participants.js +63 -0
  76. package/src/api/parties/{Type}/{ID}/error.js +66 -0
  77. package/src/api/parties/{Type}/{ID}/{SubId}/error.js +56 -0
  78. package/src/api/parties/{Type}/{ID}/{SubId}.js +77 -0
  79. package/src/api/parties/{Type}/{ID}.js +98 -0
  80. package/src/api/routes.js +294 -0
  81. package/src/constants.js +16 -0
  82. package/src/domain/oracle/index.js +33 -0
  83. package/src/domain/oracle/oracle.js +234 -0
  84. package/src/domain/participants/index.js +35 -0
  85. package/src/domain/participants/participants.js +560 -0
  86. package/src/domain/parties/getPartiesByTypeAndID.js +239 -0
  87. package/src/domain/parties/index.js +32 -0
  88. package/src/domain/parties/parties.js +215 -0
  89. package/src/domain/parties/utils.js +84 -0
  90. package/src/domain/timeout/dto.js +48 -0
  91. package/src/domain/timeout/index.js +104 -0
  92. package/src/handlers/TimeoutHandler.js +94 -0
  93. package/src/handlers/index.js +70 -0
  94. package/src/handlers/monitoring/index.js +51 -0
  95. package/src/handlers/monitoring/plugins/health.js +61 -0
  96. package/src/handlers/monitoring/plugins/metrics.js +48 -0
  97. package/src/handlers/register.js +102 -0
  98. package/src/index.js +66 -0
  99. package/src/interface/admin-swagger.yaml +804 -0
  100. package/src/interface/admin_swagger.json +959 -0
  101. package/src/interface/api-swagger-iso20022-parties.yaml +1734 -0
  102. package/src/interface/api-swagger.yaml +1733 -0
  103. package/src/interface/api_swagger.json +3046 -0
  104. package/src/interface/fspiop-rest-v2.0-ISO20022_parties.yaml +2256 -0
  105. package/src/interface/thirdparty/admin-swagger.yaml +808 -0
  106. package/src/interface/thirdparty/admin_swagger.json +961 -0
  107. package/src/interface/thirdparty/api-swagger.yaml +1739 -0
  108. package/src/interface/thirdparty/api_swagger.json +3142 -0
  109. package/src/lib/argv.js +39 -0
  110. package/src/lib/cache.js +126 -0
  111. package/src/lib/config.js +183 -0
  112. package/src/lib/db.js +26 -0
  113. package/src/lib/headers.js +53 -0
  114. package/src/lib/healthCheck/subServiceHealth.js +84 -0
  115. package/src/lib/index.js +11 -0
  116. package/src/lib/migrator.js +17 -0
  117. package/src/lib/requestLogger.js +54 -0
  118. package/src/lib/util.js +66 -0
  119. package/src/metrics/handler.js +33 -0
  120. package/src/metrics/plugin.js +52 -0
  121. package/src/metrics/routes.js +43 -0
  122. package/src/models/currency/currency.js +48 -0
  123. package/src/models/currency/index.js +32 -0
  124. package/src/models/endpointType/endpointType.js +48 -0
  125. package/src/models/endpointType/index.js +32 -0
  126. package/src/models/misc/migrationLock.js +49 -0
  127. package/src/models/oracle/facade.js +341 -0
  128. package/src/models/oracle/index.js +41 -0
  129. package/src/models/oracle/oracleEndpoint.js +192 -0
  130. package/src/models/oracle/oracleEndpointCached.js +108 -0
  131. package/src/models/participantEndpoint/facade.js +238 -0
  132. package/src/models/partyIdType/index.js +32 -0
  133. package/src/models/partyIdType/partyIdType.js +41 -0
  134. package/src/plugins.js +139 -0
  135. package/src/server.js +199 -0
  136. package/test/fixtures/index.js +131 -0
  137. package/test/fixtures/iso.js +110 -0
  138. package/test/integration/.env +8 -0
  139. package/test/integration/api/parties.test.js +137 -0
  140. package/test/integration/constants.js +20 -0
  141. package/test/integration/domain/oracle/index.test.js +324 -0
  142. package/test/integration/domain/timeout/index.test.js +75 -0
  143. package/test/integration/env.sh +15 -0
  144. package/test/integration/example.test.js +12 -0
  145. package/test/integration/models/currency/currency.test.js +68 -0
  146. package/test/integration/plugins.test.js +62 -0
  147. package/test/integration/prepareTestParticipants.js +30 -0
  148. package/test/integration/setup.js +5 -0
  149. package/test/integration-config.json +81 -0
  150. package/test/integration-runner.sh +108 -0
  151. package/test/unit/api/health.test.js +142 -0
  152. package/test/unit/api/oracles/{ID}.test.js +264 -0
  153. package/test/unit/api/oracles.test.js +173 -0
  154. package/test/unit/api/participants/participants.test.js +117 -0
  155. package/test/unit/api/participants/{Type}/{ID}/error.test.js +155 -0
  156. package/test/unit/api/participants/{Type}/{ID}/{SubId}/error.test.js +131 -0
  157. package/test/unit/api/participants/{Type}/{ID}/{SubId}.test.js +377 -0
  158. package/test/unit/api/participants/{Type}/{ID}.test.js +383 -0
  159. package/test/unit/api/participants.test.js +108 -0
  160. package/test/unit/api/parties/endpointcache.test.js +83 -0
  161. package/test/unit/api/parties/parties.test.js +102 -0
  162. package/test/unit/api/parties/{Type}/{ID}/error.test.js +145 -0
  163. package/test/unit/api/parties/{Type}/{ID}/{SubId}/error.test.js +141 -0
  164. package/test/unit/api/parties/{Type}/{ID}/{SubId}.test.js +241 -0
  165. package/test/unit/api/parties/{Type}/{ID}.test.js +240 -0
  166. package/test/unit/domain/oracle/oracle.test.js +505 -0
  167. package/test/unit/domain/participants/participants.test.js +1724 -0
  168. package/test/unit/domain/parties/parties.test.js +940 -0
  169. package/test/unit/domain/timeout/dto.test.js +28 -0
  170. package/test/unit/domain/timeout/index.test.js +81 -0
  171. package/test/unit/handlers/TimeoutHandler.test.js +125 -0
  172. package/test/unit/handlers/index.test.js +56 -0
  173. package/test/unit/handlers/register.test.js +90 -0
  174. package/test/unit/index.test.js +139 -0
  175. package/test/unit/iso20022/partiesValidation.test.js +129 -0
  176. package/test/unit/lib/TransformFacades.test.js +18 -0
  177. package/test/unit/lib/argv.test.js +40 -0
  178. package/test/unit/lib/cache.test.js +172 -0
  179. package/test/unit/lib/config.test.js +108 -0
  180. package/test/unit/lib/healthCheck/subServiceHealth.test.js +89 -0
  181. package/test/unit/lib/migrator.test.js +52 -0
  182. package/test/unit/lib/requestLogger.test.js +115 -0
  183. package/test/unit/lib/util.test.js +68 -0
  184. package/test/unit/mocks.js +66 -0
  185. package/test/unit/models/currency/currency.test.js +91 -0
  186. package/test/unit/models/endpointType/endpointType.test.js +69 -0
  187. package/test/unit/models/misc/migrationLock.test.js +96 -0
  188. package/test/unit/models/oracle/facade.test.js +546 -0
  189. package/test/unit/models/oracle/oracleEndpoint.test.js +409 -0
  190. package/test/unit/models/oracle/oracleEndpointCached.test.js +153 -0
  191. package/test/unit/models/participantEndpoint/facade.test.js +295 -0
  192. package/test/unit/models/partyIdType/partyIdType.test.js +88 -0
  193. package/test/unit/plugins.test.js +89 -0
  194. package/test/unit/setup.js +7 -0
  195. package/test/util/apiClients/AlsApiClient.js +44 -0
  196. package/test/util/apiClients/BasicApiClient.js +34 -0
  197. package/test/util/apiClients/ProxyApiClient.js +25 -0
  198. package/test/util/apiClients/index.js +7 -0
  199. package/test/util/helper.js +332 -0
  200. package/test/util/index.js +11 -0
  201. package/test/util/mockgen.js +43 -0
  202. package/test/util/onboarding.js +132 -0
  203. package/test/util/scripts/addAlsDb.sh +33 -0
  204. package/test/util/scripts/configureMockServer.sh +35 -0
  205. package/test/util/scripts/env.sh +19 -0
  206. package/test/util/scripts/populateTestData.sh +62 -0
  207. package/test/util/scripts/startMockCentralServer.sh +45 -0
  208. package/test/util/scripts/startMockOracleServer.sh +45 -0
  209. package/test/util/testConfig.js +44 -0
@@ -0,0 +1,324 @@
1
+ /*****
2
+ License
3
+ --------------
4
+ Copyright © 2017 Bill & Melinda Gates Foundation
5
+ The Mojaloop files are made available by the Bill & Melinda Gates Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at
6
+ http://www.apache.org/licenses/LICENSE-2.0
7
+ Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
8
+ Contributors
9
+ --------------
10
+ This is the official list of the Mojaloop project contributors for this file.
11
+ Names of the original copyright holders (individuals or organizations)
12
+ should be listed with a '*' in the first column. People who have
13
+ contributed from an organization can be listed under the organization
14
+ that actually holds the copyright for their contributions (see the
15
+ Gates Foundation organization for an example). Those individuals should have
16
+ their names indented and be marked with a '-'. Email address can be added
17
+ optionally within square brackets <email>.
18
+ * Gates Foundation
19
+ - Name Surname <name.surname@gatesfoundation.com>
20
+
21
+ * Crosslake
22
+ - Lewis Daly <lewisd@crosslaketech.com>
23
+ - Kevin Leyow <kevin.leyow@modusbox.com>
24
+
25
+ --------------
26
+ ******/
27
+
28
+ const Db = require('../../../../src/lib/db')
29
+ const Config = require('../../../../src/lib/config')
30
+ const Oracle = require('../../../../src/domain/oracle')
31
+ const OracleModel = require('../../../../src/models/oracle')
32
+ const EventSdk = require('@mojaloop/event-sdk')
33
+
34
+ describe('Oracle', () => {
35
+ beforeAll(async () => {
36
+ await Db.connect(Config.DATABASE)
37
+ })
38
+
39
+ afterAll(async () => {
40
+ await Db.disconnect()
41
+ })
42
+
43
+ const createHeaders = {
44
+ accept: 'application/vnd.interoperability.participants+json;version=1',
45
+ 'cache-control': 'no-cache',
46
+ date: '',
47
+ 'content-type': 'application/vnd.interoperability.participants+json;version=1.1',
48
+ host: '127.0.0.1:4003',
49
+ 'accept-encoding': 'gzip, deflate',
50
+ 'content-length': 164,
51
+ connection: 'keep-alive'
52
+ }
53
+ const testSpan = EventSdk.Tracer.createSpan('createOracle service')
54
+
55
+ const oracleAuhMsisdnUrlPayload = {
56
+ isDefault: true,
57
+ currency: 'AUD',
58
+ oracleIdType: 'MSISDN',
59
+ endpoint: {
60
+ value: 'http://localhost:8444',
61
+ endpointType: 'URL'
62
+ }
63
+ }
64
+
65
+ const oracleUsdMsisdnUrlPayload = {
66
+ isDefault: true,
67
+ currency: 'USD',
68
+ oracleIdType: 'MSISDN',
69
+ endpoint: {
70
+ value: 'http://localhost:8444',
71
+ endpointType: 'URL'
72
+ }
73
+ }
74
+
75
+ const oracleAudEmailUrlPayload = {
76
+ isDefault: true,
77
+ currency: 'AUD',
78
+ oracleIdType: 'EMAIL',
79
+ endpoint: {
80
+ value: 'http://localhost:8444',
81
+ endpointType: 'URL'
82
+ }
83
+ }
84
+
85
+ describe('Create', () => {
86
+ it('creates an oracle', async () => {
87
+ const result = await Oracle.createOracle(oracleAuhMsisdnUrlPayload, createHeaders, testSpan)
88
+ expect(result).toBe(true)
89
+
90
+ const oracleEndpointResult = await OracleModel.getOracleEndpointByType('MSISDN')
91
+ const createdId = oracleEndpointResult[0].oracleEndpointId
92
+ await Db.from('oracleEndpoint').destroy({ oracleEndpointId: createdId })
93
+ })
94
+
95
+ it('creates a similar oracle if the first is inactive', async () => {
96
+ const result = await Oracle.createOracle(oracleAuhMsisdnUrlPayload, createHeaders, testSpan)
97
+ expect(result).toBe(true)
98
+
99
+ // Delete/mark first oracle inactive
100
+ const previousOracleResult = await OracleModel.getOracleEndpointByType('MSISDN')
101
+ const firstOracleCreatedId = previousOracleResult[0].oracleEndpointId
102
+ await Oracle.deleteOracle({ ID: firstOracleCreatedId })
103
+
104
+ const secondResult = await Oracle.createOracle(oracleAuhMsisdnUrlPayload, createHeaders, testSpan)
105
+ expect(secondResult).toBe(true)
106
+
107
+ const oracleEndpointResult = await OracleModel.getOracleEndpointByType('MSISDN')
108
+ const activeCreatedId = oracleEndpointResult[0].oracleEndpointId
109
+ await Db.from('oracleEndpoint').destroy({ oracleEndpointId: activeCreatedId })
110
+
111
+ // Cleanup inactive endpoint for good measure
112
+ await Db.from('oracleEndpoint').destroy({ oracleEndpointId: firstOracleCreatedId })
113
+ })
114
+
115
+ it('rejects creating a similar oracle when one is still active', async () => {
116
+ const result = await Oracle.createOracle(oracleAuhMsisdnUrlPayload, createHeaders, testSpan)
117
+ expect(result).toBe(true)
118
+
119
+ try {
120
+ await Oracle.createOracle(oracleAuhMsisdnUrlPayload, createHeaders, testSpan)
121
+ } catch (error) {
122
+ expect(error.message).toBe('Active oracle with matching partyIdTypeId, endpointTypeId, currencyId already exists')
123
+ }
124
+
125
+ const oracleEndpointResult = await OracleModel.getOracleEndpointByType('MSISDN')
126
+ const createdId = oracleEndpointResult[0].oracleEndpointId
127
+ await Db.from('oracleEndpoint').destroy({ oracleEndpointId: createdId })
128
+ })
129
+ })
130
+
131
+ describe('Update', () => {
132
+ it('updates an oracle', async () => {
133
+ const createResult = await Oracle.createOracle(oracleAuhMsisdnUrlPayload, createHeaders, testSpan)
134
+ expect(createResult).toBe(true)
135
+
136
+ // Get newly created oracle
137
+ const oracleEndpointResult = await OracleModel.getOracleEndpointByType('MSISDN')
138
+ const createdId = oracleEndpointResult[0].oracleEndpointId
139
+
140
+ // Update the oracle's currency
141
+ const updateResult = await Oracle.updateOracle(
142
+ {
143
+ ID: createdId
144
+ },
145
+ {
146
+ isDefault: true,
147
+ currency: 'USD',
148
+ oracleIdType: 'MSISDN',
149
+ endpoint: {
150
+ value: 'http://localhost:8444',
151
+ endpointType: 'URL'
152
+ }
153
+ }
154
+ )
155
+ expect(updateResult).toBe(true)
156
+
157
+ const updatedOracleEndpointResult = await OracleModel.getOracleEndpointByType('MSISDN')
158
+ expect(updatedOracleEndpointResult[0].currency).toEqual('USD')
159
+
160
+ // Cleanup
161
+ const updatedId = updatedOracleEndpointResult[0].oracleEndpointId
162
+ await Db.from('oracleEndpoint').destroy({ oracleEndpointId: updatedId })
163
+ })
164
+
165
+ it('updates an oracle even if there is a similar oracle that is inactive', async () => {
166
+ const createResultAUD = await Oracle.createOracle(
167
+ oracleAuhMsisdnUrlPayload,
168
+ createHeaders,
169
+ testSpan
170
+ )
171
+ expect(createResultAUD).toBe(true)
172
+
173
+ // Get newly created AUD currency oracle
174
+ const oracleEndpointResultAUD = await OracleModel.getOracleEndpointByType('MSISDN')
175
+ const createdIdAUD = oracleEndpointResultAUD[0].oracleEndpointId
176
+
177
+ // Delete/mark first AUD currency oracle inactive
178
+ await Oracle.deleteOracle({ ID: createdIdAUD })
179
+
180
+ const payloadUSD = {
181
+ isDefault: true,
182
+ currency: 'USD',
183
+ oracleIdType: 'MSISDN',
184
+ endpoint: {
185
+ value: 'http://localhost:8444',
186
+ endpointType: 'URL'
187
+ }
188
+ }
189
+
190
+ // Create USD currency oracle
191
+ const createResultUSD = await Oracle.createOracle(payloadUSD, createHeaders, testSpan)
192
+ expect(createResultUSD).toBe(true)
193
+
194
+ // Get newly created USD currency oracle
195
+ const oracleEndpointResultUSD = await OracleModel.getOracleEndpointByType('MSISDN')
196
+ const createdIdUSD = oracleEndpointResultUSD[0].oracleEndpointId
197
+
198
+ // Update the USD currency oracle to be AUD currency
199
+ const updateResult = await Oracle.updateOracle(
200
+ {
201
+ ID: createdIdUSD
202
+ },
203
+ {
204
+ isDefault: true,
205
+ currency: 'AUD',
206
+ oracleIdType: 'MSISDN',
207
+ endpoint: {
208
+ value: 'http://localhost:8444',
209
+ endpointType: 'URL'
210
+ }
211
+ }
212
+ )
213
+
214
+ // Assert true since previous AUD currency oracle was deleted
215
+ expect(updateResult).toBe(true)
216
+
217
+ // Cleanup
218
+ const updatedOracleEndpointResult = await OracleModel.getOracleEndpointByType('MSISDN')
219
+ expect(updatedOracleEndpointResult[0].currency).toEqual('AUD')
220
+
221
+ const updatedId = updatedOracleEndpointResult[0].oracleEndpointId
222
+ await Db.from('oracleEndpoint').destroy({ oracleEndpointId: updatedId })
223
+
224
+ // Cleanup inactive model for good measure
225
+ await Db.from('oracleEndpoint').destroy({ oracleEndpointId: createdIdAUD })
226
+ })
227
+
228
+ it('rejects updating an oracles currency if updating it would match an existing active oracle', async () => {
229
+ const testSpan = EventSdk.Tracer.createSpan('createOracle service')
230
+ const createResultAUD = await Oracle.createOracle(
231
+ oracleAuhMsisdnUrlPayload,
232
+ createHeaders,
233
+ testSpan
234
+ )
235
+ expect(createResultAUD).toBe(true)
236
+
237
+ // Create USD currency oracle
238
+ const createResultUSD = await Oracle.createOracle(
239
+ oracleUsdMsisdnUrlPayload,
240
+ createHeaders,
241
+ testSpan
242
+ )
243
+ expect(createResultUSD).toBe(true)
244
+
245
+ // Get newly created USD currency oracle
246
+ const oracleEndpointResultUSD = await OracleModel.getOracleEndpointByTypeAndCurrency('MSISDN', 'USD')
247
+ const createdIdUSD = oracleEndpointResultUSD[0].oracleEndpointId
248
+
249
+ // Update the USD currency oracle to be AUD currency
250
+ try {
251
+ await Oracle.updateOracle(
252
+ {
253
+ ID: createdIdUSD
254
+ },
255
+ oracleAuhMsisdnUrlPayload
256
+ )
257
+ } catch (error) {
258
+ // Should throw error since there is an existing active oracle
259
+ // with similar currency
260
+ expect(error.message).toBe('Active oracle with matching partyIdTypeId, endpointTypeId, currencyId already exists')
261
+ }
262
+
263
+ // Cleanup
264
+ const oracleEndpointResultMSISDNAUD = await OracleModel.getOracleEndpointByTypeAndCurrency('MSISDN', 'AUD')
265
+ const oracleEndpointResultMSISDNUSD = await OracleModel.getOracleEndpointByTypeAndCurrency('MSISDN', 'USD')
266
+ await Db.from('oracleEndpoint').destroy({
267
+ oracleEndpointId: oracleEndpointResultMSISDNUSD[0].oracleEndpointId
268
+ })
269
+ await Db.from('oracleEndpoint').destroy({
270
+ oracleEndpointId: oracleEndpointResultMSISDNAUD[0].oracleEndpointId
271
+ })
272
+ })
273
+
274
+ it('rejects updating an oracles id type if updating it would match an existing active oracle', async () => {
275
+ const testSpan = EventSdk.Tracer.createSpan('createOracle service')
276
+ const createResultMSISDN = await Oracle.createOracle(
277
+ oracleAuhMsisdnUrlPayload,
278
+ createHeaders,
279
+ testSpan
280
+ )
281
+ expect(createResultMSISDN).toBe(true)
282
+
283
+ // Create email id type oracle
284
+ const createResultEMAIL = await Oracle.createOracle(
285
+ oracleAudEmailUrlPayload,
286
+ createHeaders,
287
+ testSpan
288
+ )
289
+ expect(createResultEMAIL).toBe(true)
290
+
291
+ // Get newly created email id type oracle
292
+ const oracleEndpointResultEmail = await OracleModel.getOracleEndpointByTypeAndCurrency('EMAIL', 'AUD')
293
+ const createdIdEmail = oracleEndpointResultEmail[0].oracleEndpointId
294
+
295
+ // Update the email id type oracle to be MSISDN id type oracle
296
+ try {
297
+ await Oracle.updateOracle(
298
+ {
299
+ ID: createdIdEmail
300
+ },
301
+ oracleAuhMsisdnUrlPayload
302
+ )
303
+ } catch (error) {
304
+ // Should throw error since there is an existing active oracle
305
+ // with similar id type
306
+ expect(error.message).toBe('Active oracle with matching partyIdTypeId, endpointTypeId, currencyId already exists')
307
+ }
308
+
309
+ // Cleanup
310
+ const oracleEndpointResultMSISDNAUD = await OracleModel.getOracleEndpointByTypeAndCurrency('MSISDN', 'AUD')
311
+ const oracleEndpointResultEMAILAUD = await OracleModel.getOracleEndpointByTypeAndCurrency('EMAIL', 'AUD')
312
+ await Db.from('oracleEndpoint').destroy({
313
+ oracleEndpointId: oracleEndpointResultMSISDNAUD[0].oracleEndpointId
314
+ })
315
+ await Db.from('oracleEndpoint').destroy({
316
+ oracleEndpointId: oracleEndpointResultEMAILAUD[0].oracleEndpointId
317
+ })
318
+ })
319
+
320
+ // Currently there is only one endpoint type, that being 'URL'.
321
+ // Would need to add tests when more endpoints are added.
322
+ it.todo('rejects updating an oracles endpoint type if updating it would match an existing active oracle')
323
+ })
324
+ })
@@ -0,0 +1,75 @@
1
+ const { createProxyCache } = require('@mojaloop/inter-scheme-proxy-cache-lib')
2
+ const { PAYER_DFSP, PARTY_ID_TYPE, PROXY_NAME } = require('../../constants')
3
+ const fixtures = require('../../../fixtures')
4
+ const { ProxyApiClient } = require('../../../util')
5
+ const config = require('../../../../src/lib/config')
6
+
7
+ const wait = (sec) => new Promise(resolve => setTimeout(resolve, sec * 1000))
8
+
9
+ const CRON_TIMEOUT_SEC = 10 // see TIMEXP
10
+
11
+ describe('Timeout Handler', () => {
12
+ const { type, proxyConfig } = config.PROXY_CACHE_CONFIG
13
+ const proxyCache = createProxyCache(type, proxyConfig)
14
+ const proxyClient = new ProxyApiClient()
15
+
16
+ beforeAll(async () => {
17
+ await proxyCache.connect()
18
+ const redisClient = proxyCache.redisClient
19
+ const redisNodes = redisClient.nodes ? redisClient.nodes('master') : [redisClient]
20
+ await Promise.all(redisNodes.map(async node => {
21
+ await node.flushall()
22
+ }))
23
+ })
24
+
25
+ afterAll(async () => {
26
+ return Promise.all([
27
+ proxyClient.deleteHistory(),
28
+ proxyCache.disconnect()
29
+ ])
30
+ })
31
+
32
+ it('test', async () => {
33
+ let history = await proxyClient.deleteHistory()
34
+ expect(history).toEqual([])
35
+
36
+ // send a couple of keys to redis
37
+ const partyIds = ['1234567', '7654321']
38
+ const keys = [
39
+ `'als:${PAYER_DFSP}:${PARTY_ID_TYPE}:${partyIds[0]}:expiresAt'`,
40
+ `'als:${PAYER_DFSP}:${PARTY_ID_TYPE}:${partyIds[1]}:expiresAt'`
41
+ ]
42
+ const proxies = [PROXY_NAME]
43
+ const alsReq1 = fixtures.mockAlsRequestDto(PAYER_DFSP, PARTY_ID_TYPE, partyIds[0])
44
+ const alsReq2 = fixtures.mockAlsRequestDto(PAYER_DFSP, PARTY_ID_TYPE, partyIds[1])
45
+ const results = await Promise.all([
46
+ proxyCache.setSendToProxiesList(alsReq1, proxies, CRON_TIMEOUT_SEC),
47
+ proxyCache.setSendToProxiesList(alsReq2, proxies, CRON_TIMEOUT_SEC)
48
+ ])
49
+ expect(results.includes(false)).toBe(false)
50
+
51
+ // wait for the timeout handler to process the keys
52
+ await wait(CRON_TIMEOUT_SEC * 1.5)
53
+
54
+ // check that the keys are no longer in redis
55
+ const exists = await Promise.all(keys.map(key => proxyCache.redisClient.exists(key)))
56
+ expect(exists.includes(1)).toBe(false)
57
+
58
+ // check that the callbacks are sent and received at the FSP
59
+ // for test resilience, we will retry the history check a few times
60
+ const retryMaxCount = 20
61
+ const retryIntervalSec = 2
62
+ let retryCount = 0
63
+
64
+ while (history.length < 2 && retryCount < retryMaxCount) {
65
+ await wait(retryIntervalSec)
66
+ history = await proxyClient.getHistory()
67
+ retryCount++
68
+ }
69
+ expect(history.length).toBe(2)
70
+ const path0 = history.find(h => h.path.includes(partyIds[0])).path
71
+ const path1 = history.find(h => h.path.includes(partyIds[1])).path
72
+ expect(path0).toBe(`/parties/${PARTY_ID_TYPE}/${partyIds[0]}/error`)
73
+ expect(path1).toBe(`/parties/${PARTY_ID_TYPE}/${partyIds[1]}/error`)
74
+ }, 60_000)
75
+ })
@@ -0,0 +1,15 @@
1
+ #!/bin/sh
2
+
3
+ # Retrieve the external IP address of the host machine (on macOS)
4
+ # or the IP address of the docker0 interface (on Linux)
5
+ get_external_ip() {
6
+ if [ "$(uname)" = "Linux" ]; then
7
+ echo "$(ip addr show docker0 | grep 'inet ' | awk '{print $2}' | cut -d/ -f1)"
8
+ else
9
+ # Need to find a way to support Windows here
10
+ echo "$(route get ifconfig.me | grep interface | sed -e 's/.*: //' | xargs ipconfig getifaddr)"
11
+ fi
12
+ }
13
+
14
+ # set/override dynamic variables
15
+ export REDIS_CLUSTER_ANNOUNCE_IP=$(get_external_ip)
@@ -0,0 +1,12 @@
1
+ describe('example test', () => {
2
+ it('runs an example integration test', async () => {
3
+ // Arrange
4
+ const expected = 7
5
+
6
+ // Act
7
+ const result = 5 + 2
8
+
9
+ // Assert
10
+ expect(result).toBe(expected)
11
+ })
12
+ })
@@ -0,0 +1,68 @@
1
+ /*****
2
+ License
3
+ --------------
4
+ Copyright © 2017 Bill & Melinda Gates Foundation
5
+ The Mojaloop files are made available by the Bill & Melinda Gates Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at
6
+ http://www.apache.org/licenses/LICENSE-2.0
7
+ Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
8
+ Contributors
9
+ --------------
10
+ This is the official list of the Mojaloop project contributors for this file.
11
+ Names of the original copyright holders (individuals or organizations)
12
+ should be listed with a '*' in the first column. People who have
13
+ contributed from an organization can be listed under the organization
14
+ that actually holds the copyright for their contributions (see the
15
+ Gates Foundation organization for an example). Those individuals should have
16
+ their names indented and be marked with a '-'. Email address can be added
17
+ optionally within square brackets <email>.
18
+ * Gates Foundation
19
+ - Name Surname <name.surname@gatesfoundation.com>
20
+
21
+ * Crosslake
22
+ - Lewis Daly <lewisd@crosslaketech.com>
23
+
24
+ --------------
25
+ ******/
26
+
27
+ 'use strict'
28
+
29
+ const Db = require('../../../../src/lib/db')
30
+ const Config = require('../../../../src/lib/config')
31
+ const { getCurrencyById } = require('../../../../src/models/currency')
32
+
33
+ describe('currency model', () => {
34
+ beforeAll(async () => {
35
+ await Db.connect(Config.DATABASE)
36
+ })
37
+
38
+ afterAll(async () => {
39
+ await Db.disconnect()
40
+ })
41
+
42
+ describe('getCurrencyById', () => {
43
+ it('gets a currency by id', async () => {
44
+ // Arrange
45
+ const expected = {
46
+ currencyId: 'AUD',
47
+ name: 'Australian dollar',
48
+ isActive: 1
49
+ }
50
+
51
+ // Act
52
+ const result = await getCurrencyById('AUD')
53
+ delete result.createdDate
54
+
55
+ // Assert
56
+ expect(result).toMatchObject(expected)
57
+ })
58
+
59
+ it('returns null when it cannot find a currency', async () => {
60
+ // Arrange
61
+ // Act
62
+ const currency = await getCurrencyById('UUU')
63
+
64
+ // Assert
65
+ expect(currency).toBe(null)
66
+ })
67
+ })
68
+ })
@@ -0,0 +1,62 @@
1
+ /*****
2
+ License
3
+ --------------
4
+ Copyright © 2017 Bill & Melinda Gates Foundation
5
+ The Mojaloop files are made available by the Bill & Melinda Gates Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at
6
+ http://www.apache.org/licenses/LICENSE-2.0
7
+ Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
8
+ Contributors
9
+ --------------
10
+ This is the official list of the Mojaloop project contributors for this file.
11
+ Names of the original copyright holders (individuals or organizations)
12
+ should be listed with a '*' in the first column. People who have
13
+ contributed from an organization can be listed under the organization
14
+ that actually holds the copyright for their contributions (see the
15
+ Gates Foundation organization for an example). Those individuals should have
16
+ their names indented and be marked with a '-'. Email address can be added
17
+ optionally within square brackets <email>.
18
+ * Gates Foundation
19
+ - Name Surname <name.surname@gatesfoundation.com>
20
+
21
+ * Crosslake
22
+ - Lewis Daly <lewisd@crosslaketech.com>
23
+ - Miguel de Barros <miguel.debarros@modusbox.com>
24
+
25
+ --------------
26
+ ******/
27
+
28
+ 'use strict'
29
+
30
+ const axios = require('axios').default
31
+
32
+ const {
33
+ TEST_ALS_HOST,
34
+ ADMIN_PORT,
35
+ API_PORT
36
+ } = require('../util/testConfig')
37
+
38
+ describe('plugins', () => {
39
+ describe('API documentation - /swagger.json', () => {
40
+ it('loads the swagger file for the admin api', async () => {
41
+ // Arrange
42
+ // nothing to do
43
+
44
+ // Act
45
+ const response = await axios.get(`http://${TEST_ALS_HOST}:${ADMIN_PORT}/swagger.json`)
46
+
47
+ // Assert
48
+ expect(response.data.info.title).toBe('Open API for ALS Admin API')
49
+ })
50
+
51
+ it('loads the swagger file for the default api', async () => {
52
+ // Arrange
53
+ // nothing to do
54
+
55
+ // Act
56
+ const response = await axios.get(`http://${TEST_ALS_HOST}:${API_PORT}/swagger.json`)
57
+
58
+ // Assert
59
+ expect(response.data.info.title).toBe('Open API for FSP Interoperability (FSPIOP) (Implementation Friendly Version)')
60
+ })
61
+ })
62
+ })
@@ -0,0 +1,30 @@
1
+ require('./setup')
2
+
3
+ const Logger = require('@mojaloop/central-services-logger')
4
+ const { onboarding } = require('../util')
5
+ const { PROXY_NAME, PAYER_DFSP } = require('../integration/constants')
6
+
7
+ const pause = async (ms = 1000) => new Promise(resolve => {
8
+ Logger.info(`pause for ${ms / 1000} sec....`)
9
+ setTimeout(resolve, ms)
10
+ })
11
+
12
+ const prepareTestParticipants = async () => {
13
+ await pause(15_000) // sometimes on CircleCI env we have error: socket hang up
14
+ await onboarding.createHubAccounts()
15
+ await pause()
16
+
17
+ await onboarding.createTestParticipant({ name: PAYER_DFSP })
18
+ await onboarding.createTestParticipant({
19
+ name: PROXY_NAME,
20
+ isProxy: true
21
+ })
22
+
23
+ await onboarding.createOracle()
24
+ Logger.info('prepareTestParticipants is finished')
25
+ }
26
+
27
+ prepareTestParticipants().catch(err => {
28
+ Logger.error(err)
29
+ throw err
30
+ })
@@ -0,0 +1,5 @@
1
+ const path = require('node:path')
2
+
3
+ require('dotenv').config({
4
+ path: path.join(__dirname, '.env')
5
+ })
@@ -0,0 +1,81 @@
1
+ {
2
+ "HUB_PARTICIPANT": {
3
+ "ID": 1,
4
+ "NAME": "Hub"
5
+ },
6
+ "TEST_ALS_HOST":"account-lookup-service",
7
+ "ADMIN_PORT": 4001,
8
+ "API_PORT": 4002,
9
+ "DATABASE": {
10
+ "DIALECT": "mysql",
11
+ "HOST": "mysql-als",
12
+ "PORT": 3306,
13
+ "USER": "account_lookup",
14
+ "PASSWORD": "password",
15
+ "DATABASE": "account_lookup",
16
+ "POOL_MIN_SIZE": 10,
17
+ "POOL_MAX_SIZE": 10,
18
+ "ACQUIRE_TIMEOUT_MILLIS": 30000,
19
+ "CREATE_TIMEOUT_MILLIS": 30000,
20
+ "DESTROY_TIMEOUT_MILLIS": 5000,
21
+ "IDLE_TIMEOUT_MILLIS": 30000,
22
+ "REAP_INTERVAL_MILLIS": 1000,
23
+ "CREATE_RETRY_INTERVAL_MILLIS": 200,
24
+ "DEBUG": false
25
+ },
26
+ "PROTOCOL_VERSIONS": {
27
+ "CONTENT": {
28
+ "DEFAULT": "1.1",
29
+ "VALIDATELIST": [
30
+ "1.1",
31
+ "1.0"
32
+ ]
33
+ },
34
+ "ACCEPT": {
35
+ "DEFAULT": "1",
36
+ "VALIDATELIST": [
37
+ "1",
38
+ "1.0",
39
+ "1.1"
40
+ ]
41
+ }
42
+ },
43
+ "DISPLAY_ROUTES": true,
44
+ "RUN_MIGRATIONS": true,
45
+ "CENTRAL_SHARED_ENDPOINT_CACHE_CONFIG": {
46
+ "expiresIn": 180000,
47
+ "generateTimeout": 30000
48
+ },
49
+ "PROXY_CACHE": {
50
+ "enabled": true,
51
+ "type": "redis-cluster",
52
+ "proxyConfig": {
53
+ "cluster": [
54
+ { "host": "redis-node-0", "port": 6379 }
55
+ ]
56
+ }
57
+ },
58
+ "SWITCH_ENDPOINT": "http://localhost:3001",
59
+ "INSTRUMENTATION": {
60
+ "METRICS": {
61
+ "DISABLED": false,
62
+ "labels": {
63
+ "fspId": "*"
64
+ },
65
+ "config": {
66
+ "timeout": 5000,
67
+ "prefix": "moja_",
68
+ "defaultLabels": {
69
+ "serviceName": "account-lookup-service"
70
+ }
71
+ }
72
+ }
73
+ },
74
+ "ENDPOINT_SECURITY":{
75
+ "JWS": {
76
+ "JWS_SIGN": false,
77
+ "JWS_SIGNING_KEY_PATH": "secrets/jwsSigningKey.key"
78
+ }
79
+ },
80
+ "API_DOC_ENDPOINTS_ENABLED": true
81
+ }