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 +3 -3
- package/src/domain/parties/deps.js +8 -2
- package/src/domain/parties/services/GetPartiesService.js +58 -44
- package/src/domain/parties/services/PutPartiesErrorService.js +2 -1
- package/test/unit/domain/parties/services/BasePartiesService.test.js +1 -1
- package/test/unit/domain/parties/services/GetPartiesService.test.js +67 -1
- package/test/unit/domain/parties/services/deps.js +4 -2
- package/test/util/mockDeps.js +12 -1
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.
|
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.
|
103
|
-
"@mojaloop/sdk-standard-components": "19.11.
|
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 {
|
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 = ({
|
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
|
-
|
50
|
-
|
51
|
-
|
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
|
131
|
-
this.log.verbose('processOraclePartyList is done', {
|
132
|
-
// if NOT
|
133
|
-
return
|
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({
|
140
|
-
log.verbose('triggerInterSchemeDiscoveryFlow start...', { proxy
|
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
|
148
|
-
const
|
149
|
-
|
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
|
252
|
-
headers:
|
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'}
|
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
|
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 {
|
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
|
}
|
package/test/util/mockDeps.js
CHANGED
@@ -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
|
}
|