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
@@ -25,185 +25,270 @@
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')
83
- return source
64
+ if (!(proxyEnabled && proxy)) {
65
+ const schemeSource = await this.validateParticipant(source)
66
+ if (schemeSource) {
67
+ log.debug('source participant is in scheme')
68
+ return source
69
+ }
84
70
  }
85
71
 
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)
72
+ if (!proxyEnabled || !proxy) {
73
+ throw super.createFspiopIdNotFoundError(ERROR_MESSAGES.sourceFspNotFound, log)
90
74
  }
91
75
 
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)
76
+ const schemeProxy = await this.validateParticipant(proxy)
77
+ if (!schemeProxy) {
78
+ throw super.createFspiopIdNotFoundError(ERROR_MESSAGES.partyProxyNotFound, log)
97
79
  }
98
80
 
99
81
  const isCached = await this.deps.proxyCache.addDfspIdToProxyMapping(source, proxy)
100
82
  if (!isCached) {
101
- const errMessage = 'failed to addDfspIdToProxyMapping'
102
- log.warn(errMessage)
103
- throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
83
+ throw super.createFspiopIdNotFoundError('failed to addDfspIdToProxyMapping', log)
104
84
  }
105
85
 
106
- log.info('source is added to proxyMapping cache:', { proxy, isCached })
86
+ log.info('source is added to proxyMapping cache')
107
87
  return proxy
108
88
  }
109
89
 
110
- async forwardRequestToDestination ({ destination, headers, params }) {
111
- this.deps.stepState.inProgress('validateDestination-1')
90
+ async forwardRequestToDestination () {
91
+ const { headers, params } = this.inputs
92
+ const { destination } = this.state
112
93
  const log = this.log.child({ method: 'forwardRequestToDestination' })
113
94
  let sendTo = destination
114
95
 
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)
96
+ const schemeParticipant = await this.validateParticipant(destination)
97
+ if (!schemeParticipant) {
98
+ this.stepInProgress('lookupProxyDestination-2')
99
+ const proxyId = this.state.proxyEnabled && await this.deps.proxyCache.lookupProxyByDfspId(destination)
119
100
 
120
101
  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)
102
+ log.warn('destination participant is not in scheme, and no dfsp-to-proxy mapping', { destination })
103
+ await super.sendDeleteOracleRequest(headers, params)
104
+ await this.triggerInterSchemeDiscoveryFlow(GetPartiesService.headersWithoutDestination(headers))
105
+ return
124
106
  }
125
107
  sendTo = proxyId
126
108
  }
127
- // all ok, go ahead and forward the request
109
+
128
110
  await this.#forwardGetPartiesRequest({ sendTo, headers, params })
129
111
  log.info('discovery getPartiesByTypeAndID request was sent', { sendTo })
130
112
  }
131
113
 
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')
114
+ async sendOracleDiscoveryRequest () {
115
+ this.stepInProgress('#sendOracleDiscoveryRequest')
116
+ const { headers, params, query } = this.inputs
117
+ return this.deps.oracle.oracleRequest(headers, RestMethods.GET, params, query, undefined, this.deps.cache)
118
+ }
119
+
120
+ async processOraclePartyListResponse (response) {
121
+ if (!Array.isArray(response?.data?.partyList) || response.data.partyList.length === 0) {
122
+ this.log.verbose('oracle partyList is empty')
123
+ return false
124
+ }
125
+
126
+ this.stepInProgress('processOraclePartyList')
127
+ const partyList = this.#filterOraclePartyList(response)
128
+
129
+ let sentCount = 0
130
+ await Promise.all(partyList.map(async party => {
131
+ const isSent = await this.#processSingleOracleParty(party)
132
+ if (isSent) sentCount++
133
+ }))
134
+
135
+ const isSent = sentCount > 0
136
+ this.log.verbose('processOraclePartyList is done', { isSent, sentCount })
137
+ // if NOT isSent, need to trigger interScheme discovery flow
138
+ return isSent
139
+ }
140
+
141
+ async triggerInterSchemeDiscoveryFlow (headers) {
142
+ const { params } = this.inputs
143
+ const { proxy, source } = this.state
144
+ const log = this.log.child({ source })
145
+ log.verbose('triggerInterSchemeDiscoveryFlow start...', { proxy })
146
+
147
+ const proxyNames = await this.#getFilteredProxyList(proxy)
148
+ if (!proxyNames.length) {
149
+ return this.#sendPartyNotFoundErrorCallback(headers)
150
+ }
151
+
152
+ const alsReq = await this.#setProxyListToCache(proxyNames, source, params)
153
+ const { sentList, wasLast } = await this.#sendOutProxyRequests({ proxyNames, alsReq, headers, params })
154
+ if (sentList.length === 0) {
155
+ throw super.createFspiopIdNotFoundError(ERROR_MESSAGES.proxyConnectionError, log)
156
+ }
157
+ if (wasLast) {
158
+ throw super.createFspiopIdNotFoundError(ERROR_MESSAGES.noSuccessfulProxyDiscoveryResponses, log)
159
+ }
160
+
161
+ log.info('triggerInterSchemeDiscoveryFlow is done:', { sentList, alsReq })
162
+ return sentList
163
+ }
164
+
165
+ isLocalSource () {
166
+ const isLocal = this.state.source === this.state.requester
167
+ this.log.debug(`isLocalSource: ${isLocal}`)
168
+ return isLocal
169
+ }
170
+
171
+ #filterOraclePartyList (response) {
172
+ // Oracle's API is a standard rest-style end-point Thus a GET /party on the oracle will return all participant-party records.
173
+ // 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:
174
+ this.stepInProgress('filterOraclePartyList')
175
+ const { params } = this.inputs
135
176
  const callbackEndpointType = this.deps.partiesUtils.getPartyCbType(params.SubId)
