account-lookup-service 17.7.0 → 17.8.0-snapshot.10

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 (37) hide show
  1. package/docker/mock-proxy/src/server.ts +13 -4
  2. package/package.json +11 -8
  3. package/src/constants.js +35 -2
  4. package/src/domain/parties/deps.js +11 -4
  5. package/src/domain/parties/getPartiesByTypeAndID.js +9 -13
  6. package/src/domain/parties/partiesUtils.js +4 -34
  7. package/src/domain/parties/putParties.js +26 -71
  8. package/src/domain/parties/services/BasePartiesService.js +143 -15
  9. package/src/domain/parties/services/GetPartiesService.js +210 -165
  10. package/src/domain/parties/services/PutPartiesErrorService.js +52 -28
  11. package/src/domain/parties/services/PutPartiesService.js +51 -33
  12. package/src/domain/parties/services/TimeoutPartiesService.js +84 -0
  13. package/src/domain/parties/services/index.js +3 -1
  14. package/src/domain/timeout/createSpan.js +55 -0
  15. package/src/domain/timeout/index.js +27 -36
  16. package/src/handlers/TimeoutHandler.js +2 -2
  17. package/src/index.js +3 -0
  18. package/src/lib/util.js +11 -3
  19. package/test/fixtures/index.js +53 -3
  20. package/test/integration/api/parties.test.js +1 -0
  21. package/test/integration/domain/timeout/index.test.js +83 -28
  22. package/test/unit/domain/parties/parties.test.js +26 -18
  23. package/test/unit/domain/parties/partiesUtils.test.js +51 -0
  24. package/test/unit/domain/parties/services/BasePartiesService.test.js +71 -0
  25. package/test/unit/domain/parties/services/GetPartiesService.test.js +316 -0
  26. package/test/unit/domain/parties/services/PutPartiesErrorService.test.js +50 -0
  27. package/test/unit/domain/parties/services/TimeoutPartiesService.test.js +72 -0
  28. package/test/unit/domain/parties/services/deps.js +51 -0
  29. package/test/unit/domain/timeout/index.test.js +17 -12
  30. package/test/util/apiClients/AlsApiClient.js +6 -4
  31. package/test/util/apiClients/BasicApiClient.js +33 -6
  32. package/test/util/apiClients/ProxyApiClient.js +46 -1
  33. package/test/util/index.js +5 -6
  34. package/test/util/mockDeps.js +72 -0
  35. package/src/domain/timeout/dto.js +0 -54
  36. package/test/unit/domain/parties/utils.test.js +0 -60
  37. package/test/unit/domain/timeout/dto.test.js +0 -24
@@ -25,185 +25,268 @@
25
25
  --------------
26
26
  ******/
27
27
 
28
- const ErrorHandler = require('@mojaloop/central-services-error-handling')
29
28
  const { ERROR_MESSAGES } = require('../../../constants')
30
- const { createCallbackHeaders } = require('../../../lib/headers')
31
29
  const BasePartiesService = require('./BasePartiesService')
32
30
 
33
- const {
34
- FspEndpointTypes, FspEndpointTemplates,
35
- Headers, RestMethods
36
- } = BasePartiesService.enums()
37
-
31
+ const { FspEndpointTypes, RestMethods } = BasePartiesService.enums()
38
32
  const proxyCacheTtlSec = 40 // todo: make configurable
39
33
 
