account-lookup-service 17.7.1 → 17.8.0-snapshot.100

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 (39) hide show
  1. package/CHANGELOG.md +0 -7
  2. package/docker/mock-proxy/src/server.ts +13 -4
  3. package/package.json +12 -9
  4. package/src/constants.js +35 -2
  5. package/src/domain/parties/deps.js +11 -4
  6. package/src/domain/parties/getPartiesByTypeAndID.js +9 -13
  7. package/src/domain/parties/partiesUtils.js +4 -34
  8. package/src/domain/parties/putParties.js +26 -71
  9. package/src/domain/parties/services/BasePartiesService.js +146 -17
  10. package/src/domain/parties/services/GetPartiesService.js +213 -166
  11. package/src/domain/parties/services/PutPartiesErrorService.js +52 -28
  12. package/src/domain/parties/services/PutPartiesService.js +51 -33
  13. package/src/domain/parties/services/TimeoutPartiesService.js +84 -0
  14. package/src/domain/parties/services/index.js +3 -1
  15. package/src/domain/timeout/createSpan.js +55 -0
  16. package/src/domain/timeout/index.js +27 -36
  17. package/src/handlers/TimeoutHandler.js +2 -2
  18. package/src/index.js +3 -0
  19. package/src/lib/util.js +11 -3
  20. package/src/server.js +16 -5
  21. package/test/fixtures/index.js +53 -3
  22. package/test/integration/api/parties.test.js +1 -0
  23. package/test/integration/domain/timeout/index.test.js +83 -28
  24. package/test/unit/domain/parties/parties.test.js +27 -20
  25. package/test/unit/domain/parties/partiesUtils.test.js +51 -0
  26. package/test/unit/domain/parties/services/BasePartiesService.test.js +72 -0
  27. package/test/unit/domain/parties/services/GetPartiesService.test.js +340 -0
  28. package/test/unit/domain/parties/services/PutPartiesErrorService.test.js +50 -0
  29. package/test/unit/domain/parties/services/TimeoutPartiesService.test.js +72 -0
  30. package/test/unit/domain/parties/services/deps.js +51 -0
  31. package/test/unit/domain/timeout/index.test.js +17 -12
  32. package/test/util/apiClients/AlsApiClient.js +6 -4
  33. package/test/util/apiClients/BasicApiClient.js +33 -6
  34. package/test/util/apiClients/ProxyApiClient.js +46 -1
  35. package/test/util/index.js +5 -6
  36. package/test/util/mockDeps.js +72 -0
  37. package/src/domain/timeout/dto.js +0 -54
  38. package/test/unit/domain/parties/utils.test.js +0 -60
  39. package/test/unit/domain/timeout/dto.test.js +0 -24
