account-lookup-service 17.8.0-snapshot.6 → 17.8.0-snapshot.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
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.8.0-snapshot.6",
4
+ "version": "17.8.0-snapshot.8",
5
5
  "license": "Apache-2.0",
6
6
  "author": "ModusBox",
7
7
  "contributors": [
@@ -99,8 +99,8 @@
99
99
  "@mojaloop/database-lib": "11.1.4",
100
100
  "@mojaloop/event-sdk": "14.4.0",
101
101
  "@mojaloop/inter-scheme-proxy-cache-lib": "2.4.0",
102
- "@mojaloop/ml-schema-transformer-lib": "2.6.0",
103
- "@mojaloop/sdk-standard-components": "19.11.1",
102
+ "@mojaloop/ml-schema-transformer-lib": "2.7.0",
103
+ "@mojaloop/sdk-standard-components": "19.11.2",
104
104
  "@now-ims/hapi-now-auth": "2.1.0",
105
105
  "ajv": "8.17.1",
106
106
  "ajv-keywords": "5.1.0",
@@ -25,7 +25,7 @@
25
25
  --------------
26
26
  ******/
27
27
 
28
- const { proxies } = require('@mojaloop/central-services-shared').Util
28
+ const { Util } = require('@mojaloop/central-services-shared')
29
29
  const { logger } = require('../../lib')
30
30
  const config = require('../../lib/config')
31
31
  const oracle = require('../../models/oracle/facade')
@@ -33,7 +33,13 @@ const participant = require('../../models/participantEndpoint/facade')
33
33
  const partiesUtils = require('./partiesUtils')
34
34
 
35
35
  /** @returns {PartiesDeps} */
36
- const createDeps = ({ cache, proxyCache, childSpan, log = logger }) => Object.freeze({
36
+ const createDeps = ({
37
+ cache,
38
+ proxyCache,
39
+ proxies = Util.proxies,
40
+ childSpan = null,
41
+ log = logger
42
+ }) => Object.freeze({
37
43
  cache,
38
44
  proxyCache,
39
45
  childSpan,
@@ -46,12 +46,10 @@ class GetPartiesService extends BasePartiesService {
46
46
  }
47
47
 
48
48
  const response = await this.sendOracleDiscoveryRequest()
49
- if (Array.isArray(response?.data?.partyList) && response.data.partyList.length > 0) {
50
- const isDone = await this.processOraclePartyListResponse(response)
51
- if (isDone) return
52
- }
49
+ const isSent = await this.processOraclePartyListResponse(response)
50
+ this.log.info(`getParties request is ${isSent ? '' : 'NOT '}forwarded to oracle lookup DFSP`)
51
+ if (isSent) return
53
52
 
54
- this.log.info('no forwarded requests for oracle partyList')
55
53
  const fspiopError = await this.triggerInterSchemeDiscoveryFlow(this.inputs.headers)
56
54
  if (fspiopError) {
57
55
  this.state.fspiopError = fspiopError // todo: think, if we need this
@@ -118,6 +116,11 @@ class GetPartiesService extends BasePartiesService {
118
116
  }
119
117
 
120
118
  async processOraclePartyListResponse (response) {
119
+ if (!Array.isArray(response?.data?.partyList) || response.data.partyList.length === 0) {
120
+ this.log.verbose('oracle partyList is empty')
121
+ return false
122
+ }
123
+
121
124
  this.stepInProgress('processOraclePartyList')
122
125
  const partyList = this.#filterOraclePartyList(response)
123
126
 
@@ -127,47 +130,31 @@ class GetPartiesService extends BasePartiesService {
127
130
  if (isSent) sentCount++
128
131
  }))
129
132
 
130
- const isDone = sentCount > 0
131
- this.log.verbose('processOraclePartyList is done', { isDone, sentCount })
132
- // if NOT isDone, need to trigger interScheme discovery flow
133
- return isDone
133
+ const isSent = sentCount > 0
134
+ this.log.verbose('processOraclePartyList is done', { isSent, sentCount })
135
+ // if NOT isSent, need to trigger interScheme discovery flow
136
+ return isSent
134
137
  }
135
138
 
136
139
  async triggerInterSchemeDiscoveryFlow (headers) {
137
140
  const { params } = this.inputs
138
141
  const { proxy, source } = this.state
139
- const log = this.log.child({ method: 'triggerInterSchemeDiscoveryFlow' })
140
- log.verbose('triggerInterSchemeDiscoveryFlow start...', { proxy, source })
142
+ const log = this.log.child({ source })
143
+ log.verbose('triggerInterSchemeDiscoveryFlow start...', { proxy })
141
144
 
142
145
  const proxyNames = await this.#getFilteredProxyList(proxy)
143
146
  if (!proxyNames.length) {
144
147
  return this.#sendPartyNotFoundErrorCallback(headers)
145
148
  }
146
149
 
147
- this.stepInProgress('setSendToProxiesList-10')
148
- const alsReq = this.deps.partiesUtils.alsRequestDto(source, params)
149
- log.verbose('starting setSendToProxiesList flow: ', { proxyNames, alsReq, proxyCacheTtlSec })
150
-
151
- const isCached = await this.deps.proxyCache.setSendToProxiesList(alsReq, proxyNames, proxyCacheTtlSec)
152
- if (!isCached) {
153
- throw super.createFspiopIdNotFoundError(ERROR_MESSAGES.failedToCacheSendToProxiesList, log)
154
- }
155
-
156
- this.stepInProgress('sendingProxyRequests')
157
- const sending = proxyNames.map(
158
- sendTo => this.#forwardGetPartiesRequest({ sendTo, headers, params })
159
- .then(({ status, data } = {}) => ({ status, data }))
160
- )
161
- const results = await Promise.allSettled(sending)
162
- const isOk = results.some(result => result.status === 'fulfilled')
163
- // If, at least, one request is sent to proxy, we treat the whole flow as successful.
164
- // Failed requests should be handled by TTL expired/timeout handler
165
- log.info('triggerInterSchemeDiscoveryFlow is done:', { isOk, results, proxyNames, alsReq })
166
- this.stepInProgress('allSent-12')
167
-
168
- if (!isOk) {
150
+ const alsReq = await this.#setProxyListToCache(proxyNames, source, params)
151
+ const sentList = await this.#sendOutProxyRequests({ proxyNames, alsReq, headers, params })
152
+ if (sentList.length === 0) {
169
153
  throw super.createFspiopIdNotFoundError(ERROR_MESSAGES.proxyConnectionError, log)
170
154
  }
155
+
156
+ log.info('triggerInterSchemeDiscoveryFlow is done:', { sentList, alsReq })
157
+ return sentList
171
158
  }
172
159
 
173
160
  isLocalSource () {
@@ -245,11 +232,15 @@ class GetPartiesService extends BasePartiesService {
245
232
 
246
233
  async #sendPartyNotFoundErrorCallback (headers) {
247
234
  const { params } = this.inputs
235
+ const callbackHeaders = GetPartiesService.createErrorCallbackHeaders(headers, params)
248
236
  const fspiopError = super.createFspiopIdNotFoundError('No proxy found to start inter-scheme discovery flow')
237
+ const errorInfo = await this.deps.partiesUtils.makePutPartiesErrorPayload(
238
+ this.deps.config, fspiopError, callbackHeaders, params
239
+ )
249
240
 
250
241
  await this.sendErrorCallback({
251
- errorInfo: fspiopError.toApiErrorObject(this.deps.config.ERROR_HANDLING),
252
- headers: GetPartiesService.createErrorCallbackHeaders(headers, params),
242
+ errorInfo,
243
+ headers: callbackHeaders,
253
244
  params
254
245
  })
255
246
  return fspiopError
@@ -264,9 +255,29 @@ class GetPartiesService extends BasePartiesService {
264
255
  headers, sendTo, callbackEndpointType, RestMethods.GET, undefined, options, this.deps.childSpan
265
256
  )
266
257
  await this.#setProxyGetPartiesTimeout(sendTo)
258
+ this.log.debug('#forwardGetPartiesRequest is done:', { sendTo, sentResult })
267
259
  return sentResult
268
260
  }
269
261
 
262
+ async #sendOutProxyRequests ({ proxyNames, alsReq, headers, params }) {
263
+ this.stepInProgress('#sendOutProxyRequests')
264
+ const sentList = []
265
+
266
+ const sendProxyRequest = (sendTo) => this.#forwardGetPartiesRequest({ sendTo, headers, params })
267
+ .then(() => { sentList.push(sendTo) })
268
+ .catch(err => {
269
+ this.log.error(`error in sending request to proxy ${sendTo}: `, err)
270
+ return this.deps.proxyCache.receivedErrorResponse(alsReq, sendTo)
271
+ })
272
+ .catch(err => {
273
+ this.log.error(`failed to remove proxy ${sendTo} from proxyCache: `, err)
274
+ })
275
+ await Promise.all(proxyNames.map(sendProxyRequest))
276
+
277
+ this.log.verbose('#sendOutProxyRequests is done:', { sentList, proxyNames })
278
+ return sentList
279
+ }
280
+
270
281
  async #getFilteredProxyList (proxy) {
271
282
  this.stepInProgress('#getFilteredProxyList')
272
283
  if (!this.state.proxyEnabled) {
@@ -279,6 +290,18 @@ class GetPartiesService extends BasePartiesService {
279
290
  return proxyNames.filter(name => name !== proxy)
280
291
  }
281
292
 
293
+ async #setProxyListToCache (proxyNames, source, params) {
294
+ this.stepInProgress('#setProxyListToCache')
295
+ const alsReq = this.deps.partiesUtils.alsRequestDto(source, params)
296
+
297
+ const isCached = await this.deps.proxyCache.setSendToProxiesList(alsReq, proxyNames, proxyCacheTtlSec)
298
+ if (!isCached) {
299
+ throw super.createFspiopIdNotFoundError(ERROR_MESSAGES.failedToCacheSendToProxiesList)
300
+ }
301
+ this.log.verbose('#setProxyListToCache is done: ', { alsReq, proxyNames, proxyCacheTtlSec })
302
+ return alsReq
303
+ }
304
+
282
305
  async #setProxyGetPartiesTimeout (sendTo) {
283
306
  const isLocalSource = this.isLocalSource()
284
307
  const isSentToProxy = this.state.destination !== sendTo
@@ -295,12 +318,3 @@ class GetPartiesService extends BasePartiesService {
295
318
  }
296
319
 
297
320
  module.exports = GetPartiesService
298
-
299
- // As Payee DFSP Scheme Oracle identifies direct participant.
300
- // As Payer DFSP Scheme oracle identifies interscheme participant.
301
-
302
- // zm-dfsp --> ZM
303
- // region-dfsp --> Region
304
- // mw-dfsp --> MW (mw-party-123)
305
-
306
- // 1. region-dfsp gets info about mw-party-123
@@ -30,6 +30,7 @@ const { ERROR_MESSAGES } = require('../../../constants')
30
30
  const BasePartiesService = require('./BasePartiesService')
31
31
 
32
32
  class PutPartiesErrorService extends BasePartiesService {
33
+ /** @returns {Promise<true | undefined>} - If true, need to trigger inter-scheme discovery. */
33
34
  async handleRequest () {
34
35
  if (this.state.proxyEnabled && this.state.proxy) {
35
36
  const alsReq = this.deps.partiesUtils.alsRequestDto(this.state.destination, this.inputs.params) // or source?
@@ -67,7 +68,7 @@ class PutPartiesErrorService extends BasePartiesService {
67
68
  this.stepInProgress('checkLastProxyCallback')
68
69
  const { proxy } = this.state
69
70
  const isLast = await this.deps.proxyCache.receivedErrorResponse(alsReq, proxy)
70
- this.log.info(`got${isLast ? '' : 'NOT'} last error callback from proxy`, { proxy, alsReq, isLast })
71
+ this.log.info(`got ${isLast ? '' : 'NOT '}last inter-scheme error callback from a proxy`, { proxy, alsReq, isLast })
71
72
  return isLast
72
73
  }
73
74
 
@@ -48,7 +48,7 @@ describe('BasePartiesService Tests -->', () => {
48
48
  const service = new BasePartiesService(deps, { headers, params })
49
49
 
50
50
  await service.handleError(new Error('test error'))
51
- expect(participantMock.sendErrorToParticipant.mock.calls.length).toBe(1)
51
+ expect(participantMock.sendErrorToParticipant).toHaveBeenCalledTimes(1)
52
52
  // eslint-disable-next-line no-unused-vars
53
53
  const [sentTo, _, payload] = participantMock.sendErrorToParticipant.mock.lastCall
54
54
  expect(sentTo).toBe(source)
@@ -25,14 +25,23 @@
25
25
  --------------
26
26
  ******/
27
27
 
28
- const { createMockDeps, createProxyCacheMock, oracleMock, participantMock } = require('./deps')
28
+ const {
29
+ createMockDeps,
30
+ createProxyCacheMock,
31
+ createProxiesUtilMock,
32
+ oracleMock,
33
+ participantMock
34
+ } = require('./deps')
29
35
  // ↑ should be first require to mock external deps ↑
30
36
  const { GetPartiesService } = require('#src/domain/parties/services/index')
37
+ const { API_TYPES, ERROR_MESSAGES } = require('#src/constants')
31
38
  const fixtures = require('#test/fixtures/index')
32
39
 
33
40
  const { RestMethods, Headers } = GetPartiesService.enums()
34
41
 
35
42
  describe('GetPartiesService Tests -->', () => {
43
+ const { config } = createMockDeps()
44
+
36
45
  beforeEach(() => {
37
46
  jest.clearAllMocks()
38
47
  })
@@ -151,6 +160,45 @@ describe('GetPartiesService Tests -->', () => {
151
160
  })
152
161
  })
153
162
 
163
+ describe('triggerInterSchemeDiscoveryFlow Tests', () => {
164
+ test('should remove proxy from proxyCache if sending request to it fails', async () => {
165
+ expect.assertions(2)
166
+ participantMock.sendRequest = jest.fn().mockRejectedValue(new Error('Proxy error'))
167
+ const proxies = createProxiesUtilMock({
168
+ getAllProxiesNames: jest.fn().mockResolvedValue(['proxy-1'])
169
+ })
170
+ const deps = createMockDeps({ proxies })
171
+
172
+ const headers = fixtures.partiesCallHeadersDto({ destination: '' })
173
+ const params = fixtures.partiesParamsDto()
174
+ const service = new GetPartiesService(deps, { headers, params })
175
+
176
+ await expect(service.triggerInterSchemeDiscoveryFlow(headers))
177
+ .rejects.toThrow(ERROR_MESSAGES.proxyConnectionError)
178
+ expect(deps.proxyCache.receivedErrorResponse).toHaveBeenCalledTimes(1)
179
+ })
180
+
181
+ test('should NOT throw an error if at least one request is sent to a proxy', async () => {
182
+ participantMock.sendRequest = jest.fn()
183
+ .mockRejectedValueOnce(new Error('Proxy error'))
184
+ .mockResolvedValueOnce({})
185
+ const proxyOk = 'proxyOk'
186
+ const proxies = createProxiesUtilMock({
187
+ getAllProxiesNames: jest.fn().mockResolvedValue(['proxyErr', proxyOk])
188
+ })
189
+ const deps = createMockDeps({ proxies })
190
+
191
+ const headers = fixtures.partiesCallHeadersDto({ destination: '' })
192
+ const params = fixtures.partiesParamsDto()
193
+ const service = new GetPartiesService(deps, { headers, params })
194
+
195
+ const sentList = await service.triggerInterSchemeDiscoveryFlow(headers)
196
+ expect(sentList).toEqual([proxyOk])
197
+ expect(deps.proxyCache.receivedErrorResponse).toHaveBeenCalledTimes(1)
198
+ expect(participantMock.sendRequest.mock.lastCall[1]).toBe(proxyOk)
199
+ })
200
+ })
201
+
154
202
  describe('setProxyGetPartiesTimeout Tests', () => {
155
203
  test('should set getParties timeout for local source and external destination', async () => {
156
204
  participantMock.validateParticipant = jest.fn()
@@ -221,4 +269,22 @@ describe('GetPartiesService Tests -->', () => {
221
269
  expect(participantMock.sendRequest).toHaveBeenCalledTimes(1)
222
270
  })
223
271
  })
272
+
273
+ test('should send partyNotFound callback in ISO20022 format', async () => {
274
+ participantMock.validateParticipant = jest.fn().mockResolvedValue({})
275
+ oracleMock.oracleRequest = jest.fn()
276
+ const deps = {
277
+ ...createMockDeps(),
278
+ config: { ...config, API_TYPE: API_TYPES.iso20022 }
279
+ }
280
+ const headers = fixtures.partiesCallHeadersDto({ destination: '' })
281
+ const params = fixtures.partiesParamsDto()
282
+ const service = new GetPartiesService(deps, { headers, params })
283
+
284
+ await service.handleRequest()
285
+ expect(participantMock.sendErrorToParticipant).toHaveBeenCalledTimes(1)
286
+ const isoPayload = participantMock.sendErrorToParticipant.mock.lastCall[2]
287
+ expect(isoPayload.Assgnmt).toBeDefined()
288
+ expect(isoPayload.Rpt).toBeDefined()
289
+ })
224
290
  })
@@ -32,18 +32,20 @@ const oracleMock = require('#src/models/oracle/facade')
32
32
  const participantMock = require('#src/models/participantEndpoint/facade')
33
33
  const { createDeps } = require('#src/domain/parties/deps')
34
34
  const { logger } = require('#src/lib/index')
35
- const { createProxyCacheMock } = require('#test/util/mockDeps')
35
+ const { createProxyCacheMock, createProxiesUtilMock } = require('#test/util/mockDeps')
36
36
 
37
37
  /** @returns {PartiesDeps} */
38
38
  const createMockDeps = ({
39
39
  proxyCache = createProxyCacheMock(),
40
+ proxies = createProxiesUtilMock(),
40
41
  log = logger.child({ test: true }),
41
42
  cache
42
- } = {}) => createDeps({ cache, proxyCache, log })
43
+ } = {}) => createDeps({ cache, proxyCache, proxies, log })
43
44
 
44
45
  module.exports = {
45
46
  createMockDeps,
46
47
  createProxyCacheMock,
48
+ createProxiesUtilMock,
47
49
  oracleMock,
48
50
  participantMock
49
51
  }
@@ -56,6 +56,17 @@ const createProxyCacheMock = ({
56
56
  setSendToProxiesList
57
57
  })
58
58
 
59
+ // @mojaloop/central-services-shared/Util/proxies
60
+ /** @returns {Proxies} */
61
+ const createProxiesUtilMock = ({
62
+ getAllProxiesNames = jest.fn().mockResolvedValue([]),
63
+ invalidateProxiesCache = jest.fn()
64
+ } = {}) => ({
65
+ getAllProxiesNames,
66
+ invalidateProxiesCache
67
+ })
68
+
59
69
  module.exports = {
60
- createProxyCacheMock
70
+ createProxyCacheMock,
71
+ createProxiesUtilMock
61
72
  }