40
34
  class GetPartiesService extends BasePartiesService {
41
- async handleRequest ({ headers, params, query, results }) {
42
- const source = headers[Headers.FSPIOP.SOURCE]
43
- const proxy = headers[Headers.FSPIOP.PROXY]
44
- const destination = headers[Headers.FSPIOP.DESTINATION]
35
+ async handleRequest () {
36
+ const { destination, source, proxy } = this.state
45
37
  // see https://github.com/mojaloop/design-authority/issues/79
46
38
  // the requester has specified a destination routing header. We should respect that and forward the request directly to the destination
47
39
  // without consulting any oracles.
48
- this.log.info('handling getParties request', { source, destination, proxy })
49
-
50
- const requester = await this.validateRequester({ source, proxy })
51
- results.requester = requester
40
+ this.log.info('handling getParties request...', { source, destination, proxy })
41
+ this.state.requester = await this.validateRequester()
52
42
 
53
43
  if (destination) {
54
- await this.forwardRequestToDestination({ destination, headers, params })
55
- return
56
- }
57
- const response = await this.sendOracleDiscoveryRequest({ headers, params, query })
58
-
59
- if (Array.isArray(response?.data?.partyList) && response.data.partyList.length > 0) {
60
- const partyList = this.filterOraclePartyList({ response, params })
61
- await this.processOraclePartyList({ partyList, headers, params, destination })
44
+ await this.forwardRequestToDestination()
62
45
  return
63
46
  }
64
47
 
65
- this.log.info('empty partyList form oracle, getting proxyList...', { params })
66
- const proxyNames = await this.getFilteredProxyList(proxy)
48
+ const response = await this.sendOracleDiscoveryRequest()
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
67
52
 
68
- if (proxyNames.length) {
69
- await this.triggerSendToProxiesFlow({ proxyNames, headers, params, source })
70
- return
53
+ const fspiopError = await this.triggerInterSchemeDiscoveryFlow(this.inputs.headers)
54
+ if (fspiopError) {
55
+ this.state.fspiopError = fspiopError // todo: think, if we need this
71
56
  }
72
-
73
- results.fspiopError = await this.sendPartyNotFoundErrorCallback({ requester, headers, params })
74
57
  }
75
58
 
76
- async validateRequester ({ source, proxy }) {
77
- this.deps.stepState.inProgress('validateRequester-0')
59
+ async validateRequester () {
60
+ const { source, proxy, proxyEnabled } = this.state
78
61
  const log = this.log.child({ source, proxy, method: 'validateRequester' })
62
+ this.stepInProgress('validateRequester-0')
79
63
 
80
- const sourceParticipant = await this.validateParticipant(source)
81
- if (sourceParticipant) {
82
- log.debug('source is in scheme')
64
+ const schemeSource = await this.validateParticipant(source)
65
+ if (schemeSource) {
66
+ log.debug('source participant is in scheme')
83
67
  return source
84
68
  }
85
69
 
86
- if (!this.proxyEnabled || !proxy) {
87
- const errMessage = ERROR_MESSAGES.sourceFspNotFound
88
- log.warn(errMessage)
89
- throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
70
+ if (!proxyEnabled || !proxy) {
71
+ throw super.createFspiopIdNotFoundError(ERROR_MESSAGES.sourceFspNotFound, log)
90
72
  }
91
73
 
92
- const proxyParticipant = await this.validateParticipant(proxy)
93
- if (!proxyParticipant) {
94
- const errMessage = ERROR_MESSAGES.partyProxyNotFound
95
- log.warn(errMessage)
96
- throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
74
+ const schemeProxy = await this.validateParticipant(proxy)
75
+ if (!schemeProxy) {
76
+ throw super.createFspiopIdNotFoundError(ERROR_MESSAGES.partyProxyNotFound, log)
97
77
  }
98
78
 
99
79
  const isCached = await this.deps.proxyCache.addDfspIdToProxyMapping(source, proxy)
100
80
  if (!isCached) {
101
- const errMessage = 'failed to addDfspIdToProxyMapping'
102
- log.warn(errMessage)
103
- throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
81
+ throw super.createFspiopIdNotFoundError('failed to addDfspIdToProxyMapping', log)
104
82
  }
105
83
 
106
- log.info('source is added to proxyMapping cache:', { proxy, isCached })
84
+ log.info('source is added to proxyMapping cache')
107
85
  return proxy
108
86
  }
109
87
 
110
- async forwardRequestToDestination ({ destination, headers, params }) {
111
- this.deps.stepState.inProgress('validateDestination-1')
88
+ async forwardRequestToDestination () {
89
+ const { headers, params } = this.inputs
90
+ const { destination } = this.state
112
91
  const log = this.log.child({ method: 'forwardRequestToDestination' })
113
92
  let sendTo = destination
114
93
 
115
- const destParticipantModel = await this.validateParticipant(destination)
116
- if (!destParticipantModel) {
117
- this.deps.stepState.inProgress('lookupProxyDestination-2')
118
- const proxyId = this.proxyEnabled && await this.deps.proxyCache.lookupProxyByDfspId(destination)
94
+ const schemeParticipant = await this.validateParticipant(destination)
95
+ if (!schemeParticipant) {
96
+ this.stepInProgress('lookupProxyDestination-2')
97
+ const proxyId = this.state.proxyEnabled && await this.deps.proxyCache.lookupProxyByDfspId(destination)
119
98
 
120
99
  if (!proxyId) {
121
- log.warn('no destination participant, and no dfsp-to-proxy mapping', { destination })
122
- const errMessage = ERROR_MESSAGES.partyDestinationFspNotFound
123
- throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
100
+ log.warn('destination participant is not in scheme, and no dfsp-to-proxy mapping', { destination })
101
+ await super.sendDeleteOracleRequest(headers, params)
102
+ await this.triggerInterSchemeDiscoveryFlow(GetPartiesService.headersWithoutDestination(headers))
103
+ return
124
104
  }
125
105
  sendTo = proxyId
126
106
  }
127
- // all ok, go ahead and forward the request
107
+
128
108
  await this.#forwardGetPartiesRequest({ sendTo, headers, params })
129
109
  log.info('discovery getPartiesByTypeAndID request was sent', { sendTo })
130
110
  }
131
111
 
132
- filterOraclePartyList ({ response, params }) {
133
- // Oracle's API is a standard rest-style end-point Thus a GET /party on the oracle will return all participant-party records. We must filter the results based on the callbackEndpointType to make sure we remove records containing partySubIdOrType when we are in FSPIOP_CALLBACK_URL_PARTIES_GET mode:
134
- this.deps.stepState.inProgress('filterOraclePartyList-5')
112
+ async sendOracleDiscoveryRequest () {
113
+ this.stepInProgress('#sendOracleDiscoveryRequest')
114
+ const { headers, params, query } = this.inputs
115
+ return this.deps.oracle.oracleRequest(headers, RestMethods.GET, params, query, undefined, this.deps.cache)
116
+ }
117
+
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
+
124
+ this.stepInProgress('processOraclePartyList')
125
+ const partyList = this.#filterOraclePartyList(response)
126
+
127
+ let sentCount = 0
128
+ await Promise.all(partyList.map(async party => {
129
+ const isSent = await this.#processSingleOracleParty(party)
130
+ if (isSent) sentCount++
131
+ }))
132
+
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
137
+ }
138
+
139
+ async triggerInterSchemeDiscoveryFlow (headers) {
140
+ const { params } = this.inputs
141
+ const { proxy, source } = this.state
142
+ const log = this.log.child({ source })
143
+ log.verbose('triggerInterSchemeDiscoveryFlow start...', { proxy })
144
+
145
+ const proxyNames = await this.#getFilteredProxyList(proxy)
146
+ if (!proxyNames.length) {
147
+ return this.#sendPartyNotFoundErrorCallback(headers)
148
+ }
149
+
150
+ const alsReq = await this.#setProxyListToCache(proxyNames, source, params)
151
+ const { sentList, wasLast } = await this.#sendOutProxyRequests({ proxyNames, alsReq, headers, params })
152
+ if (sentList.length === 0) {
153
+ throw super.createFspiopIdNotFoundError(ERROR_MESSAGES.proxyConnectionError, log)
154
+ }
155
+ if (wasLast) {
156
+ throw super.createFspiopIdNotFoundError(ERROR_MESSAGES.noSuccessfulProxyDiscoveryResponses, log)
157
+ }
158
+
159
+ log.info('triggerInterSchemeDiscoveryFlow is done:', { sentList, alsReq })
160
+ return sentList
161
+ }
162
+
163
+ isLocalSource () {
164
+ const isLocal = this.state.source === this.state.requester
165
+ this.log.debug(`isLocalSource: ${isLocal}`)
166
+ return isLocal
167
+ }
168
+
169
+ #filterOraclePartyList (response) {
170
+ // Oracle's API is a standard rest-style end-point Thus a GET /party on the oracle will return all participant-party records.
171
+ // We must filter the results based on the callbackEndpointType to make sure we remove records containing partySubIdOrType when we are in FSPIOP_CALLBACK_URL_PARTIES_GET mode:
172
+ this.stepInProgress('filterOraclePartyList')
173
+ const { params } = this.inputs
135
174
  const callbackEndpointType = this.deps.partiesUtils.getPartyCbType(params.SubId)
136
- let filteredResponsePartyList
175
+ let filteredPartyList
137
176
 
138
177
  switch (callbackEndpointType) {
139
178
  case FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_GET:
140
- filteredResponsePartyList = response.data.partyList.filter(party => party.partySubIdOrType == null) // Filter records that DON'T contain a partySubIdOrType
179
+ filteredPartyList = response.data.partyList.filter(party => party.partySubIdOrType == null) // Filter records that DON'T contain a partySubIdOrType
141
180
  break
142
181
  case FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_GET:
143
- filteredResponsePartyList = response.data.partyList.filter(party => party.partySubIdOrType === params.SubId) // Filter records that match partySubIdOrType
182
+ filteredPartyList = response.data.partyList.filter(party => party.partySubIdOrType === params.SubId) // Filter records that match partySubIdOrType
144
183
  break
145
184
  default:
146
- filteredResponsePartyList = response // Fallback to providing the standard list
185
+ filteredPartyList = response // Fallback to providing the standard list
147
186
  }
148
187
 
149
- if (!Array.isArray(filteredResponsePartyList) || !filteredResponsePartyList.length) {
150
- const errMessage = 'Requested FSP/Party not found'
151
- this.log.warn(errMessage)
152
- throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
188
+ if (!Array.isArray(filteredPartyList) || !filteredPartyList.length) {
189
+ throw super.createFspiopIdNotFoundError(ERROR_MESSAGES.emptyFilteredPartyList)
153
190
  }
154
191
 
155
- return filteredResponsePartyList
192
+ return filteredPartyList
156
193
  }
157
194
 
158
- async processOraclePartyList ({ partyList, headers, params, destination }) {
159
- const log = this.log.child({ method: 'processOraclePartyList' })
195
+ /** @returns {Promise<boolean>} - is request forwarded to participant */
196
+ async #processSingleOracleParty (party) {
197
+ const { headers, params } = this.inputs
198
+ const { fspId } = party
199
+
200
+ const schemeParticipant = await this.validateParticipant(fspId)
201
+ if (schemeParticipant) {
202
+ this.log.info('participant is in scheme, so forwarding to it...', { fspId })
203
+ await this.#forwardGetPartiesRequest({
204
+ sendTo: fspId,
205
+ headers: GetPartiesService.overrideDestinationHeader(headers, fspId),
206
+ params
207
+ })
208
+ return true
209
+ }
160
210
 
161
- let sentCount = 0 // if sentCount === 0 after sending, should we restart the whole process?
162
- const sending = partyList.map(async party => {
163
- const { fspId } = party
164
- const clonedHeaders = { ...headers }
165
- if (!destination) {
166
- clonedHeaders[Headers.FSPIOP.DESTINATION] = fspId
211
+ if (this.state.proxyEnabled) {
212
+ const proxyName = await this.deps.proxyCache.lookupProxyByDfspId(fspId)
213
+ if (!proxyName) {
214
+ this.log.warn('no proxyMapping for external DFSP! Deleting reference in oracle...', { fspId })
215
+ await super.sendDeleteOracleRequest(headers, params)
216
+ // todo: check if it won't delete all parties
217
+ return false
167
218
  }
168
- this.deps.stepState.inProgress('validateParticipant-6')
169
- const schemeParticipant = await this.validateParticipant(fspId)
170
- if (schemeParticipant) {
171
- sentCount++
172
- log.info('participant is in scheme', { fspId })
173
- return this.#forwardGetPartiesRequest({
174
- sendTo: fspId,
175
- headers: clonedHeaders,
219
+
220
+ // Coz there's no destination header, it means we're inside initial inter-scheme discovery phase.
221
+ // So we should proceed only if source is in scheme (local participant)
222
+ const isLocalSource = this.isLocalSource()
223
+ if (isLocalSource) {
224
+ this.log.info('participant is NOT in scheme, but source is. So forwarding to proxy...', { fspId, proxyName })
225
+ await this.#forwardGetPartiesRequest({ // todo: add timeout if sendTo is proxy
226
+ sendTo: proxyName,
227
+ headers: GetPartiesService.overrideDestinationHeader(headers, fspId),
176
228
  params
177
229
  })
230
+ return true
178
231
  }
232
+ }
233
+ return false
234
+ }
179
235
 
180
- // If the participant is not in the scheme and proxy routing is enabled,
181
- // we should check if there is a proxy for it and send the request to the proxy
182
- if (this.proxyEnabled) {
183
- this.deps.stepState.inProgress('lookupProxyByDfspId-7')
184
- const proxyName = await this.deps.proxyCache.lookupProxyByDfspId(fspId)
185
- if (!proxyName) {
186
- log.warn('no proxyMapping for participant! TODO: Delete reference in oracle...', { fspId })
187
- // todo: delete reference in oracle
188
- } else {
189
- sentCount++
190
- log.info('participant is NOT in scheme, use proxy name', { fspId, proxyName })
191
- return this.#forwardGetPartiesRequest({
192
- sendTo: proxyName,
193
- headers: clonedHeaders,
194
- params
195
- })
196
- }
197
- }
236
+ async #sendPartyNotFoundErrorCallback (headers) {
237
+ const { params } = this.inputs
238
+ const callbackHeaders = GetPartiesService.createErrorCallbackHeaders(headers, params)
239
+ const fspiopError = super.createFspiopIdNotFoundError('No proxy found to start inter-scheme discovery flow')
240
+ const errorInfo = await this.deps.partiesUtils.makePutPartiesErrorPayload(
241
+ this.deps.config, fspiopError, callbackHeaders, params
242
+ )
243
+
244
+ await this.sendErrorCallback({
245
+ errorInfo,
246
+ headers: callbackHeaders,
247
+ params
198
248
  })
199
- await Promise.all(sending)
200
- log.verbose('processOraclePartyList is done', { sentCount })
201
- // todo: think what if sentCount === 0 here
249
+ return fspiopError
202
250
  }
203
251
 
204
- async getFilteredProxyList (proxy) {
205
- this.deps.stepState.inProgress('getAllProxies-8')
206
- if (!this.proxyEnabled) {
252
+ async #forwardGetPartiesRequest ({ sendTo, headers, params }) {
253
+ this.stepInProgress('#forwardGetPartiesRequest')
254
+ const callbackEndpointType = this.deps.partiesUtils.getPartyCbType(params.SubId)
255
+ const options = this.deps.partiesUtils.partiesRequestOptionsDto(params)
256
+
257
+ const sentResult = await this.deps.participant.sendRequest(
258
+ headers, sendTo, callbackEndpointType, RestMethods.GET, undefined, options, this.deps.childSpan
259
+ )
260
+ await this.#setProxyGetPartiesTimeout(sendTo)
261
+ this.log.debug('#forwardGetPartiesRequest is done:', { sendTo, sentResult })
262
+ return sentResult
263
+ }
264
+
265
+ async #sendOutProxyRequests ({ proxyNames, alsReq, headers, params }) {
266
+ this.stepInProgress('#sendOutProxyRequests')
267
+ const sentList = []
268
+ let wasLast = false // if any failed proxy request was last
269
+
270
+ const sendProxyRequest = (sendTo) => this.#forwardGetPartiesRequest({ sendTo, headers, params })
271
+ .then(() => { sentList.push(sendTo) })
272
+ .catch(err => {
273
+ this.log.error(`error in sending request to proxy ${sendTo}: `, err)
274
+ this.log.verbose(`removing proxy ${sendTo} from proxyCache...`)
275
+ return this.deps.proxyCache.receivedErrorResponse(alsReq, sendTo)
276
+ })
277
+ .then((isLast) => { wasLast = isLast })
278
+ .catch(err => {
279
+ this.log.error(`failed to remove proxy ${sendTo} from proxyCache: `, err)
280
+ })
281
+ await Promise.all(proxyNames.map(sendProxyRequest))
282
+
283
+ this.log.verbose('#sendOutProxyRequests is done:', { sentList, wasLast, proxyNames })
284
+ return { sentList, wasLast }
285
+ }
286
+
287
+ async #getFilteredProxyList (proxy) {
288
+ this.stepInProgress('#getFilteredProxyList')
289
+ if (!this.state.proxyEnabled) {
207
290
  this.log.warn('proxyCache is not enabled')
208
291
  return []
209
292
  }
@@ -213,68 +296,30 @@ class GetPartiesService extends BasePartiesService {
213
296
  return proxyNames.filter(name => name !== proxy)
214
297
  }
215
298
 
216
- async triggerSendToProxiesFlow ({ proxyNames, headers, params, source }) {
217
- const log = this.log.child({ method: 'triggerSendToProxiesFlow' })
218
- this.deps.stepState.inProgress('setSendToProxiesList-10')
299
+ async #setProxyListToCache (proxyNames, source, params) {
300
+ this.stepInProgress('#setProxyListToCache')
219
301
  const alsReq = this.deps.partiesUtils.alsRequestDto(source, params)
220
- log.info('starting setSendToProxiesList flow: ', { proxyNames, alsReq, proxyCacheTtlSec })
221
302
 
222
303
  const isCached = await this.deps.proxyCache.setSendToProxiesList(alsReq, proxyNames, proxyCacheTtlSec)
223
304
  if (!isCached) {
224
- log.warn('failed to setSendToProxiesList')
225
- throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, ERROR_MESSAGES.failedToCacheSendToProxiesList)
226
- }
227
-
228
- this.deps.stepState.inProgress('sendingProxyRequests-11')
229
- const sending = proxyNames.map(
230
- sendTo => this.#forwardGetPartiesRequest({ sendTo, headers, params })
231
- .then(({ status, data } = {}) => ({ status, data }))
232
- )
233
- const results = await Promise.allSettled(sending)
234
- const isOk = results.some(result => result.status === 'fulfilled')
235
- // If, at least, one request is sent to proxy, we treat the whole flow as successful.
236
- // Failed requests should be handled by TTL expired/timeout handler
237
- // todo: - think, if we should handle failed requests here (e.g., by calling receivedErrorResponse)
238
- log.info('triggerSendToProxiesFlow is done:', { isOk, results, proxyNames, alsReq })
239
- this.deps.stepState.inProgress('allSent-12')
240
- if (!isOk) {
241
- log.warn('no successful requests sent to proxies')
242
- throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, ERROR_MESSAGES.proxyConnectionError)
305
+ throw super.createFspiopIdNotFoundError(ERROR_MESSAGES.failedToCacheSendToProxiesList)
243
306
  }
307
+ this.log.verbose('#setProxyListToCache is done: ', { alsReq, proxyNames, proxyCacheTtlSec })
308
+ return alsReq
244
309
  }
245
310
 
246
- async sendPartyNotFoundErrorCallback ({ requester, headers, params }) {
247
- this.deps.stepState.inProgress('sendErrorCallback-9')
248
- const callbackHeaders = createCallbackHeaders({
249
- requestHeaders: headers,
250
- partyIdType: params.Type,
251
- partyIdentifier: params.ID,
252
- endpointTemplate: params.SubId
253
- ? FspEndpointTemplates.PARTIES_SUB_ID_PUT_ERROR
254
- : FspEndpointTemplates.PARTIES_PUT_ERROR
255
- })
256
- const fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.PARTY_NOT_FOUND)
257
-
258
- await this.sendErrorCallback({
259
- sendTo: requester,
260
- errorInfo: fspiopError.toApiErrorObject(this.deps.config.ERROR_HANDLING),
261
- headers: callbackHeaders,
262
- params
263
- })
264
- return fspiopError
265
- }
266
-
267
- async sendOracleDiscoveryRequest ({ headers, params, query }) {
268
- this.deps.stepState.inProgress('oracleRequest-4')
269
- return this.deps.oracle.oracleRequest(headers, RestMethods.GET, params, query, undefined, this.deps.cache)
270
- }
271
-
272
- async #forwardGetPartiesRequest ({ sendTo, headers, params }) {
273
- this.deps.stepState.inProgress('forwardRequest-3')
274
- const callbackEndpointType = this.deps.partiesUtils.getPartyCbType(params.SubId)
275
- const options = this.deps.partiesUtils.partiesRequestOptionsDto(params)
276
-
277
- return this.deps.participant.sendRequest(headers, sendTo, callbackEndpointType, RestMethods.GET, undefined, options, this.deps.childSpan)
311
+ async #setProxyGetPartiesTimeout (sendTo) {
312
+ const isLocalSource = this.isLocalSource()
313
+ const isSentToProxy = this.state.destination !== sendTo
314
+ this.log.verbose('isLocalSource and isSentToProxy: ', { isLocalSource, isSentToProxy, sendTo })
315
+
316
+ if (isSentToProxy && isLocalSource) {
317
+ this.stepInProgress('#setProxyGetPartiesTimeout')
318
+ const alsReq = this.deps.partiesUtils.alsRequestDto(this.state.source, this.inputs.params)
319
+ const isSet = await this.deps.proxyCache.setProxyGetPartiesTimeout(alsReq, sendTo)
320
+ this.log.info('#setProxyGetPartiesTimeout is done', { isSet })
321
+ return isSet
322
+ }
278
323
  }
279
324
  }
280
325
 
@@ -30,49 +30,73 @@ const { ERROR_MESSAGES } = require('../../../constants')
30
30
  const BasePartiesService = require('./BasePartiesService')
31
31
 
32
32
  class PutPartiesErrorService extends BasePartiesService {
33
- // async handleRequest () {
34
- // // todo: add impl.
35
- // }
36
-
37
- async checkPayee ({ headers, params, payload, proxy }) {
38
- const notValid = this.deps.partiesUtils.isNotValidPayeeCase(payload)
39
- if (notValid) {
40
- this.deps.stepState.inProgress('notValidPayeeCase-1')
41
- this.log.warn('notValidPayee case - deleting Participants and run getPartiesByTypeAndID', { proxy, payload })
42
- const swappedHeaders = this.deps.partiesUtils.swapSourceDestinationHeaders(headers)
43
- await super.sendDeleteOracleRequest(swappedHeaders, params)
33
+ /** @returns {Promise<true | undefined>} - If true, need to trigger inter-scheme discovery. */
34
+ async handleRequest () {
35
+ if (this.state.proxyEnabled && this.state.proxy) {
36
+ const alsReq = this.deps.partiesUtils.alsRequestDto(this.state.destination, this.inputs.params) // or source?
37
+ const isPending = await this.deps.proxyCache.isPendingCallback(alsReq)
38
+
39
+ if (!isPending) {
40
+ // not initial inter-scheme discovery case. Cleanup oracle and trigger inter-scheme discovery
41
+ this.log.warn('Need to cleanup oracle and trigger new inter-scheme discovery flow')
42
+ await this.cleanupOracle()
43
+ await this.removeProxyGetPartiesTimeoutCache(alsReq)
44
+ return true // need to trigger inter-scheme discovery
45
+ }
46
+
47
+ const isLast = await this.checkLastProxyCallback(alsReq)
48
+ if (!isLast) {
49
+ this.log.verbose('putPartiesErrorByTypeAndID proxy callback was processed')
50
+ return
51
+ }
44
52
  }
45
- return notValid
53
+
54
+ await this.identifyDestinationForErrorCallback()
55
+ await this.sendErrorCallbackToParticipant()
56
+ this.log.info('putPartiesByTypeAndID is done')
46
57
  }
47
58
 
48
- async checkLastProxyCallback ({ destination, proxy, params }) {
49
- this.deps.stepState.inProgress('checkLastProxyCallback-2')
50
- const alsReq = this.deps.partiesUtils.alsRequestDto(destination, params) // or source?
59
+ async cleanupOracle () {
60
+ this.stepInProgress('cleanupOracle')
61
+ const { headers, params, payload } = this.inputs
62
+ this.log.info('cleanupOracle due to error callback...', { payload })
63
+ const swappedHeaders = this.deps.partiesUtils.swapSourceDestinationHeaders(headers)
64
+ await super.sendDeleteOracleRequest(swappedHeaders, params)
65
+ }
66
+
67
+ async checkLastProxyCallback (alsReq) {
68
+ this.stepInProgress('checkLastProxyCallback')
69
+ const { proxy } = this.state
51
70
  const isLast = await this.deps.proxyCache.receivedErrorResponse(alsReq, proxy)
52
- 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 })
53
72
  return isLast
54
73
  }
55
74
 
56
- async identifyDestinationForErrorCallback (destination) {
57
- this.deps.stepState.inProgress('validateParticipant-3')
58
- const destinationParticipant = await super.validateParticipant(destination)
59
- if (destinationParticipant) return destination
75
+ async identifyDestinationForErrorCallback () {
76
+ this.stepInProgress('identifyDestinationForErrorCallback')
77
+ const { destination } = this.state
78
+ const schemeParticipant = await super.validateParticipant(destination)
79
+ if (schemeParticipant) {
80
+ this.state.requester = destination
81
+ return
82
+ }
60
83
 
61
- this.deps.stepState.inProgress('lookupProxyDestination-4')
62
- const proxyName = this.proxyEnabled && await this.deps.proxyCache.lookupProxyByDfspId(destination)
63
- if (proxyName) return proxyName
84
+ this.stepInProgress('lookupProxyDestination-4')
85
+ const proxyName = this.state.proxyEnabled && await this.deps.proxyCache.lookupProxyByDfspId(destination)
86
+ if (proxyName) {
87
+ this.state.requester = proxyName
88
+ return
89
+ }
64
90
 
65
91
  const errMessage = ERROR_MESSAGES.partyDestinationFspNotFound
66
92
  this.log.warn(errMessage, { destination })
67
93
  throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.DESTINATION_FSP_ERROR, errMessage)
68
94
  }
69
95
 
70
- async sendErrorCallbackToParticipant ({ sendTo, headers, params, dataUri }) {
71
- this.deps.stepState.inProgress('sendErrorToParticipant-5')
96
+ async sendErrorCallbackToParticipant () {
97
+ const { headers, params, dataUri } = this.inputs
72
98
  const errorInfo = PutPartiesErrorService.decodeDataUriPayload(dataUri)
73
- return super.sendErrorCallback({
74
- sendTo, errorInfo, headers, params
75
- })
99
+ return super.sendErrorCallback({ errorInfo, headers, params })
76
100
  }
77
101
  }
78
102