account-lookup-service 17.12.3 → 17.12.4

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.
@@ -107,9 +107,13 @@ class BasePartiesService {
107
107
  try {
108
108
  log.error('error in processing parties request: ', error)
109
109
  const fspiopError = ErrorHandler.Factory.reformatFSPIOPError(error)
110
+
110
111
  const callbackHeaders = BasePartiesService.createErrorCallbackHeaders(this.inputs.headers, params)
111
112
  const errorInfo = await this.deps.partiesUtils.makePutPartiesErrorPayload(this.deps.config, fspiopError, callbackHeaders, params)
112
113
 
114
+ this.state.destination = callbackHeaders[Headers.FSPIOP.DESTINATION]
115
+ await this.identifyDestinationForCallback()
116
+
113
117
  await this.sendErrorCallback({
114
118
  errorInfo,
115
119
  headers: callbackHeaders,
@@ -127,7 +131,7 @@ class BasePartiesService {
127
131
  async validateParticipant (participantId) {
128
132
  try {
129
133
  this.stepInProgress('validateParticipant')
130
- return this.deps.participant.validateParticipant(participantId)
134
+ return await this.deps.participant.validateParticipant(participantId)
131
135
  } catch (err) {
132
136
  this.log.warn(`error in validateParticipant ${participantId}: `, err)
133
137
  return null
@@ -157,7 +161,7 @@ class BasePartiesService {
157
161
 
158
162
  async sendErrorCallback ({ errorInfo, headers, params }) {
159
163
  this.stepInProgress('sendErrorCallback')
160
- const sendTo = this.state.requester || headers[Headers.FSPIOP.DESTINATION] /* || this.state.source */
164
+ const sendTo = this.state.requester || headers[Headers.FSPIOP.DESTINATION]
161
165
  const endpointType = this.deps.partiesUtils.errorPartyCbType(params.SubId)
162
166
 
163
167
  await this.deps.participant.sendErrorToParticipant(
@@ -168,7 +172,9 @@ class BasePartiesService {
168
172
 
169
173
  async sendDeleteOracleRequest (headers, params) {
170
174
  this.stepInProgress('sendDeleteOracleRequest')
171
- return this.deps.oracle.oracleRequest(headers, RestMethods.DELETE, params, null, null, this.deps.cache)
175
+ const result = await this.deps.oracle.oracleRequest(headers, RestMethods.DELETE, params, null, null, this.deps.cache)
176
+ this.log.verbose('sendDeleteOracleRequest is done', { params })
177
+ return result
172
178
  }
173
179
 
174
180
  async removeProxyGetPartiesTimeoutCache (alsReq) {
package/src/lib/config.js CHANGED
@@ -175,7 +175,8 @@ const config = {
175
175
  FEATURE_ENABLE_EXTENDED_PARTY_ID_TYPE: RC.FEATURE_ENABLE_EXTENDED_PARTY_ID_TYPE || false,
176
176
  PROTOCOL_VERSIONS: getProtocolVersions(DEFAULT_PROTOCOL_VERSION, RC.PROTOCOL_VERSIONS),
177
177
  PROXY_CACHE_CONFIG: RC.PROXY_CACHE,
178
- DELETE_PARTICIPANT_VALIDATION_ENABLED: RC.DELETE_PARTICIPANT_VALIDATION_ENABLED || false
178
+ DELETE_PARTICIPANT_VALIDATION_ENABLED: RC.DELETE_PARTICIPANT_VALIDATION_ENABLED || false,
179
+ HTTP_REQUEST_TIMEOUT_MS: RC.HTTP_REQUEST_TIMEOUT_MS ?? 20_000
179
180
  }
180
181
 
181
182
  if (config.JWS_SIGN) {
@@ -43,7 +43,10 @@ const { Headers, RestMethods, ReturnCodes } = Enums.Http
43
43
  const sendHttpRequest = ({ method, ...restArgs }) => request.sendRequest({
44
44
  ...restArgs,
45
45
  method: method.toUpperCase(),
46
- hubNameRegex
46
+ hubNameRegex,
47
+ axiosRequestOptionsOverride: {
48
+ timeout: Config.HTTP_REQUEST_TIMEOUT_MS
49
+ }
47
50
  })
48
51
 
49
52
  /**
@@ -37,6 +37,9 @@ const { logger } = require('../../lib')
37
37
  const { hubNameRegex } = require('../../lib/util').hubNameConfig
38
38
 
39
39
  const uriRegex = /(?:^.*)(\/(participants|parties|quotes|transfers)(\/.*)*)$/
40
+ const axiosRequestOptionsOverride = {
41
+ timeout: Config.HTTP_REQUEST_TIMEOUT_MS
42
+ }
40
43
 
41
44
  /**
42
45
  * @module src/models/participantEndpoint/facade
@@ -115,7 +118,8 @@ exports.sendRequest = async (headers, requestedParticipant, endpointType, method
115
118
  span,
116
119
  protocolVersions,
117
120
  hubNameRegex,
118
- apiType: Config.API_TYPE
121
+ apiType: Config.API_TYPE,
122
+ axiosRequestOptionsOverride
119
123
  }
120
124
  logger.debug('participant - sendRequest params:', { params })
121
125
  params.jwsSigner = defineJwsSigner(Config, headers, requestedEndpoint)
@@ -252,7 +256,8 @@ exports.sendErrorToParticipant = async (participantName, endpointType, errorInfo
252
256
  hubNameRegex,
253
257
  span,
254
258
  protocolVersions,
255
- apiType: Config.API_TYPE
259
+ apiType: Config.API_TYPE,
260
+ axiosRequestOptionsOverride
256
261
  }
257
262
  logger.debug('participant - sendErrorToParticipant params: ', { params })
258
263
  params.jwsSigner = defineJwsSigner(Config, clonedHeaders, requesterErrorEndpoint)
@@ -240,7 +240,9 @@ describe('Parties Tests', () => {
240
240
  it('handles error when sourceDfsp cannot be found (no fspiop-proxy in headers)', async () => {
241
241
  expect.hasAssertions()
242
242
  // Arrange
243
- participant.validateParticipant = sandbox.stub().resolves(null)
243
+ participant.validateParticipant = sandbox.stub()
244
+ .resolves(null)
245
+ .onSecondCall().resolves({})
244
246
  participant.sendErrorToParticipant = sandbox.stub().resolves(null)
245
247
  const loggerStub = sandbox.stub(logger.mlLogger, 'error')
246
248
 
@@ -258,7 +260,11 @@ describe('Parties Tests', () => {
258
260
 
259
261
  it('should send error callback, if proxy-header is present, but no proxy in the scheme', async () => {
260
262
  Config.PROXY_CACHE_CONFIG.enabled = true
261
- participant.validateParticipant = sandbox.stub().resolves(null)
263
+ participant.validateParticipant = sandbox.stub()
264
+ .resolves(null) // source
265
+ .onSecondCall().resolves(null) // proxy
266
+ .onThirdCall().resolves({}) // destination of callback
267
+ // !! Need to review the whole test: coz it's not clear where to send callback if no proxy and external source
262
268
  participant.sendErrorToParticipant = sandbox.stub().resolves()
263
269
  participant.sendRequest = sandbox.stub().resolves()
264
270
  const proxy = `proxy-${Date.now()}`
@@ -483,6 +489,7 @@ describe('Parties Tests', () => {
483
489
  participant.validateParticipant = sandbox.stub()
484
490
  .onFirstCall().resolves(null) // source
485
491
  .onSecondCall().resolves(null) // oracle dfsp
492
+ .onThirdCall().resolves({})
486
493
  participant.sendRequest = sandbox.stub().resolves()
487
494
  participant.sendErrorToParticipant = sandbox.stub().resolves()
488
495
 
@@ -631,6 +638,9 @@ describe('Parties Tests', () => {
631
638
  // Arrange
632
639
  const loggerStub = sandbox.stub(logger.constructor.prototype, 'error')
633
640
  participant.sendErrorToParticipant = sandbox.stub().resolves()
641
+ participant.validateParticipant = sandbox.stub()
642
+ .resolves(null)
643
+ .onSecondCall().resolves({})
634
644
 
635
645
  const payload = JSON.stringify({ testPayload: true })
636
646
  const dataUri = encodePayload(payload, 'application/json')
@@ -755,7 +765,7 @@ describe('Parties Tests', () => {
755
765
  await partiesDomain.putPartiesByTypeAndID(Helper.putByTypeIdRequest.headers, Helper.putByTypeIdRequest.params, 'put', payload, dataUri, null, proxyCache)
756
766
 
757
767
  // Assert
758
- expect(participant.validateParticipant.callCount).toBe(2)
768
+ expect(participant.validateParticipant.callCount).toBe(3)
759
769
  expect(participant.sendErrorToParticipant.callCount).toBe(1)
760
770
  participant.validateParticipant.reset()
761
771
  participant.sendErrorToParticipant.reset()
@@ -784,7 +794,7 @@ describe('Parties Tests', () => {
784
794
  await partiesDomain.putPartiesByTypeAndID(Helper.putByTypeIdRequest.headers, params, 'put', payload, dataUri, null, proxyCache)
785
795
 
786
796
  // Assert
787
- expect(participant.validateParticipant.callCount).toBe(2)
797
+ expect(participant.validateParticipant.callCount).toBe(3)
788
798
  expect(participant.sendErrorToParticipant.callCount).toBe(1)
789
799
  const firstCallArgs = participant.sendErrorToParticipant.getCall(0).args
790
800
  expect(firstCallArgs[1]).toBe(expectedErrorCallbackEnpointType)
@@ -850,7 +860,9 @@ describe('Parties Tests', () => {
850
860
  it('sends error to the participant when there is no destination participant', async () => {
851
861
  expect.hasAssertions()
852
862
  // Arrange
853
- participant.validateParticipant = sandbox.stub().resolves(null)
863
+ participant.validateParticipant = sandbox.stub()
864
+ .resolves(null)
865
+ .onSecondCall().resolves({})
854
866
  participant.sendErrorToParticipant = sandbox.stub().throws(new Error('Unknown error'))
855
867
  const payload = JSON.stringify({ errorPayload: true })
856
868
  const dataUri = encodePayload(payload, 'application/json')
@@ -884,14 +896,16 @@ describe('Parties Tests', () => {
884
896
  // Assert
885
897
  expect(participant.sendErrorToParticipant.callCount).toBe(1)
886
898
  const sendErrorCallArgs = participant.sendErrorToParticipant.getCall(0).args
887
- expect(sendErrorCallArgs[0]).toStrictEqual(headers['fspiop-destination'])
899
+ expect(sendErrorCallArgs[0]).toStrictEqual(headers['fspiop-source'])
888
900
  })
889
901
 
890
902
  it('handles error when `validateParticipant()` fails', async () => {
891
903
  expect.hasAssertions()
892
904
  // Arrange)
893
905
  const loggerStub = sandbox.stub(logger.constructor.prototype, 'error')
894
- participant.validateParticipant = sandbox.stub().throws(new Error('Validation fails'))
906
+ participant.validateParticipant = sandbox.stub()
907
+ .onFirstCall().throws(new Error('Validation fails'))
908
+ .onSecondCall().resolves({})
895
909
  participant.sendErrorToParticipant = sandbox.stub().resolves({})
896
910
  const payload = JSON.stringify({ errorPayload: true })
897
911
  const expectedCallbackEnpointType = Enum.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_PUT_ERROR
@@ -911,7 +925,9 @@ describe('Parties Tests', () => {
911
925
  // Arrange
912
926
 
913
927
  const loggerStub = sandbox.stub(logger.constructor.prototype, 'error')
914
- participant.validateParticipant = sandbox.stub().throws(new Error('Validation fails'))
928
+ participant.validateParticipant = sandbox.stub()
929
+ .throws(new Error('Validation fails'))
930
+ .onSecondCall().resolves({})
915
931
  participant.sendErrorToParticipant = sandbox.stub().resolves({})
916
932
  const payload = JSON.stringify({ errorPayload: true })
917
933
  const params = { ...Helper.putByTypeIdRequest.params, SubId: 'SubId' }
@@ -38,6 +38,7 @@ describe('BasePartiesService Tests -->', () => {
38
38
  })
39
39
 
40
40
  test('should send error party callback in ISO20022 format', async () => {
41
+ participantMock.validateParticipant = jest.fn().mockResolvedValue({})
41
42
  const deps = {
42
43
  ...createMockDeps(),
43
44
  config: { ...config, API_TYPE: API_TYPES.iso20022 }
@@ -0,0 +1,100 @@
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 { createMockDeps, participantMock } = require('./deps')
29
+ // ↑ should be first require to mock external deps ↑
30
+ const { Enum } = require('@mojaloop/central-services-shared')
31
+ const { PutPartiesService } = require('#src/domain/parties/services/index')
32
+ const config = require('#src/lib/config')
33
+ const fixtures = require('#test/fixtures/index')
34
+
35
+ const { Headers } = Enum.Http
36
+
37
+ describe('PutPartiesService Tests -->', () => {
38
+ beforeEach(() => {
39
+ jest.clearAllMocks()
40
+ })
41
+
42
+ test('should send error callback to source in case of error on forwarding request to destination (buffer scheme)', async () => {
43
+ const errMessage = 'Test Http Error'
44
+ participantMock.sendRequest = jest.fn().mockRejectedValue(new Error(errMessage))
45
+ participantMock.validateParticipant = jest.fn().mockResolvedValue({})
46
+ const source = 'mwk-dfsp'
47
+ const destination = 'zmk-dfsp'
48
+ const headers = fixtures.partiesCallHeadersDto({ source, destination, proxy: '' })
49
+ const params = fixtures.partiesParamsDto()
50
+ const dataUri = fixtures.dataUriDto()
51
+ const service = new PutPartiesService(createMockDeps(), { headers, params, dataUri })
52
+
53
+ await service.handleRequest()
54
+ .catch(err => service.handleError(err))
55
+
56
+ expect(participantMock.sendErrorToParticipant).toHaveBeenCalledTimes(1)
57
+ // eslint-disable-next-line no-unused-vars
58
+ const [sentTo, _, payload, cbHeaders] = participantMock.sendErrorToParticipant.mock.lastCall
59
+ expect(sentTo).toBe(source)
60
+ expect(payload.errorInformation.errorCode).toBe('2001')
61
+ expect(payload.errorInformation.errorDescription.endsWith(errMessage)).toBe(true)
62
+ expect(cbHeaders[Headers.FSPIOP.SOURCE]).toBe(config.HUB_NAME)
63
+ expect(cbHeaders[Headers.FSPIOP.DESTINATION]).toBe(source)
64
+ })
65
+
66
+ test('should send error callback to source proxy in case of error on forwarding request to destination proxy (region scheme)', async () => {
67
+ const errMessage = 'Test Http Error'
68
+ participantMock.sendRequest = jest.fn().mockRejectedValue(new Error(errMessage))
69
+ participantMock.validateParticipant = jest.fn().mockResolvedValue(null)
70
+
71
+ const source = 'zmk-dfsp'
72
+ const proxy = 'proxy-zmk'
73
+ const destination = 'mwk-dfsp'
74
+ const proxyDest = 'proxy-mwk'
75
+
76
+ const deps = createMockDeps()
77
+ deps.proxyCache.lookupProxyByDfspId = jest.fn(async (dfsp) => {
78
+ if (dfsp === source) return proxy
79
+ if (dfsp === destination) return proxyDest
80
+ return null
81
+ })
82
+
83
+ const headers = fixtures.partiesCallHeadersDto({ source, destination, proxy })
84
+ const params = fixtures.partiesParamsDto()
85
+ const dataUri = fixtures.dataUriDto()
86
+ const service = new PutPartiesService(deps, { headers, params, dataUri })
87
+
88
+ await service.handleRequest()
89
+ .catch(err => service.handleError(err))
90
+
91
+ expect(participantMock.sendErrorToParticipant).toHaveBeenCalledTimes(1)
92
+ // eslint-disable-next-line no-unused-vars
93
+ const [sentTo, _, payload, cbHeaders] = participantMock.sendErrorToParticipant.mock.lastCall
94
+ expect(sentTo).toBe(proxy)
95
+ expect(payload.errorInformation.errorCode).toBe('2001')
96
+ expect(payload.errorInformation.errorDescription.endsWith(errMessage)).toBe(true)
97
+ expect(cbHeaders[Headers.FSPIOP.SOURCE]).toBe(config.HUB_NAME)
98
+ expect(cbHeaders[Headers.FSPIOP.DESTINATION]).toBe(source)
99
+ })
100
+ })
@@ -188,6 +188,26 @@ describe('participantEndpoint Facade', () => {
188
188
 
189
189
  expect(mockSendRequest.mock.lastCall[0].jwsSigner).toBeTruthy()
190
190
  })
191
+
192
+ it('should add default timeout to sendRequest', async () => {
193
+ const mockedConfig = {
194
+ JWS_SIGN: false,
195
+ FSPIOP_SOURCE_TO_SIGN: mockHubName,
196
+ PROTOCOL_VERSIONS: fixtures.protocolVersionsDto(),
197
+ HTTP_REQUEST_TIMEOUT_MS: 1000
198
+ }
199
+ jest.mock('../../../../src/lib/config', () => (mockedConfig))
200
+ mockGetEndpoint.mockImplementation(() => 'https://example.com/12345')
201
+ mockSendRequest.mockImplementation(async () => true)
202
+ const ParticipantFacade = require(`${src}/models/participantEndpoint/facade`)
203
+
204
+ const headers = {}
205
+ const requestedParticipant = {}
206
+ const endpointType = 'URL'
207
+
208
+ await ParticipantFacade.sendRequest(headers, requestedParticipant, endpointType)
209
+ expect(mockSendRequest.mock.calls[0][0].axiosRequestOptionsOverride.timeout).toBe(mockedConfig.HTTP_REQUEST_TIMEOUT_MS)
210
+ })
191
211
  })
192
212
 
193
213
  describe('validateParticipant', () => {