136
- let filteredResponsePartyList
177
+ let filteredPartyList
137
178
 
138
179
  switch (callbackEndpointType) {
139
180
  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
181
+ filteredPartyList = response.data.partyList.filter(party => party.partySubIdOrType == null) // Filter records that DON'T contain a partySubIdOrType
141
182
  break
142
183
  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
184
+ filteredPartyList = response.data.partyList.filter(party => party.partySubIdOrType === params.SubId) // Filter records that match partySubIdOrType
144
185
  break
145
186
  default:
146
- filteredResponsePartyList = response // Fallback to providing the standard list
187
+ filteredPartyList = response // Fallback to providing the standard list
147
188
  }
148
189
 
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)
190
+ if (!Array.isArray(filteredPartyList) || !filteredPartyList.length) {
191
+ throw super.createFspiopIdNotFoundError(ERROR_MESSAGES.emptyFilteredPartyList)
153
192
  }
154
193
 
155
- return filteredResponsePartyList
194
+ return filteredPartyList
156
195
  }
157
196
 
158
- async processOraclePartyList ({ partyList, headers, params, destination }) {
159
- const log = this.log.child({ method: 'processOraclePartyList' })
197
+ /** @returns {Promise<boolean>} - is request forwarded to participant */
198
+ async #processSingleOracleParty (party) {
199
+ const { headers, params } = this.inputs
200
+ const { fspId } = party
201
+
202
+ const schemeParticipant = await this.validateParticipant(fspId)
203
+ if (schemeParticipant) {
204
+ this.log.info('participant is in scheme, so forwarding to it...', { fspId })
205
+ await this.#forwardGetPartiesRequest({
206
+ sendTo: fspId,
207
+ headers: GetPartiesService.overrideDestinationHeader(headers, fspId),
208
+ params
209
+ })
210
+ return true
211
+ }
160
212
 
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
213
+ if (this.state.proxyEnabled) {
214
+ const proxyName = await this.deps.proxyCache.lookupProxyByDfspId(fspId)
215
+ if (!proxyName) {
216
+ this.log.warn('no proxyMapping for external DFSP! Deleting reference in oracle...', { fspId })
217
+ await super.sendDeleteOracleRequest(headers, params)
218
+ // todo: check if it won't delete all parties
219
+ return false
167
220
  }
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,
221
+
222
+ // Coz there's no destination header, it means we're inside initial inter-scheme discovery phase.
223
+ // So we should proceed only if source is in scheme (local participant)
224
+ const isLocalSource = this.isLocalSource()
225
+ if (isLocalSource) {
226
+ this.log.info('participant is NOT in scheme, but source is. So forwarding to proxy...', { fspId, proxyName })
227
+ await this.#forwardGetPartiesRequest({ // todo: add timeout if sendTo is proxy
228
+ sendTo: proxyName,
229
+ headers: GetPartiesService.overrideDestinationHeader(headers, fspId),
176
230
  params
177
231
  })
232
+ return true
178
233
  }
234
+ }
235
+ return false
236
+ }
179
237
 
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
- }
238
+ async #sendPartyNotFoundErrorCallback (headers) {
239
+ const { params } = this.inputs
240
+ const callbackHeaders = GetPartiesService.createErrorCallbackHeaders(headers, params)
241
+ const fspiopError = super.createFspiopIdNotFoundError('No proxy found to start inter-scheme discovery flow')
242
+ const errorInfo = await this.deps.partiesUtils.makePutPartiesErrorPayload(
243
+ this.deps.config, fspiopError, callbackHeaders, params
244
+ )
245
+
246
+ await this.sendErrorCallback({
247
+ errorInfo,
248
+ headers: callbackHeaders,
249
+ params
198
250
  })