package/CHANGELOG.md CHANGED
@@ -2,13 +2,6 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
- ### [17.7.1](https://github.com/mojaloop/account-lookup-service/compare/v17.7.0...v17.7.1) (2025-03-27)
6
-
7
-
8
- ### Chore
9
-
10
- * **csi-1248:** update transform-lib, others ([#543](https://github.com/mojaloop/account-lookup-service/issues/543)) ([7a0a5c4](https://github.com/mojaloop/account-lookup-service/commit/7a0a5c402e8f8f28661540ea5982b1ac9a97a8dd))
11
-
12
5
  ## [17.7.0](https://github.com/mojaloop/account-lookup-service/compare/v17.6.0...v17.7.0) (2025-03-26)
13
6
 
14
7
 
@@ -38,24 +38,26 @@ app.get('/health', (req: Request, res: Response) => {
38
38
  app.get('/parties/:type/:id', (req: Request, res: Response) => {
39
39
  const { type, id } = req.params;
40
40
  const headers = hubCbHeaders(req.headers);
41
- console.log('parties request details:', { type, id, headers, CL_HOST, CL_PORT });
41
+ const statusCode = detectStatusCode(req);
42
+ console.log('parties request details:', { type, id, headers, CL_HOST, CL_PORT, statusCode });
42
43
 
43
44
  // todo: reply to CL with party info
44
45
 
45
46
  res
46
47
  .set(headers)
47
- .status(202)
48
+ .status(statusCode)
48
49
  .json({ success: true });
49
50
  });
50
51
 
51
52
  app.put('/parties/:type/:id/error', (req: Request, res: Response) => {
52
53
  const { type, id } = req.params;
53
54
  const headers = dfspCbHeaders(req.headers);
54
- console.log('parties put error request details:', { type, id, headers, CL_HOST, CL_PORT });
55
+ const statusCode = detectStatusCode(req);
56
+ console.log('parties put error request details:', { type, id, headers, CL_HOST, CL_PORT, statusCode });
55
57
 
56
58
  res
57
59
  .set(headers)
58
- .status(200)
60
+ .status(statusCode)
59
61
  .json({ success: true });
60
62
  });
61
63
 
@@ -92,3 +94,10 @@ const httpsServer = http.createServer(app);
92
94
  httpsServer.listen(PROXY_PORT, () => {
93
95
  console.log(`Mock proxyAdapter "${PROXY_NAME}" is running on port ${PROXY_PORT}...`);
94
96
  });
97
+
98
+ const X_RESPONSE_STATUS_HEADER = 'x-response-status';
99
+
100
+ const detectStatusCode = (req: Request) =>
101
+ typeof req.headers[X_RESPONSE_STATUS_HEADER] === 'string'
102
+ ? parseInt(req.headers[X_RESPONSE_STATUS_HEADER], 10)
103
+ : (req.method === 'GET' ? 202 : 200);
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "account-lookup-service",
3
3
  "description": "Account Lookup Service is used to validate Party and Participant lookups.",
4
- "version": "17.7.1",
4
+ "version": "17.8.0-snapshot.100",
5
5
  "license": "Apache-2.0",
6
6
  "author": "ModusBox",
7
7
  "contributors": [
8
+ "Eugen Klymniuk <eugen.klymniuk@infitx.com>",
8
9
  "Rajiv Mothilal <rajiv.mothilal@modusbox.com>",
9
10
  "Matt Kingston <matt.kingston@modusbox.com>",
10
11
  "Lewis Daly <lewisd@crosslaketech.com>",
@@ -44,10 +45,8 @@
44
45
  "start:api": "node src/index.js server --api",
45
46
  "start:admin": "node src/index.js server --admin",
46
47
  "start:handlers": "node src/handlers/index.js handlers --timeout",
47
- "standard": "standard",
48
- "standard:fix": "standard --fix",
49
- "lint": "npm run standard",
50
- "lint:fix": "npm run standard:fix",
48
+ "lint": "standard",
49
+ "lint:fix": "standard --fix",
51
50
  "dev": "nodemon src/index.js server",
52
51
  "test": "npm run test:unit",
53
52
  "test:unit": "NODE_OPTIONS='--experimental-vm-modules --max-old-space-size=8192' jest --runInBand",
@@ -93,21 +92,21 @@
93
92
  "@hapi/vision": "7.0.3",
94
93
  "@mojaloop/central-services-error-handling": "13.0.7",
95
94
  "@mojaloop/central-services-health": "15.0.4",
96
- "@mojaloop/central-services-logger": "11.8.0",
95
+ "@mojaloop/central-services-logger": "11.8.1",
97
96
  "@mojaloop/central-services-metrics": "12.5.0",
98
- "@mojaloop/central-services-shared": "18.23.1",
97
+ "@mojaloop/central-services-shared": "18.23.2",
99
98
  "@mojaloop/central-services-stream": "11.5.2",
100
99
  "@mojaloop/database-lib": "11.1.4",
101
100
  "@mojaloop/event-sdk": "14.4.0",
102
101
  "@mojaloop/inter-scheme-proxy-cache-lib": "2.4.0",
103
102
  "@mojaloop/ml-schema-transformer-lib": "2.7.0",
104
- "@mojaloop/sdk-standard-components": "19.11.2",
103
+ "@mojaloop/sdk-standard-components": "19.11.3-snapshot.0",
105
104
  "@now-ims/hapi-now-auth": "2.1.0",
106
105
  "ajv": "8.17.1",
107
106
  "ajv-keywords": "5.1.0",
108
107
  "blipp": "4.0.2",
109
108
  "commander": "13.1.0",
110
- "cron": "4.1.1",
109
+ "cron": "4.1.3",
111
110
  "fast-safe-stringify": "^2.1.1",
112
111
  "hapi-auth-bearer-token": "8.0.0",
113
112
  "joi": "17.13.3",
@@ -190,5 +189,9 @@
190
189
  "scripts": {
191
190
  "postchangelog": "replace '\\[mojaloop/#(\\d+)\\]\\(https://github.com/mojaloop/(.*)/issues/(\\d+)\\)' '[mojaloop/#$1](https://github.com/mojaloop/project/issues/$1)' CHANGELOG.md"
192
191
  }
192
+ },
193
+ "imports": {
194
+ "#src/*": "./src/*.js",
195
+ "#test/*": "./test/*.js"
193
196
  }
194
197
  }
package/src/constants.js CHANGED
@@ -1,9 +1,41 @@
1
+ /*****
2
+ License
3
+ --------------
4
+ Copyright © 2020-2025 Mojaloop Foundation
5
+ The Mojaloop files are made available by the Mojaloop 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
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ 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.
10
+
11
+ Contributors
12
+ --------------
13
+ This is the official list of the Mojaloop project contributors for this file.
14
+ Names of the original copyright holders (individuals or organizations)
15
+ should be listed with a '*' in the first column. People who have
16
+ contributed from an organization can be listed under the organization
17
+ that actually holds the copyright for their contributions (see the
18
+ Mojaloop Foundation for an example). Those individuals should have
19
+ their names indented and be marked with a '-'. Email address can be added
20
+ optionally within square brackets <email>.
21
+
22
+ * Mojaloop Foundation
23
+ * Eugen Klymniuk <eugen.klymniuk@infitx.com>
24
+
25
+ --------------
26
+ ******/
27
+
28
+ const { API_TYPES } = require('@mojaloop/central-services-shared').Util.Hapi
29
+
1
30
  const ERROR_MESSAGES = Object.freeze({
31
+ emptyFilteredPartyList: 'Empty oracle partyList, filtered based on callbackEndpointType',
32
+ failedToCacheSendToProxiesList: 'Failed to cache sendToProxiesList',
33
+ noDiscoveryRequestsForwarded: 'No discovery requests forwarded to participants',
2
34
  sourceFspNotFound: 'Requester FSP not found',
3
35
  partyDestinationFspNotFound: 'Destination FSP not found',
4
36
  partyProxyNotFound: 'Proxy not found',
5
- proxyConnectionError: 'Proxy connection error',
6
- failedToCacheSendToProxiesList: 'Failed to cache sendToProxiesList'
37
+ proxyConnectionError: 'Proxy connection error - no successful requests sent to proxies',
38
+ noSuccessfulProxyDiscoveryResponses: 'No successful proxy discovery responses'
7
39
  })
8
40
 
9
41
  const HANDLER_TYPES = Object.freeze({
@@ -11,6 +43,7 @@ const HANDLER_TYPES = Object.freeze({
11
43
  })
12
44
 
13
45
  module.exports = {
46
+ API_TYPES,
14
47
  ERROR_MESSAGES,
15
48
  HANDLER_TYPES
16
49
  }
@@ -25,18 +25,25 @@
25
25
  --------------
26
26
  ******/
27
27
 
28
- const { proxies } = require('@mojaloop/central-services-shared').Util
28
+ const { Util } = require('@mojaloop/central-services-shared')
29
+ const { logger } = require('../../lib')
30
+ const config = require('../../lib/config')
29
31
  const oracle = require('../../models/oracle/facade')
30
32
  const participant = require('../../models/participantEndpoint/facade')
31
- const config = require('../../lib/config')
32
33
  const partiesUtils = require('./partiesUtils')
33
34
 
34
- const createDeps = ({ cache, proxyCache, childSpan, log, stepState }) => Object.freeze({
35
+ /** @returns {PartiesDeps} */
36
+ const createDeps = ({
37
+ cache,
38
+ proxyCache,
39
+ proxies = Util.proxies,
40
+ childSpan = null,
41
+ log = logger
42
+ }) => Object.freeze({
35
43
  cache,
36
44
  proxyCache,
37
45
  childSpan,
38
46
  log,
39
- stepState,
40
47
  config,
41
48
  oracle,
42
49
  participant,
@@ -50,26 +50,22 @@ const getPartiesByTypeAndID = async (headers, params, method, query, span, cache
50
50
  ['success']
51
51
  ).startTimer()
52
52
  const childSpan = span ? span.getChild(component) : undefined
53
- const log = logger.child({ component, params })
54
- const stepState = libUtil.initStepState()
55
-
56
- const deps = createDeps({ cache, proxyCache, childSpan, log, stepState })
57
- const service = new GetPartiesService(deps)
58
- const results = {}
53
+ const deps = createDeps({ cache, proxyCache, childSpan })
54
+ const service = new GetPartiesService(deps, { headers, params, query })
55
+ let fspiopError
59
56
 
60
57
  try {
61
- await service.handleRequest({ headers, params, query, results })
62
- log.info('getPartiesByTypeAndID is done')
58
+ await service.handleRequest()
59
+ logger.info('getPartiesByTypeAndID is done')
63
60
  histTimerEnd({ success: true })
64
61
  } catch (error) {
65
- const { requester } = results
66
- results.fspiopError = await service.handleError({ error, requester, headers, params })
62
+ fspiopError = await service.handleError(error)
67
63
  histTimerEnd({ success: false })
68
- if (results.fspiopError) {
69
- libUtil.countFspiopError(results.fspiopError, { operation: component, step: stepState.step })
64
+ if (fspiopError) {
65
+ libUtil.countFspiopError(fspiopError, { operation: component, step: service.currenStep })
70
66
  }
71
67
  } finally {
72
- await libUtil.finishSpanWithError(childSpan, results.fspiopError)
68
+ await libUtil.finishSpanWithError(childSpan, fspiopError)
73
69
  }
74
70
  }
75
71
 
@@ -25,13 +25,9 @@
25
25
  --------------
26
26
  ******/
27
27
 
28
- const { Enum, Util: { Hapi } } = require('@mojaloop/central-services-shared')
29
- const { MojaloopApiErrorCodes } = require('@mojaloop/sdk-standard-components').Errors
30
- // todo: check why do we need sdk-standard-components deps here !!!
31
- const ErrorHandler = require('@mojaloop/central-services-error-handling')
32
-
33
- const participant = require('../../models/participantEndpoint/facade')
28
+ const { Enum } = require('@mojaloop/central-services-shared')
34
29
  const { TransformFacades } = require('../../lib')
30
+ const { API_TYPES } = require('../../constants')
35
31
 
36
32
  const { FspEndpointTypes } = Enum.EndPoints
37
33
  const { Headers } = Enum.Http
@@ -50,7 +46,7 @@ const errorPartyCbType = (partySubId) => partySubId
50
46
 
51
47
  const makePutPartiesErrorPayload = async (config, fspiopError, headers, params) => {
52
48
  const body = fspiopError.toApiErrorObject(config.ERROR_HANDLING)
53
- return config.API_TYPE === Hapi.API_TYPES.iso20022
49
+ return config.API_TYPE === API_TYPES.iso20022
54
50
  ? (await TransformFacades.FSPIOP.parties.putError({ body, headers, params })).body
55
51
  : body
56
52
  }
@@ -81,38 +77,12 @@ const swapSourceDestinationHeaders = (headers) => {
81
77
  }
82
78
  }
83
79
 
84
- // todo: check if we need this function
85
- const createErrorHandlerOnSendingCallback = (config, logger) => async (err, headers, params, requester) => {
86
- try {
87
- logger.error('error in sending parties callback: ', err)
88
- const sendTo = requester || headers[Headers.FSPIOP.SOURCE]
89
- const errorCallbackEndpointType = errorPartyCbType(params.SubId)
90
- const fspiopError = ErrorHandler.Factory.reformatFSPIOPError(err)
91
- const errInfo = await makePutPartiesErrorPayload(config, fspiopError, headers, params)
92
-
93
- await participant.sendErrorToParticipant(sendTo, errorCallbackEndpointType, errInfo, headers, params)
94
-
95
- logger.info('handleErrorOnSendingCallback in done', { sendTo, params, errInfo })
96
- return fspiopError
97
- } catch (exc) {
98
- // We can't do anything else here- we _must_ handle all errors _within_ this function because
99
- // we've already sent a sync response- we cannot throw.
100
- logger.error('failed to handleErrorOnSendingCallback. No further processing! ', exc)
101
- }
102
- }
103
-
104
- function isNotValidPayeeCase (payload) {
105
- return payload?.errorInformation?.errorCode === MojaloopApiErrorCodes.PAYEE_IDENTIFIER_NOT_VALID.code
106
- }
107
-
108
80
  module.exports = {
109
81
  getPartyCbType,
110
82
  putPartyCbType,
111
83
  errorPartyCbType,
112
84
  makePutPartiesErrorPayload,
113
- createErrorHandlerOnSendingCallback,
114
85
  alsRequestDto,
115
86
  partiesRequestOptionsDto,
116
- swapSourceDestinationHeaders,
117
- isNotValidPayeeCase
87
+ swapSourceDestinationHeaders
118
88
  }
@@ -29,7 +29,6 @@
29
29
 
30
30
  'use strict'
31
31
 
32
- const { Headers } = require('@mojaloop/central-services-shared').Enum.Http
33
32
  const Metrics = require('@mojaloop/central-services-metrics')
34
33
  const libUtil = require('../../lib/util')
35
34
  const { logger } = require('../../lib')
@@ -37,9 +36,7 @@ const { createDeps } = require('./deps')
37
36
  const services = require('./services')
38
37
 
39
38
  /**
40
- * @function putPartiesByTypeAndID
41
- *
42
- * @description This sends a callback to inform participant of successful lookup
39
+ * Sends a callback to inform participant of successful lookup
43
40
  *
44
41
  * @param {object} headers - incoming http request headers
45
42
  * @param {object} params - uri parameters of the http request
@@ -58,45 +55,25 @@ const putPartiesByTypeAndID = async (headers, params, method, payload, dataUri,
58
55
  ['success']
59
56
  ).startTimer()
60
57
  // const childSpan = span ? span.getChild(component) : undefined
61
- const log = logger.child({ component, params })
62
- const stepState = libUtil.initStepState()
63
-
64
- const deps = createDeps({ cache, proxyCache, log, stepState })
65
- const service = new services.PutPartiesService(deps)
66
- const results = {}
67
-
68
- const source = headers[Headers.FSPIOP.SOURCE]
69
- const destination = headers[Headers.FSPIOP.DESTINATION]
70
- const proxy = headers[Headers.FSPIOP.PROXY]
71
- log.info('putPartiesByTypeAndID start', { source, destination, proxy })
58
+ const deps = createDeps({ cache, proxyCache })
59
+ const service = new services.PutPartiesService(deps, { headers, params, payload, dataUri })
60
+ let fspiopError
72
61
 
73
62
  try {
74
- await service.validateSourceParticipant({ source, proxy })
75
-
76
- if (proxy) {
77
- await service.checkProxySuccessResponse({ destination, source, headers, params })
78
- }
79
-
80
- const sendTo = await service.identifyDestinationForSuccessCallback(destination)
81
- results.requester = sendTo
82
- await service.sendSuccessCallback({ sendTo, headers, params, dataUri })
83
-
84
- log.info('putPartiesByTypeAndID callback was sent', { sendTo })
63
+ await service.handleRequest()
64
+ logger.info('putPartiesByTypeAndID is done')
85
65
  histTimerEnd({ success: true })
86
66
  } catch (error) {
87
- const { requester } = results
88
- results.fspiopError = await service.handleError({ error, requester, headers, params })
89
- if (results.fspiopError) {
90
- libUtil.countFspiopError(results.fspiopError, { operation: component, step: stepState.step })
67
+ fspiopError = await service.handleError(error)
68
+ if (fspiopError) {
69
+ libUtil.countFspiopError(fspiopError, { operation: component, step: service.currenStep })
91
70
  }
92
71
  histTimerEnd({ success: false })
93
72
  }
94
73
  }
95
74
 
96
75
  /**
97
- * @function putPartiesErrorByTypeAndID
98
- *
99
- * @description This populates the cache of endpoints
76
+ * Sends error callback to inform participant of failed lookup
100
77
  *
101
78
  * @param {object} headers - incoming http request headers
102
79
  * @param {object} params - uri parameters of the http request
@@ -114,53 +91,31 @@ const putPartiesErrorByTypeAndID = async (headers, params, payload, dataUri, spa
114
91
  ['success']
115
92
  ).startTimer()
116
93
  const childSpan = span ? span.getChild(component) : undefined
117
- const log = logger.child({ component, params })
118
- const stepState = libUtil.initStepState()
119
-
120
- const deps = createDeps({ cache, proxyCache, childSpan, log, stepState })
121
- const service = new services.PutPartiesErrorService(deps)
122
- const results = {}
123
-
124
- const destination = headers[Headers.FSPIOP.DESTINATION]
125
- const proxy = headers[Headers.FSPIOP.PROXY]
126
- const proxyEnabled = !!(deps.config.PROXY_CACHE_CONFIG.enabled && proxyCache)
127
- log.info('putPartiesErrorByTypeAndID start', { destination, proxy, proxyEnabled })
94
+ const deps = createDeps({ cache, proxyCache, childSpan })
95
+ const inputs = { headers, params, payload, dataUri }
96
+ const service = new services.PutPartiesErrorService(deps, inputs)
97
+ let fspiopError
128
98
 
129
99
  try {
130
- if (proxyEnabled && proxy) {
131
- const notValid = await service.checkPayee({ headers, params, payload, proxy })
132
- if (notValid) {
133
- const getPartiesService = new services.GetPartiesService(deps)
134
- // todo: think, if we need to remove destination header before starting new discovery
135
- await getPartiesService.handleRequest({ headers, params, results })
136
- log.info('putPartiesErrorByTypeAndID triggered new discovery flow')
137
- histTimerEnd({ success: true })
138
- return
139
- }
140
-
141
- const isLast = await service.checkLastProxyCallback({ destination, proxy, params })
142
- if (!isLast) {
143
- log.info('putPartiesErrorByTypeAndID proxy callback was processed', { proxy })
144
- histTimerEnd({ success: true })
145
- return
146
- }
100
+ const needDiscovery = await service.handleRequest()
101
+ if (needDiscovery) {
102
+ const getPartiesService = new services.GetPartiesService(deps, inputs)
103
+ await getPartiesService.triggerInterSchemeDiscoveryFlow(
104
+ services.GetPartiesService.headersWithoutDestination(headers)
105
+ )
106
+ // think, if we need to start the whole processing with getPartiesService.handleRequest() ?
147
107
  }
148
108
 
149
- const sendTo = await service.identifyDestinationForErrorCallback(destination)
150
- results.requester = sendTo
151
- await service.sendErrorCallbackToParticipant({ sendTo, headers, params, dataUri })
152
-
153
- log.info('putPartiesErrorByTypeAndID callback was sent', { sendTo })
109
+ logger.info('putPartiesErrorByTypeAndID is done')
154
110
  histTimerEnd({ success: true })
155
111
  } catch (error) {
156
- const { requester } = results
157
- results.fspiopError = await service.handleError({ error, requester, headers, params })
158
- if (results.fspiopError) {
159
- libUtil.countFspiopError(results.fspiopError, { operation: component, step: stepState.step })
112
+ fspiopError = await service.handleError(error)
113
+ if (fspiopError) {
114
+ libUtil.countFspiopError(fspiopError, { operation: component, step: service.currenStep })
160
115
  }
161
116
  histTimerEnd({ success: false })
162
117
  } finally {
163
- await libUtil.finishSpanWithError(childSpan, results.fspiopError)
118
+ await libUtil.finishSpanWithError(childSpan, fspiopError)
164
119
  }
165
120
  }
166
121
 
@@ -26,33 +26,94 @@
26
26
  ******/
27
27
 
28
28
  const ErrorHandler = require('@mojaloop/central-services-error-handling')
29
- const { decodePayload } = require('@mojaloop/central-services-shared').Util.StreamingProtocol
30
29
  const { Enum } = require('@mojaloop/central-services-shared')
30
+ const { decodePayload } = require('@mojaloop/central-services-shared').Util.StreamingProtocol
31
+ const { initStepState } = require('../../../lib/util')
32
+ const { createCallbackHeaders } = require('../../../lib/headers')
31
33
 
32
34
  const { FspEndpointTypes, FspEndpointTemplates } = Enum.EndPoints
33
35
  const { Headers, RestMethods } = Enum.Http
34
36
 
37
+ /**
38
+ * @typedef {Object} PartiesDeps
39
+ * @property {Object} cache
40
+ * @property {Object} proxyCache
41
+ * @property {Object} log
42
+ * @property {Object} config
43
+ * @property {Object} oracle
44
+ * @property {Object} participant
45
+ * @property {Proxies} proxies
46
+ * @property {Object} partiesUtils
47
+ * @property {Object} [childSpan]
48
+ */
49
+
50
+ /**
51
+ * Input parameters from party lookup request
52
+ *
53
+ * @typedef {Object} PartiesInputs
54
+ * @property {Object} headers - incoming http request headers.
55
+ * @property {Object} params - uri parameters of the http request.
56
+ * @property {Object} [payload] - payload of the request being sent out.
57
+ * @property {Object} [query] - uri query parameters of the http request
58
+ * @property {string} [dataUri] - encoded payload of the request being sent out.
59
+ */
60
+
61
+ /**
62
+ * Any calculated values we get during request processing
63
+ *
64
+ * @typedef {Object} PartiesModelState
65
+ * @property {string} destination - The destination DFSP ID from headers
66
+ * @property {string} source - The source DFSP ID from headers
67
+ * @property {string} [proxy] - The proxy DFSP ID from headers, if present
68
+ * @property {string} requester - The entity initiating the request (either a DFSP in scheme or a proxy)
69
+ * @property {boolean} proxyEnabled - Indicates whether proxy functionality is enabled in the current configuration
70
+ * @property {StepState} stepState - Processing steps state
71
+ */
72
+
35
73
  class BasePartiesService {
36
- constructor (deps) {
37
- this.deps = deps
38
- this.log = this.deps.log.child({ component: this.constructor.name })
39
- this.proxyEnabled = !!(deps.config.PROXY_CACHE_CONFIG?.enabled && deps.proxyCache)
74
+ #deps // see PartiesDeps
75
+ #inputs // see PartiesInputs
76
+ #state // see PartiesModelState
77
+
78
+ /**
79
+ * @param {PartiesDeps} deps - The dependencies required by the class instance.
80
+ * @param {PartiesInputs} inputs - The input parameters from incoming http request.
81
+ * @return {void}
82
+ */
83
+ constructor (deps, inputs) {
84
+ this.#deps = Object.freeze(deps)
85
+ this.#inputs = Object.freeze(inputs)
86
+ this.#state = this.#initiateState()
87
+ this.log = this.deps.log.child({
88
+ component: this.constructor.name,
89
+ params: this.inputs.params
90
+ })
40
91
  }
41
92
 
42
- // async handleRequest () {
43
- // throw new Error('handleRequest must be implemented by subclass')
44
- // }
93
+ /** @returns {PartiesDeps} */
94
+ get deps () { return this.#deps }
95
+
96
+ /** @returns {PartiesInputs} */
97
+ get inputs () { return this.#inputs }
45
98
 
46
- async handleError ({ error, requester, headers, params }) {
99
+ /** @returns {PartiesModelState} */
100
+ get state () { return this.#state }
101
+
102
+ async handleError (error) {
103
+ const { params } = this.inputs
47
104
  const log = this.log.child({ method: 'handleError' })
48
105
  try {
49
106
  log.error('error in processing parties request: ', error)
50
- const sendTo = requester || headers[Headers.FSPIOP.SOURCE]
51
107
  const fspiopError = ErrorHandler.Factory.reformatFSPIOPError(error)
52
- const errorInfo = await this.deps.partiesUtils.makePutPartiesErrorPayload(this.deps.config, fspiopError, headers, params)
53
-
54
- await this.sendErrorCallback({ sendTo, errorInfo, headers, params })
55
- log.info('handleError in done', { sendTo, errorInfo })
108
+ const callbackHeaders = BasePartiesService.createErrorCallbackHeaders(this.inputs.headers, params)
109
+ const errorInfo = await this.deps.partiesUtils.makePutPartiesErrorPayload(this.deps.config, fspiopError, callbackHeaders, params)
110
+
111
+ await this.sendErrorCallback({
112
+ errorInfo,
113
+ headers: callbackHeaders,
114
+ params
115
+ })
116
+ log.info('handleError in done')
56
117
  return fspiopError
57
118
  } catch (exc) {
58
119
  // We can't do anything else here- we _must_ handle all errors _within_ this function because
@@ -62,26 +123,94 @@ class BasePartiesService {
62
123
  }
63
124
 
64
125
  async validateParticipant (participantId) {
126
+ this.stepInProgress('validateParticipant')
65
127
  return this.deps.participant.validateParticipant(participantId)
66
128
  }
67
129
 
68
- async sendErrorCallback ({ sendTo, errorInfo, headers, params, payload = undefined }) {
130
+ async sendErrorCallback ({ errorInfo, headers, params }) {
131
+ this.stepInProgress('sendErrorCallback')
132
+ const sendTo = this.state.requester || this.state.source
69
133
  const endpointType = this.deps.partiesUtils.errorPartyCbType(params.SubId)
134
+
70
135
  await this.deps.participant.sendErrorToParticipant(
71
- sendTo, endpointType, errorInfo, headers, params, payload, this.deps.childSpan
136
+ sendTo, endpointType, errorInfo, headers, params, undefined, this.deps.childSpan
72
137
  )
73
- this.log.verbose('sendErrorCallback is done', { sendTo, errorInfo })
138
+ this.log.info('sendErrorCallback is done', { sendTo, errorInfo })
74
139
  }
75
140
 
76
141
  async sendDeleteOracleRequest (headers, params) {
142
+ this.stepInProgress('sendDeleteOracleRequest')
77
143
  return this.deps.oracle.oracleRequest(headers, RestMethods.DELETE, params, null, null, this.deps.cache)
78
144
  }
79
145
 
146
+ async removeProxyGetPartiesTimeoutCache (alsReq) {
147
+ const isRemoved = await this.deps.proxyCache.removeProxyGetPartiesTimeout(alsReq, this.state.proxy)
148
+ this.log.debug('removeProxyGetPartiesTimeoutCache is done', { isRemoved, alsReq })
149
+ return isRemoved
150
+ }
151
+
152
+ createFspiopIdNotFoundError (errMessage, log = this.log) {
153
+ log.warn(errMessage)
154
+ return ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
155
+ }
156
+
157
+ stepInProgress (stepName) {
158
+ this.log.debug('step is in progress', { stepName })
159
+ this.state.stepState?.inProgress(stepName)
160
+ }
161
+
162
+ get currenStep () {
163
+ return this.state.stepState?.step
164
+ }
165
+
166
+ /** @returns {PartiesModelState} */
167
+ #initiateState () {
168
+ const { headers } = this.inputs
169
+ return {
170
+ destination: headers[Headers.FSPIOP.DESTINATION],
171
+ source: headers[Headers.FSPIOP.SOURCE],
172
+ proxy: headers[Headers.FSPIOP.PROXY],
173
+ requester: '', // dfsp in scheme OR proxy
174
+ proxyEnabled: !!(this.deps.config.PROXY_CACHE_CONFIG?.enabled && this.deps.proxyCache),
175
+ stepState: initStepState()
176
+ }
177
+ }
178
+
80
179
  static decodeDataUriPayload (dataUri) {
81
180
  const decoded = decodePayload(dataUri, { asParsed: false })
82
181
  return decoded.body.toString()
83
182
  }
84
183
 
184
+ static headersWithoutDestination (headers) {
185
+ const { [Headers.FSPIOP.DESTINATION]: _, ...restHeaders } = headers || {}
186
+ return restHeaders
187
+ }
188
+
189
+ static overrideDestinationHeader (headers, destination) {
190
+ return {
191
+ ...BasePartiesService.headersWithoutDestination(headers),
192
+ ...(destination && { [Headers.FSPIOP.DESTINATION]: destination })
193
+ }
194
+ }
195
+
196
+ static createErrorCallbackHeaders (headers, params) {
197
+ return createCallbackHeaders({
198
+ requestHeaders: headers,
199
+ partyIdType: params.Type,
200
+ partyIdentifier: params.ID,
201
+ endpointTemplate: params.SubId
202
+ ? FspEndpointTemplates.PARTIES_SUB_ID_PUT_ERROR
203
+ : FspEndpointTemplates.PARTIES_PUT_ERROR
204
+ })
205
+ }
206
+
207
+ static createHubErrorCallbackHeaders (hubName, destination) {
208
+ return {
209
+ [Headers.FSPIOP.SOURCE]: hubName,
210
+ [Headers.FSPIOP.DESTINATION]: destination
211
+ }
212
+ }
213
+
85
214
  static enums () {
86
215
  return {
87
216
  FspEndpointTypes,