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,238 @@
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
+ - Rajiv Mothilal <rajiv.mothilal@modusbox.com>
22
+ - Juan Correa <juan.correa@modusbox.com>
23
+ - Miguel de Barros <miguel.debarros@modusbox.com>
24
+ --------------
25
+ ******/
26
+
27
+ 'use strict'
28
+
29
+ const Logger = require('@mojaloop/central-services-logger')
30
+ const Util = require('@mojaloop/central-services-shared').Util
31
+ const Enums = require('@mojaloop/central-services-shared').Enum
32
+ const ErrorHandler = require('@mojaloop/central-services-error-handling')
33
+ const JwsSigner = require('@mojaloop/sdk-standard-components').Jws.signer
34
+ const Metrics = require('@mojaloop/central-services-metrics')
35
+ const Config = require('../../lib/config')
36
+ const { hubNameRegex } = require('../../lib/util').hubNameConfig
37
+ const uriRegex = /(?:^.*)(\/(participants|parties|quotes|transfers)(\/.*)*)$/
38
+
39
+ /**
40
+ * @module src/models/participantEndpoint/facade
41
+ */
42
+
43
+ const defineJwsSigner = (config, headers, requestedEndpoint) => {
44
+ let jwsSigner = null
45
+
46
+ if (config.JWS_SIGN && headers[Enums.Http.Headers.FSPIOP.SOURCE] === config.FSPIOP_SOURCE_TO_SIGN) {
47
+ // We need below 2 headers for JWS
48
+ headers[Enums.Http.Headers.FSPIOP.HTTP_METHOD] = headers[Enums.Http.Headers.FSPIOP.HTTP_METHOD] || Enums.Http.RestMethods.PUT
49
+ headers[Enums.Http.Headers.FSPIOP.URI] = headers[Enums.Http.Headers.FSPIOP.URI] || uriRegex.exec(requestedEndpoint)[1]
50
+ Logger.isDebugEnabled && Logger.debug('JWS is enabled, getting JwsSigner')
51
+ jwsSigner = new JwsSigner({
52
+ logger: Logger,
53
+ signingKey: config.JWS_SIGNING_KEY
54
+ })
55
+ }
56
+
57
+ return jwsSigner
58
+ }
59
+
60
+ /**
61
+ * @function sendRequest
62
+ *
63
+ * @description it gets the applicable endpoints and sends through the response to them
64
+ *
65
+ * @param {object} headers - incoming http request headers
66
+ * @param {string} requestedParticipant - the participant the request needs to be sent to
67
+ * @param {string} endpointType - the type of endpoint being requested
68
+ * @param {string} method - the http method
69
+ * @param {object} payload - payload of the request being sent out
70
+ * @param {object} options - the options to be used in the template
71
+ * @param {object} span
72
+ *
73
+ * @returns {object} - Returns http response from request endpoint
74
+ */
75
+ exports.sendRequest = async (headers, requestedParticipant, endpointType, method = undefined, payload = undefined, options = undefined, span = undefined) => {
76
+ // Get endpoint for participant
77
+ let requestedEndpoint
78
+ const histTimerEndGetParticipantEndpoint = Metrics.getHistogram(
79
+ 'egress_getParticipantEndpoint',
80
+ 'Egress: Get Endpoint of participant',
81
+ ['success', 'endpointType', 'participantName']
82
+ ).startTimer()
83
+ try {
84
+ requestedEndpoint = await Util.Endpoints.getEndpoint(Config.SWITCH_ENDPOINT, requestedParticipant, endpointType, options || undefined)
85
+ histTimerEndGetParticipantEndpoint({ success: true, endpointType, participantName: requestedParticipant })
86
+ Logger.isDebugEnabled && Logger.debug(`participant endpoint url: ${requestedEndpoint} for endpoint type ${endpointType}`)
87
+ } catch (err) {
88
+ histTimerEndGetParticipantEndpoint({ success: false, endpointType, participantName: requestedParticipant })
89
+ Logger.isErrorEnabled && Logger.error(`error in getEndpoint: ${err?.stack}`)
90
+ throw ErrorHandler.Factory.reformatFSPIOPError(err)
91
+ }
92
+
93
+ // Send request to participant
94
+ const histTimerEndSendRequestToParticipant = Metrics.getHistogram(
95
+ 'egress_sendRequestToParticipant',
96
+ 'Egress: Send request to participant',
97
+ ['success', 'endpointType', 'participantName']
98
+ ).startTimer()
99
+ try {
100
+ // Injected Configuration for outbound Content-Type & Accept headers.
101
+ const protocolVersions = {
102
+ content: Config.PROTOCOL_VERSIONS.CONTENT.DEFAULT.toString(),
103
+ accept: Config.PROTOCOL_VERSIONS.ACCEPT.DEFAULT.toString()
104
+ }
105
+ const jwsSigner = defineJwsSigner(Config, headers, requestedEndpoint)
106
+
107
+ const resp = await Util.Request.sendRequest({
108
+ url: requestedEndpoint,
109
+ headers,
110
+ source: headers[Enums.Http.Headers.FSPIOP.SOURCE],
111
+ destination: headers[Enums.Http.Headers.FSPIOP.DESTINATION],
112
+ method,
113
+ payload,
114
+ responseType: Enums.Http.ResponseTypes.JSON,
115
+ span,
116
+ jwsSigner,
117
+ protocolVersions,
118
+ hubNameRegex,
119
+ apiType: Config.API_TYPE
120
+ })
121
+ histTimerEndSendRequestToParticipant({ success: true, endpointType, participantName: requestedParticipant })
122
+ return resp
123
+ } catch (err) {
124
+ histTimerEndSendRequestToParticipant({ success: false, endpointType, participantName: requestedParticipant })
125
+ Logger.isErrorEnabled && Logger.error(`error in sendRequest: ${err?.stack}`)
126
+
127
+ throw ErrorHandler.Factory.reformatFSPIOPError(err)
128
+ }
129
+ }
130
+
131
+ /**
132
+ * @function validateParticipant
133
+ *
134
+ * @description sends a request to central-ledger to retrieve participant details and validate that they exist within the switch
135
+ *
136
+ * @param {string} fsp The FSPIOP-Source fsp id
137
+ * @returns the participants info in a successful case and
138
+ */
139
+ exports.validateParticipant = async (fsp) => {
140
+ const histTimerEnd = Metrics.getHistogram(
141
+ 'egress_validateParticipant',
142
+ 'Egress: Validate participant',
143
+ ['success']
144
+ ).startTimer()
145
+ try {
146
+ const resp = await Util.Participants.getParticipant(Config.SWITCH_ENDPOINT, fsp)
147
+ histTimerEnd({ success: true })
148
+ return resp
149
+ } catch (err) {
150
+ histTimerEnd({ success: false })
151
+ Logger.isErrorEnabled && Logger.error(`error in validateParticipant: ${err?.stack}`)
152
+ throw ErrorHandler.Factory.reformatFSPIOPError(err)
153
+ }
154
+ }
155
+
156
+ /**
157
+ * @function sendErrorToParticipant
158
+ *
159
+ * @description it gets the applicable endpoints and sends through an error message
160
+ *
161
+ * @param {string} participantName - the participant the request needs to be sent to
162
+ * @param {string} endpointType - the type of endpoint being requested
163
+ * @param {object} errorInformation - payload of the error information being sent out
164
+ * @param {object} headers - incoming http request headers
165
+ * @param {object} params - uri parameters of the http request
166
+ * @param {object} payload - payload of the request being sent out
167
+ * @param {object} span
168
+ *
169
+ * @returns {object} - Returns http response from request endpoint
170
+ */
171
+ exports.sendErrorToParticipant = async (participantName, endpointType, errorInformation, headers, params = {}, payload = undefined, span = undefined) => {
172
+ // Get endpoint for participant
173
+ let requesterErrorEndpoint
174
+ const histTimerEndGetParticipantEndpoint = Metrics.getHistogram(
175
+ 'egress_getParticipantEndpoint',
176
+ 'Egress: Get Endpoint of participant',
177
+ ['success', 'endpointType', 'participantName']
178
+ ).startTimer()
179
+ try {
180
+ const { requestId } = payload || {}
181
+
182
+ requesterErrorEndpoint = await Util.Endpoints.getEndpoint(Config.SWITCH_ENDPOINT, participantName, endpointType, {
183
+ partyIdType: params.Type || undefined,
184
+ partyIdentifier: params.ID || undefined,
185
+ partySubIdOrType: params.SubId || undefined,
186
+ requestId
187
+ })
188
+ histTimerEndGetParticipantEndpoint({ success: true, endpointType, participantName })
189
+ } catch (err) {
190
+ histTimerEndGetParticipantEndpoint({ success: false, endpointType, participantName })
191
+ Logger.isWarnEnabled && Logger.warn(`error in getEndpoint: ${err?.message}`)
192
+ throw ErrorHandler.Factory.reformatFSPIOPError(err)
193
+ }
194
+
195
+ // Send error to participant
196
+ const histTimerEndSendRequestToParticipant = Metrics.getHistogram(
197
+ 'egress_sendRequestToParticipant',
198
+ 'Egress: Send request to participant',
199
+ ['success', 'endpointType', 'participantName']
200
+ ).startTimer()
201
+ try {
202
+ // Injected Configuration for outbound Content-Type & Accept headers.
203
+ const protocolVersions = {
204
+ content: Config.PROTOCOL_VERSIONS.CONTENT.DEFAULT.toString(),
205
+ accept: Config.PROTOCOL_VERSIONS.ACCEPT.DEFAULT.toString()
206
+ }
207
+
208
+ const clonedHeaders = { ...headers }
209
+
210
+ if (!clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] || clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] === '') {
211
+ clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] = clonedHeaders[Enums.Http.Headers.FSPIOP.SOURCE]
212
+ clonedHeaders[Enums.Http.Headers.FSPIOP.SOURCE] = Config.HUB_NAME
213
+ }
214
+
215
+ Logger.isDebugEnabled && Logger.debug(`participant endpoint url: ${requesterErrorEndpoint} for endpoint type ${endpointType}`)
216
+ const jwsSigner = defineJwsSigner(Config, clonedHeaders, requesterErrorEndpoint)
217
+
218
+ await Util.Request.sendRequest({
219
+ url: requesterErrorEndpoint,
220
+ headers: clonedHeaders,
221
+ source: clonedHeaders[Enums.Http.Headers.FSPIOP.SOURCE],
222
+ destination: clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION],
223
+ method: Enums.Http.RestMethods.PUT,
224
+ payload: errorInformation,
225
+ responseType: Enums.Http.ResponseTypes.JSON,
226
+ hubNameRegex,
227
+ span,
228
+ jwsSigner,
229
+ protocolVersions,
230
+ apiType: Config.API_TYPE
231
+ })
232
+ histTimerEndSendRequestToParticipant({ success: true, endpointType, participantName })
233
+ } catch (err) {
234
+ histTimerEndSendRequestToParticipant({ success: false, endpointType, participantName })
235
+ Logger.isWarnEnabled && Logger.warn(`error in sendErrorToParticipant: ${err?.message}`)
236
+ throw ErrorHandler.Factory.reformatFSPIOPError(err)
237
+ }
238
+ }
@@ -0,0 +1,32 @@
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
+ - Rajiv Mothilal <rajiv.mothilal@modusbox.com>
22
+
23
+ --------------
24
+ ******/
25
+
26
+ 'use strict'
27
+
28
+ const partyIdType = require('./partyIdType')
29
+
30
+ module.exports = {
31
+ getPartyIdTypeByName: partyIdType.getPartyIdTypeByName
32
+ }
@@ -0,0 +1,41 @@
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
+ * Rajiv Mothilal <rajiv.mothilal@modusbox.com>
22
+
23
+ --------------
24
+ ******/
25
+
26
+ 'use strict'
27
+
28
+ const Db = require('../../lib/db')
29
+ const ErrorHandler = require('@mojaloop/central-services-error-handling')
30
+
31
+ const getPartyIdTypeByName = async (name) => {
32
+ try {
33
+ return Db.from('partyIdType').findOne({ name, isActive: true })
34
+ } catch (err) {
35
+ throw ErrorHandler.Factory.reformatFSPIOPError(err)
36
+ }
37
+ }
38
+
39
+ module.exports = {
40
+ getPartyIdTypeByName
41
+ }
package/src/plugins.js ADDED
@@ -0,0 +1,139 @@
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
+
20
+ * Rajiv Mothilal <rajiv.mothilal@modusbox.com>
21
+ * Vijay Kumar Guthi <vijaya.guthi@infitx.com>
22
+
23
+ --------------
24
+ ******/
25
+ 'use strict'
26
+
27
+ const Config = require('./lib/config')
28
+ const Inert = require('@hapi/inert')
29
+ const Vision = require('@hapi/vision')
30
+ const Blipp = require('blipp')
31
+ const ErrorHandling = require('@mojaloop/central-services-error-handling')
32
+ const CentralServices = require('@mojaloop/central-services-shared')
33
+ const RawPayloadToDataUri = require('@mojaloop/central-services-shared').Util.Hapi.HapiRawPayload
34
+ const OpenapiBackendValidator = require('@mojaloop/central-services-shared').Util.Hapi.OpenapiBackendValidator
35
+ const APIDocumentation = require('@mojaloop/central-services-shared').Util.Hapi.APIDocumentation
36
+ const MetricsPlugin = require('./metrics/plugin')
37
+
38
+ const registerPlugins = async (server, openAPIBackend) => {
39
+ await server.register(OpenapiBackendValidator)
40
+
41
+ if (Config.API_DOC_ENDPOINTS_ENABLED) {
42
+ await server.register({
43
+ plugin: APIDocumentation,
44
+ options: {
45
+ document: openAPIBackend.document
46
+ }
47
+ })
48
+ }
49
+
50
+ if (!Config.INSTRUMENTATION_METRICS_DISABLED) {
51
+ await server.register({
52
+ plugin: MetricsPlugin
53
+ })
54
+ }
55
+
56
+ await server.register({
57
+ plugin: {
58
+ name: 'openapi',
59
+ version: '1.0.0',
60
+ multiple: true,
61
+ register: function (server, options) {
62
+ server.expose('openapi', options.openapi)
63
+ }
64
+ },
65
+ options: {
66
+ openapi: openAPIBackend
67
+ }
68
+ })
69
+
70
+ await server.register({
71
+ plugin: require('@hapi/good'),
72
+ options: {
73
+ ops: {
74
+ interval: 10000
75
+ }
76
+ }
77
+ })
78
+
79
+ await server.register({
80
+ plugin: require('@hapi/basic')
81
+ })
82
+
83
+ await server.register({
84
+ plugin: require('@now-ims/hapi-now-auth')
85
+ })
86
+
87
+ await server.register({
88
+ plugin: require('hapi-auth-bearer-token')
89
+ })
90
+
91
+ // Helper to construct FSPIOPHeaderValidation option configuration
92
+ const getOptionsForFSPIOPHeaderValidation = () => {
93
+ // configure supported FSPIOP Content-Type versions
94
+ const supportedProtocolContentVersions = []
95
+ for (const version of Config.PROTOCOL_VERSIONS.CONTENT.VALIDATELIST) {
96
+ supportedProtocolContentVersions.push(version.toString())
97
+ }
98
+
99
+ // configure supported FSPIOP Accept version
100
+ const supportedProtocolAcceptVersions = []
101
+ for (const version of Config.PROTOCOL_VERSIONS.ACCEPT.VALIDATELIST) {
102
+ supportedProtocolAcceptVersions.push(version.toString())
103
+ }
104
+
105
+ // configure FSPIOP resources
106
+ const resources = [
107
+ 'participants',
108
+ 'parties'
109
+ ]
110
+
111
+ // return FSPIOPHeaderValidation plugin options
112
+ return {
113
+ resources,
114
+ supportedProtocolContentVersions,
115
+ supportedProtocolAcceptVersions,
116
+ apiType: Config.API_TYPE
117
+ }
118
+ }
119
+
120
+ await server.register([
121
+ Inert,
122
+ Vision,
123
+ ErrorHandling,
124
+ RawPayloadToDataUri,
125
+ CentralServices.Util.Hapi.HapiEventPlugin,
126
+ {
127
+ plugin: CentralServices.Util.Hapi.FSPIOPHeaderValidation.plugin,
128
+ options: getOptionsForFSPIOPHeaderValidation()
129
+ }
130
+ ])
131
+
132
+ if (Config.DISPLAY_ROUTES === true) {
133
+ await server.register([Blipp])
134
+ }
135
+ }
136
+
137
+ module.exports = {
138
+ registerPlugins
139
+ }
package/src/server.js ADDED
@@ -0,0 +1,199 @@
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
+
20
+ * Rajiv Mothilal <rajiv.mothilal@modusbox.com>
21
+
22
+ --------------
23
+ ******/
24
+ 'use strict'
25
+
26
+ const { randomUUID } = require('node:crypto')
27
+ const Hapi = require('@hapi/hapi')
28
+ const Boom = require('@hapi/boom')
29
+
30
+ const ErrorHandler = require('@mojaloop/central-services-error-handling')
31
+ const Logger = require('@mojaloop/central-services-logger')
32
+ const Metrics = require('@mojaloop/central-services-metrics')
33
+ const { Endpoints, Participants, proxies, OpenapiBackend } = require('@mojaloop/central-services-shared').Util
34
+ const { createProxyCache } = require('@mojaloop/inter-scheme-proxy-cache-lib')
35
+
36
+ const { name, version } = require('../package.json')
37
+ const Db = require('./lib/db')
38
+ const Util = require('./lib/util')
39
+ const Plugins = require('./plugins')
40
+ const RequestLogger = require('./lib/requestLogger')
41
+ const Migrator = require('./lib/migrator')
42
+ const APIHandlers = require('./api')
43
+ const Routes = require('./api/routes')
44
+ const Cache = require('./lib/cache')
45
+ const OracleEndpointCache = require('./models/oracle/oracleEndpointCached')
46
+ const Handlers = require('./handlers/register')
47
+
48
+ const connectDatabase = async (dbConfig) => {
49
+ return Db.connect(dbConfig)
50
+ }
51
+
52
+ const migrate = async () => {
53
+ return Migrator.migrate()
54
+ }
55
+
56
+ const createConnectedProxyCache = async (proxyCacheConfig) => {
57
+ const proxyCache = createProxyCache(
58
+ proxyCacheConfig.type,
59
+ proxyCacheConfig.proxyConfig
60
+ )
61
+ await proxyCache.connect()
62
+ return proxyCache
63
+ }
64
+
65
+ /**
66
+ * @function createServer
67
+ *
68
+ * @description Create HTTP Server
69
+ *
70
+ * @param {number} port Port to register the Server against
71
+ * @param {object} api to check if admin or api server
72
+ * @param {array} routes array of API routes
73
+ * @returns {Promise<Server>} Returns the Server object
74
+ */
75
+ const createServer = async (port, api, routes, isAdmin, proxyCacheConfig, proxyMap) => {
76
+ const server = await new Hapi.Server({
77
+ port,
78
+ routes: {
79
+ validate: {
80
+ options: ErrorHandler.validateRoutes(),
81
+ failAction: async (request, h, err) => {
82
+ throw Boom.boomify(err)
83
+ }
84
+ },
85
+ payload: {
86
+ parse: true,
87
+ output: 'stream'
88
+ }
89
+ }
90
+ })
91
+ server.app.isAdmin = isAdmin
92
+
93
+ server.app.cache = Cache.registerCacheClient({
94
+ id: 'serverGeneralCache',
95
+ preloadCache: async () => Promise.resolve()
96
+ })
97
+
98
+ if (!isAdmin && proxyCacheConfig.enabled) {
99
+ server.app.proxyCache = await createConnectedProxyCache(proxyCacheConfig)
100
+ if (proxyMap) {
101
+ for (const [dfspId, proxyId] of Object.entries(proxyMap)) {
102
+ await server.app.proxyCache.addDfspIdToProxyMapping(dfspId, proxyId)
103
+ }
104
+ }
105
+ }
106
+
107
+ await server.ext([
108
+ {
109
+ type: 'onPreHandler',
110
+ method: (request, h) => {
111
+ request.headers.traceid = request.headers.traceid || randomUUID()
112
+ RequestLogger.logRequest(request)
113
+ return h.continue
114
+ }
115
+ },
116
+ {
117
+ type: 'onPreResponse',
118
+ method: (request, h) => {
119
+ RequestLogger.logResponse(request)
120
+ return h.continue
121
+ }
122
+ }
123
+ ])
124
+ await Plugins.registerPlugins(server, api, isAdmin)
125
+
126
+ server.route(routes)
127
+ // TODO: follow instructions https://github.com/anttiviljami/openapi-backend/blob/master/DOCS.md#postresponsehandler-handler
128
+ await server.start()
129
+
130
+ Logger.isInfoEnabled && Logger.info(`${name}${isAdmin ? '-admin' : ''}@${version} is running on port ${server.info.port}...`)
131
+ return server
132
+ }
133
+
134
+ const initializeInstrumentation = (metricsConfig) => {
135
+ Metrics.setup(metricsConfig)
136
+ }
137
+
138
+ const initializeApi = async (appConfig) => {
139
+ const {
140
+ INSTRUMENTATION_METRICS_DISABLED,
141
+ INSTRUMENTATION_METRICS_CONFIG,
142
+ CENTRAL_SHARED_ENDPOINT_CACHE_CONFIG,
143
+ CENTRAL_SHARED_PARTICIPANT_CACHE_CONFIG,
144
+ DATABASE,
145
+ API_PORT,
146
+ PROXY_CACHE_CONFIG,
147
+ proxyMap
148
+ } = appConfig
149
+
150
+ if (!INSTRUMENTATION_METRICS_DISABLED) {
151
+ initializeInstrumentation(INSTRUMENTATION_METRICS_CONFIG)
152
+ }
153
+ await connectDatabase(DATABASE)
154
+ const OpenAPISpecPath = Util.pathForInterface({ isAdmin: false, isMockInterface: false })
155
+ const api = await OpenapiBackend.initialise(OpenAPISpecPath, APIHandlers.ApiHandlers)
156
+
157
+ await Promise.all([
158
+ Endpoints.initializeCache(CENTRAL_SHARED_ENDPOINT_CACHE_CONFIG, Util.hubNameConfig),
159
+ Participants.initializeCache(CENTRAL_SHARED_PARTICIPANT_CACHE_CONFIG, Util.hubNameConfig),
160
+ proxies.initializeCache(CENTRAL_SHARED_PARTICIPANT_CACHE_CONFIG, Util.hubNameConfig),
161
+ OracleEndpointCache.initialize(),
162
+ Cache.initCache()
163
+ ])
164
+
165
+ return createServer(API_PORT, api, Routes.APIRoutes(api), false, PROXY_CACHE_CONFIG, proxyMap)
166
+ }
167
+
168
+ const initializeAdmin = async (appConfig) => {
169
+ const {
170
+ INSTRUMENTATION_METRICS_DISABLED,
171
+ INSTRUMENTATION_METRICS_CONFIG,
172
+ DATABASE,
173
+ RUN_MIGRATIONS,
174
+ ADMIN_PORT,
175
+ PROXY_CACHE_CONFIG
176
+ } = appConfig
177
+
178
+ if (!INSTRUMENTATION_METRICS_DISABLED) {
179
+ initializeInstrumentation(INSTRUMENTATION_METRICS_CONFIG)
180
+ }
181
+ await connectDatabase(DATABASE)
182
+ RUN_MIGRATIONS && await migrate()
183
+ const OpenAPISpecPath = Util.pathForInterface({ isAdmin: true, isMockInterface: false })
184
+ const api = await OpenapiBackend.initialise(OpenAPISpecPath, APIHandlers.AdminHandlers)
185
+
186
+ return createServer(ADMIN_PORT, api, Routes.AdminRoutes(api), true, PROXY_CACHE_CONFIG)
187
+ }
188
+
189
+ const initializeHandlers = async (handlers, appConfig, logger) => {
190
+ const proxyCache = await createConnectedProxyCache(appConfig.PROXY_CACHE_CONFIG)
191
+ const options = { proxyCache, batchSize: appConfig.HANDLERS_TIMEOUT_BATCH_SIZE, logger }
192
+ await Handlers.registerHandlers(handlers, options)
193
+ }
194
+
195
+ module.exports = {
196
+ initializeApi,
197
+ initializeAdmin,
198
+ initializeHandlers
199
+ }