199
- await Promise.all(sending)
200
- log.verbose('processOraclePartyList is done', { sentCount })
201
- // todo: think what if sentCount === 0 here
251
+ return fspiopError
202
252
  }
203
253
 
204
- async getFilteredProxyList (proxy) {
205
- this.deps.stepState.inProgress('getAllProxies-8')
206
- if (!this.proxyEnabled) {
254
+ async #forwardGetPartiesRequest ({ sendTo, headers, params }) {
255
+ this.stepInProgress('#forwardGetPartiesRequest')
256
+ const callbackEndpointType = this.deps.partiesUtils.getPartyCbType(params.SubId)
257
+ const options = this.deps.partiesUtils.partiesRequestOptionsDto(params)
258
+
259
+ const sentResult = await this.deps.participant.sendRequest(
260
+ headers, sendTo, callbackEndpointType, RestMethods.GET, undefined, options, this.deps.childSpan
261
+ )
262
+ await this.#setProxyGetPartiesTimeout(sendTo)
263
+ this.log.debug('#forwardGetPartiesRequest is done:', { sendTo, sentResult })
264
+ return sentResult
265
+ }
266
+
267
+ async #sendOutProxyRequests ({ proxyNames, alsReq, headers, params }) {
268
+ this.stepInProgress('#sendOutProxyRequests')
269
+ const sentList = []
270
+ let wasLast = false // if any failed proxy request was last
271
+
272
+ const sendProxyRequest = (sendTo) => this.#forwardGetPartiesRequest({ sendTo, headers, params })
273
+ .then(() => { sentList.push(sendTo) })
274
+ .catch(err => {
275
+ this.log.error(`error in sending request to proxy ${sendTo}: `, err)
276
+ this.log.verbose(`removing proxy ${sendTo} from proxyCache...`)
277
+ return this.deps.proxyCache.receivedErrorResponse(alsReq, sendTo)
278
+ })
279
+ .then((isLast) => { wasLast = isLast })
280
+ .catch(err => {
281
+ this.log.error(`failed to remove proxy ${sendTo} from proxyCache: `, err)
282
+ })
283
+ await Promise.all(proxyNames.map(sendProxyRequest))
284
+
285
+ this.log.verbose('#sendOutProxyRequests is done:', { sentList, wasLast, proxyNames })
286
+ return { sentList, wasLast }
287
+ }
288
+
289
+ async #getFilteredProxyList (proxy) {
290
+ this.stepInProgress('#getFilteredProxyList')
291
+ if (!this.state.proxyEnabled) {
207
292
  this.log.warn('proxyCache is not enabled')
208
293
  return []
209
294
  }
@@ -213,68 +298,30 @@ class GetPartiesService extends BasePartiesService {
213
298
  return proxyNames.filter(name => name !== proxy)
214
299
  }
215
300
 
216
- async triggerSendToProxiesFlow ({ proxyNames, headers, params, source }) {
217
- const log = this.log.child({ method: 'triggerSendToProxiesFlow' })
218
- this.deps.stepState.inProgress('setSendToProxiesList-10')
301
+ async #setProxyListToCache (proxyNames, source, params) {
302
+ this.stepInProgress('#setProxyListToCache')
219
303
  const alsReq = this.deps.partiesUtils.alsRequestDto(source, params)
220
- log.info('starting setSendToProxiesList flow: ', { proxyNames, alsReq, proxyCacheTtlSec })
221
304
 
222
305
  const isCached = await this.deps.proxyCache.setSendToProxiesList(alsReq, proxyNames, proxyCacheTtlSec)
223
306
  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)
307
+ throw super.createFspiopIdNotFoundError(ERROR_MESSAGES.failedToCacheSendToProxiesList)
243
308
  }
309
+ this.log.verbose('#setProxyListToCache is done: ', { alsReq, proxyNames, proxyCacheTtlSec })
310
+ return alsReq
244
311
  }
245
312
 
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)
313
+ async #setProxyGetPartiesTimeout (sendTo) {
314
+ const isLocalSource = this.isLocalSource()
315
+ const isSentToProxy = this.state.destination !== sendTo
316
+ this.log.verbose('isLocalSource and isSentToProxy: ', { isLocalSource, isSentToProxy, sendTo })
317
+
318
+ if (isSentToProxy && isLocalSource) {
319
+ this.stepInProgress('#setProxyGetPartiesTimeout')
320
+ const alsReq = this.deps.partiesUtils.alsRequestDto(this.state.source, this.inputs.params)
321
+ const isSet = await this.deps.proxyCache.setProxyGetPartiesTimeout(alsReq, sendTo)
322
+ this.log.info('#setProxyGetPartiesTimeout is done', { isSet })
323
+ return isSet
324
+ }
278
325
  }
279
326
  }
280
327
 
@@ -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