account-lookup-service 17.6.0 → 17.7.0-snapshot.0
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 +12 -10
- package/src/constants.js +31 -2
- package/src/domain/parties/deps.js +3 -3
- package/src/domain/parties/getPartiesByTypeAndID.js +9 -13
- package/src/domain/parties/putParties.js +26 -71
- package/src/domain/parties/services/BasePartiesService.js +97 -15
- package/src/domain/parties/services/GetPartiesService.js +101 -117
- package/src/domain/parties/services/PutPartiesErrorService.js +49 -27
- package/src/domain/parties/services/PutPartiesService.js +40 -26
- package/test/fixtures/index.js +38 -0
- package/test/unit/domain/participants/participants.test.js +1 -1
- package/test/unit/domain/parties/parties.test.js +28 -20
- package/test/unit/domain/parties/services/GetPartiesService.test.js +142 -0
- package/test/unit/domain/parties/services/PutPartiesErrorService.test.js +53 -0
- package/test/unit/domain/parties/services/deps.js +40 -0
- package/test/util/index.js +5 -6
- package/test/util/mockDeps.js +48 -0
@@ -25,185 +25,170 @@
|
|
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 (
|
42
|
-
const 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
|
-
|
51
|
-
results.requester = requester
|
40
|
+
this.log.info('handling getParties request...', { source, destination, proxy })
|
41
|
+
this.state.requester = await this.validateRequester()
|
42
|
+
// dfsp in scheme OR proxy
|
52
43
|
|
53
44
|
if (destination) {
|
54
|
-
await this.forwardRequestToDestination(
|
45
|
+
await this.forwardRequestToDestination()
|
55
46
|
return
|
56
47
|
}
|
57
|
-
const response = await this.sendOracleDiscoveryRequest({ headers, params, query })
|
58
48
|
|
49
|
+
const response = await this.sendOracleDiscoveryRequest()
|
59
50
|
if (Array.isArray(response?.data?.partyList) && response.data.partyList.length > 0) {
|
60
|
-
const partyList = this.filterOraclePartyList(
|
61
|
-
await this.processOraclePartyList(
|
51
|
+
const partyList = this.filterOraclePartyList(response)
|
52
|
+
await this.processOraclePartyList(partyList)
|
62
53
|
return
|
63
54
|
}
|
64
55
|
|
65
|
-
this.log.info('empty partyList form oracle,
|
66
|
-
const
|
67
|
-
|
68
|
-
|
69
|
-
await this.triggerSendToProxiesFlow({ proxyNames, headers, params, source })
|
70
|
-
return
|
56
|
+
this.log.info('empty partyList form oracle, checking inter-scheme discovery flow...')
|
57
|
+
const fspiopError = await this.triggerInterSchemeDiscoveryFlow(this.inputs.headers)
|
58
|
+
if (fspiopError) {
|
59
|
+
this.state.fspiopError = fspiopError // todo: think, if we need this
|
71
60
|
}
|
72
|
-
|
73
|
-
results.fspiopError = await this.sendPartyNotFoundErrorCallback({ requester, headers, params })
|
74
61
|
}
|
75
62
|
|
76
|
-
async validateRequester (
|
77
|
-
this.
|
63
|
+
async validateRequester () {
|
64
|
+
const { source, proxy, proxyEnabled } = this.state
|
78
65
|
const log = this.log.child({ source, proxy, method: 'validateRequester' })
|
66
|
+
this.stepInProgress('validateRequester-0')
|
79
67
|
|
80
|
-
const
|
81
|
-
if (
|
82
|
-
log.debug('source is in scheme')
|
68
|
+
const schemeSource = await this.validateParticipant(source)
|
69
|
+
if (schemeSource) {
|
70
|
+
log.debug('source participant is in scheme')
|
83
71
|
return source
|
84
72
|
}
|
85
73
|
|
86
|
-
if (!
|
87
|
-
|
88
|
-
log.warn(errMessage)
|
89
|
-
throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
|
74
|
+
if (!proxyEnabled || !proxy) {
|
75
|
+
throw super.createFspiopIdNotFoundError(ERROR_MESSAGES.sourceFspNotFound, log)
|
90
76
|
}
|
91
77
|
|
92
|
-
const
|
93
|
-
if (!
|
94
|
-
|
95
|
-
log.warn(errMessage)
|
96
|
-
throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
|
78
|
+
const schemeProxy = await this.validateParticipant(proxy)
|
79
|
+
if (!schemeProxy) {
|
80
|
+
throw super.createFspiopIdNotFoundError(ERROR_MESSAGES.partyProxyNotFound, log)
|
97
81
|
}
|
98
82
|
|
99
83
|
const isCached = await this.deps.proxyCache.addDfspIdToProxyMapping(source, proxy)
|
100
84
|
if (!isCached) {
|
101
|
-
|
102
|
-
log.warn(errMessage)
|
103
|
-
throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
|
85
|
+
throw super.createFspiopIdNotFoundError('failed to addDfspIdToProxyMapping', log)
|
104
86
|
}
|
105
87
|
|
106
88
|
log.info('source is added to proxyMapping cache:', { proxy, isCached })
|
107
89
|
return proxy
|
108
90
|
}
|
109
91
|
|
110
|
-
async forwardRequestToDestination (
|
111
|
-
this.
|
92
|
+
async forwardRequestToDestination () {
|
93
|
+
const { headers, params } = this.inputs
|
94
|
+
const { destination } = this.state
|
112
95
|
const log = this.log.child({ method: 'forwardRequestToDestination' })
|
113
96
|
let sendTo = destination
|
114
97
|
|
115
|
-
const
|
116
|
-
if (!
|
117
|
-
this.
|
118
|
-
const proxyId = this.proxyEnabled && await this.deps.proxyCache.lookupProxyByDfspId(destination)
|
98
|
+
const schemeParticipant = await this.validateParticipant(destination)
|
99
|
+
if (!schemeParticipant) {
|
100
|
+
this.stepInProgress('lookupProxyDestination-2')
|
101
|
+
const proxyId = this.state.proxyEnabled && await this.deps.proxyCache.lookupProxyByDfspId(destination)
|
119
102
|
|
120
103
|
if (!proxyId) {
|
121
|
-
log.warn('
|
122
|
-
|
123
|
-
|
104
|
+
log.warn('destination participant is not in scheme, and no dfsp-to-proxy mapping', { destination })
|
105
|
+
await super.sendDeleteOracleRequest(headers, params)
|
106
|
+
await this.triggerInterSchemeDiscoveryFlow(GetPartiesService.headersWithoutDestination(headers))
|
107
|
+
return
|
124
108
|
}
|
125
109
|
sendTo = proxyId
|
126
110
|
}
|
127
|
-
|
111
|
+
|
128
112
|
await this.#forwardGetPartiesRequest({ sendTo, headers, params })
|
129
113
|
log.info('discovery getPartiesByTypeAndID request was sent', { sendTo })
|
130
114
|
}
|
131
115
|
|
132
|
-
filterOraclePartyList (
|
133
|
-
// Oracle's API is a standard rest-style end-point Thus a GET /party on the oracle will return all participant-party records.
|
134
|
-
|
116
|
+
filterOraclePartyList (response) {
|
117
|
+
// Oracle's API is a standard rest-style end-point Thus a GET /party on the oracle will return all participant-party records.
|
118
|
+
// 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:
|
119
|
+
this.stepInProgress('filterOraclePartyList-5')
|
120
|
+
const { params } = this.inputs
|
135
121
|
const callbackEndpointType = this.deps.partiesUtils.getPartyCbType(params.SubId)
|
136
|
-
let
|
122
|
+
let filteredPartyList
|
137
123
|
|
138
124
|
switch (callbackEndpointType) {
|
139
125
|
case FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_GET:
|
140
|
-
|
126
|
+
filteredPartyList = response.data.partyList.filter(party => party.partySubIdOrType == null) // Filter records that DON'T contain a partySubIdOrType
|
141
127
|
break
|
142
128
|
case FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_GET:
|
143
|
-
|
129
|
+
filteredPartyList = response.data.partyList.filter(party => party.partySubIdOrType === params.SubId) // Filter records that match partySubIdOrType
|
144
130
|
break
|
145
131
|
default:
|
146
|
-
|
132
|
+
filteredPartyList = response // Fallback to providing the standard list
|
147
133
|
}
|
148
134
|
|
149
|
-
if (!Array.isArray(
|
150
|
-
|
151
|
-
this.log.warn(errMessage)
|
152
|
-
throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
|
135
|
+
if (!Array.isArray(filteredPartyList) || !filteredPartyList.length) {
|
136
|
+
throw super.createFspiopIdNotFoundError(ERROR_MESSAGES.emptyFilteredPartyList)
|
153
137
|
}
|
154
138
|
|
155
|
-
return
|
139
|
+
return filteredPartyList
|
156
140
|
}
|
157
141
|
|
158
|
-
async processOraclePartyList (
|
159
|
-
|
142
|
+
async processOraclePartyList (partyList) {
|
143
|
+
this.stepInProgress('processOraclePartyList')
|
144
|
+
const { headers, params } = this.inputs
|
160
145
|
|
161
|
-
let sentCount = 0 // if sentCount === 0 after sending,
|
146
|
+
let sentCount = 0 // if sentCount === 0 after sending, we send idNotFound error
|
162
147
|
const sending = partyList.map(async party => {
|
163
148
|
const { fspId } = party
|
164
|
-
|
165
|
-
if (!destination) {
|
166
|
-
clonedHeaders[Headers.FSPIOP.DESTINATION] = fspId
|
167
|
-
}
|
168
|
-
this.deps.stepState.inProgress('validateParticipant-6')
|
149
|
+
|
169
150
|
const schemeParticipant = await this.validateParticipant(fspId)
|
170
151
|
if (schemeParticipant) {
|
171
152
|
sentCount++
|
172
|
-
log.info('participant is in scheme', { fspId })
|
153
|
+
this.log.info('participant is in scheme, so forwarding to it...', { fspId })
|
173
154
|
return this.#forwardGetPartiesRequest({
|
174
155
|
sendTo: fspId,
|
175
|
-
headers:
|
156
|
+
headers: GetPartiesService.overrideDestinationHeader(headers, fspId),
|
176
157
|
params
|
177
158
|
})
|
178
159
|
}
|
179
160
|
|
180
|
-
|
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')
|
161
|
+
if (this.state.proxyEnabled) {
|
184
162
|
const proxyName = await this.deps.proxyCache.lookupProxyByDfspId(fspId)
|
185
163
|
if (!proxyName) {
|
186
|
-
log.warn('no proxyMapping for
|
187
|
-
|
188
|
-
|
164
|
+
this.log.warn('no proxyMapping for external DFSP! Deleting reference in oracle...', { fspId })
|
165
|
+
return super.sendDeleteOracleRequest(headers, params)
|
166
|
+
// todo: check if it won't delete all parties
|
167
|
+
}
|
168
|
+
|
169
|
+
// Coz there's no destination header, it means we're inside initial inter-scheme discovery phase.
|
170
|
+
// So we should proceed only if source is in scheme (local participant)
|
171
|
+
const schemeSource = await this.validateParticipant(this.state.source)
|
172
|
+
if (schemeSource) {
|
189
173
|
sentCount++
|
190
|
-
log.info('participant is NOT in scheme,
|
174
|
+
this.log.info('participant is NOT in scheme, but source is. So forwarding to proxy...', { fspId, proxyName })
|
191
175
|
return this.#forwardGetPartiesRequest({
|
192
176
|
sendTo: proxyName,
|
193
|
-
headers:
|
177
|
+
headers: GetPartiesService.overrideDestinationHeader(headers, fspId),
|
194
178
|
params
|
195
179
|
})
|
196
180
|
}
|
197
181
|
}
|
198
182
|
})
|
199
183
|
await Promise.all(sending)
|
200
|
-
log.verbose('processOraclePartyList is done', { sentCount })
|
201
|
-
|
184
|
+
this.log.verbose('processOraclePartyList is done', { sentCount })
|
185
|
+
|
186
|
+
if (sentCount === 0) throw super.createFspiopIdNotFoundError(ERROR_MESSAGES.noDiscoveryRequestsForwarded)
|
202
187
|
}
|
203
188
|
|
204
189
|
async getFilteredProxyList (proxy) {
|
205
|
-
this.
|
206
|
-
if (!this.proxyEnabled) {
|
190
|
+
this.stepInProgress('getFilteredProxyList')
|
191
|
+
if (!this.state.proxyEnabled) {
|
207
192
|
this.log.warn('proxyCache is not enabled')
|
208
193
|
return []
|
209
194
|
}
|
@@ -213,19 +198,27 @@ class GetPartiesService extends BasePartiesService {
|
|
213
198
|
return proxyNames.filter(name => name !== proxy)
|
214
199
|
}
|
215
200
|
|
216
|
-
async
|
217
|
-
const
|
218
|
-
this.
|
201
|
+
async triggerInterSchemeDiscoveryFlow (headers) {
|
202
|
+
const { params } = this.inputs
|
203
|
+
const { proxy, source } = this.state
|
204
|
+
const log = this.log.child({ method: 'triggerInterSchemeDiscoveryFlow' })
|
205
|
+
log.verbose('triggerInterSchemeDiscoveryFlow start...', { proxy, source })
|
206
|
+
|
207
|
+
const proxyNames = await this.getFilteredProxyList(proxy)
|
208
|
+
if (!proxyNames.length) {
|
209
|
+
return this.sendPartyNotFoundErrorCallback(headers)
|
210
|
+
}
|
211
|
+
|
212
|
+
this.stepInProgress('setSendToProxiesList-10')
|
219
213
|
const alsReq = this.deps.partiesUtils.alsRequestDto(source, params)
|
220
|
-
log.
|
214
|
+
log.verbose('starting setSendToProxiesList flow: ', { proxyNames, alsReq, proxyCacheTtlSec })
|
221
215
|
|
222
216
|
const isCached = await this.deps.proxyCache.setSendToProxiesList(alsReq, proxyNames, proxyCacheTtlSec)
|
223
217
|
if (!isCached) {
|
224
|
-
|
225
|
-
throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, ERROR_MESSAGES.failedToCacheSendToProxiesList)
|
218
|
+
throw super.createFspiopIdNotFoundError(ERROR_MESSAGES.failedToCacheSendToProxiesList, log)
|
226
219
|
}
|
227
220
|
|
228
|
-
this.
|
221
|
+
this.stepInProgress('sendingProxyRequests-11')
|
229
222
|
const sending = proxyNames.map(
|
230
223
|
sendTo => this.#forwardGetPartiesRequest({ sendTo, headers, params })
|
231
224
|
.then(({ status, data } = {}) => ({ status, data }))
|
@@ -234,43 +227,34 @@ class GetPartiesService extends BasePartiesService {
|
|
234
227
|
const isOk = results.some(result => result.status === 'fulfilled')
|
235
228
|
// If, at least, one request is sent to proxy, we treat the whole flow as successful.
|
236
229
|
// Failed requests should be handled by TTL expired/timeout handler
|
237
|
-
|
238
|
-
|
239
|
-
|
230
|
+
log.info('triggerInterSchemeDiscoveryFlow is done:', { isOk, results, proxyNames, alsReq })
|
231
|
+
this.stepInProgress('allSent-12')
|
232
|
+
|
240
233
|
if (!isOk) {
|
241
|
-
|
242
|
-
throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, ERROR_MESSAGES.proxyConnectionError)
|
234
|
+
throw super.createFspiopIdNotFoundError(ERROR_MESSAGES.proxyConnectionError, log)
|
243
235
|
}
|
244
236
|
}
|
245
237
|
|
246
|
-
async sendPartyNotFoundErrorCallback (
|
247
|
-
this.
|
248
|
-
const
|
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)
|
238
|
+
async sendPartyNotFoundErrorCallback (headers) {
|
239
|
+
const { params } = this.inputs
|
240
|
+
const fspiopError = super.createFspiopIdNotFoundError('No proxy found to start inter-scheme discovery flow')
|
257
241
|
|
258
242
|
await this.sendErrorCallback({
|
259
|
-
sendTo: requester,
|
260
243
|
errorInfo: fspiopError.toApiErrorObject(this.deps.config.ERROR_HANDLING),
|
261
|
-
headers:
|
244
|
+
headers: GetPartiesService.createErrorCallbackHeaders(headers, params),
|
262
245
|
params
|
263
246
|
})
|
264
247
|
return fspiopError
|
265
248
|
}
|
266
249
|
|
267
|
-
async sendOracleDiscoveryRequest (
|
268
|
-
this.
|
250
|
+
async sendOracleDiscoveryRequest () {
|
251
|
+
this.stepInProgress('sendOracleDiscoveryRequest')
|
252
|
+
const { headers, params, query } = this.inputs
|
269
253
|
return this.deps.oracle.oracleRequest(headers, RestMethods.GET, params, query, undefined, this.deps.cache)
|
270
254
|
}
|
271
255
|
|
272
256
|
async #forwardGetPartiesRequest ({ sendTo, headers, params }) {
|
273
|
-
this.
|
257
|
+
this.stepInProgress('#forwardGetPartiesRequest')
|
274
258
|
const callbackEndpointType = this.deps.partiesUtils.getPartyCbType(params.SubId)
|
275
259
|
const options = this.deps.partiesUtils.partiesRequestOptionsDto(params)
|
276
260
|
|
@@ -30,49 +30,71 @@ const { ERROR_MESSAGES } = require('../../../constants')
|
|
30
30
|
const BasePartiesService = require('./BasePartiesService')
|
31
31
|
|
32
32
|
class PutPartiesErrorService extends BasePartiesService {
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
33
|
+
async handleRequest () {
|
34
|
+
if (this.state.proxyEnabled && this.state.proxy) {
|
35
|
+
const alsReq = this.deps.partiesUtils.alsRequestDto(this.state.destination, this.inputs.params) // or source?
|
36
|
+
const isPending = await this.deps.proxyCache.isPendingCallback(alsReq)
|
37
|
+
|
38
|
+
if (!isPending) {
|
39
|
+
// not initial inter-scheme discovery case. Cleanup oracle and trigger inter-scheme discovery
|
40
|
+
this.log.warn('Need to cleanup oracle and trigger new inter-scheme discovery flow')
|
41
|
+
await this.cleanupOracle()
|
42
|
+
return true // need to trigger inter-scheme discovery
|
43
|
+
}
|
44
|
+
|
45
|
+
const isLast = await this.checkLastProxyCallback(alsReq)
|
46
|
+
if (!isLast) {
|
47
|
+
this.log.verbose('putPartiesErrorByTypeAndID proxy callback was processed')
|
48
|
+
return
|
49
|
+
}
|
44
50
|
}
|
45
|
-
|
51
|
+
|
52
|
+
await this.identifyDestinationForErrorCallback()
|
53
|
+
await this.sendErrorCallbackToParticipant()
|
54
|
+
this.log.info('putPartiesByTypeAndID is done')
|
46
55
|
}
|
47
56
|
|
48
|
-
async
|
49
|
-
this.
|
50
|
-
const
|
57
|
+
async cleanupOracle () {
|
58
|
+
this.stepInProgress('cleanupOracle')
|
59
|
+
const { headers, params, payload } = this.inputs
|
60
|
+
this.log.info('cleanupOracle due to error callback...', { payload })
|
61
|
+
const swappedHeaders = this.deps.partiesUtils.swapSourceDestinationHeaders(headers)
|
62
|
+
await super.sendDeleteOracleRequest(swappedHeaders, params)
|
63
|
+
}
|
64
|
+
|
65
|
+
async checkLastProxyCallback (alsReq) {
|
66
|
+
this.stepInProgress('checkLastProxyCallback')
|
67
|
+
const { proxy } = this.state
|
51
68
|
const isLast = await this.deps.proxyCache.receivedErrorResponse(alsReq, proxy)
|
52
69
|
this.log.info(`got${isLast ? '' : 'NOT'} last error callback from proxy`, { proxy, alsReq, isLast })
|
53
70
|
return isLast
|
54
71
|
}
|
55
72
|
|
56
|
-
async identifyDestinationForErrorCallback (
|
57
|
-
this.
|
58
|
-
const
|
59
|
-
|
73
|
+
async identifyDestinationForErrorCallback () {
|
74
|
+
this.stepInProgress('identifyDestinationForErrorCallback')
|
75
|
+
const { destination } = this.state
|
76
|
+
const schemeParticipant = await super.validateParticipant(destination)
|
77
|
+
if (schemeParticipant) {
|
78
|
+
this.state.requester = destination
|
79
|
+
return
|
80
|
+
}
|
60
81
|
|
61
|
-
this.
|
62
|
-
const proxyName = this.proxyEnabled && await this.deps.proxyCache.lookupProxyByDfspId(destination)
|
63
|
-
if (proxyName)
|
82
|
+
this.stepInProgress('lookupProxyDestination-4')
|
83
|
+
const proxyName = this.state.proxyEnabled && await this.deps.proxyCache.lookupProxyByDfspId(destination)
|
84
|
+
if (proxyName) {
|
85
|
+
this.state.requester = proxyName
|
86
|
+
return
|
87
|
+
}
|
64
88
|
|
65
89
|
const errMessage = ERROR_MESSAGES.partyDestinationFspNotFound
|
66
90
|
this.log.warn(errMessage, { destination })
|
67
91
|
throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.DESTINATION_FSP_ERROR, errMessage)
|
68
92
|
}
|
69
93
|
|
70
|
-
async sendErrorCallbackToParticipant (
|
71
|
-
this.
|
94
|
+
async sendErrorCallbackToParticipant () {
|
95
|
+
const { headers, params, dataUri } = this.inputs
|
72
96
|
const errorInfo = PutPartiesErrorService.decodeDataUriPayload(dataUri)
|
73
|
-
return super.sendErrorCallback({
|
74
|
-
sendTo, errorInfo, headers, params
|
75
|
-
})
|
97
|
+
return super.sendErrorCallback({ errorInfo, headers, params })
|
76
98
|
}
|
77
99
|
}
|
78
100
|
|
@@ -32,34 +32,43 @@ const BasePartiesService = require('./BasePartiesService')
|
|
32
32
|
const { RestMethods } = BasePartiesService.enums()
|
33
33
|
|
34
34
|
class PutPartiesService extends BasePartiesService {
|
35
|
-
|
36
|
-
|
37
|
-
|
35
|
+
async handleRequest () {
|
36
|
+
const { destination, source, proxy } = this.state
|
37
|
+
this.log.info('handleRequest start', { destination, source, proxy })
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
|
39
|
+
await this.validateSourceParticipant()
|
40
|
+
if (proxy) {
|
41
|
+
await this.checkProxySuccessResponse()
|
42
|
+
}
|
43
|
+
await this.identifyDestinationForSuccessCallback()
|
44
|
+
await this.sendSuccessCallback()
|
45
|
+
}
|
46
|
+
|
47
|
+
async validateSourceParticipant () {
|
48
|
+
const { source, proxy, proxyEnabled } = this.state
|
49
|
+
const log = this.log.child({ source, proxy, method: 'validateSourceParticipant' })
|
50
|
+
this.stepInProgress('validateSourceParticipant-1')
|
42
51
|
|
52
|
+
const requesterParticipant = await super.validateParticipant(source)
|
43
53
|
if (!requesterParticipant) {
|
44
|
-
if (!
|
45
|
-
|
46
|
-
this.log.warn(`${errMessage} and no proxy`, { source })
|
47
|
-
throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
|
54
|
+
if (!proxyEnabled || !proxy) {
|
55
|
+
throw super.createFspiopIdNotFoundError(ERROR_MESSAGES.sourceFspNotFound, log)
|
48
56
|
}
|
57
|
+
|
49
58
|
const isCached = await this.deps.proxyCache.addDfspIdToProxyMapping(source, proxy)
|
50
59
|
if (!isCached) {
|
51
|
-
|
52
|
-
this.log.warn(errMessage, { source, proxy })
|
53
|
-
throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
|
60
|
+
throw super.createFspiopIdNotFoundError('failed to addDfspIdToProxyMapping', log)
|
54
61
|
}
|
55
62
|
|
56
|
-
|
63
|
+
log.info('addDfspIdToProxyMapping is done', { source, proxy })
|
57
64
|
}
|
58
65
|
}
|
59
66
|
|
60
|
-
async checkProxySuccessResponse (
|
61
|
-
if (this.proxyEnabled) {
|
62
|
-
this.
|
67
|
+
async checkProxySuccessResponse () {
|
68
|
+
if (this.state.proxyEnabled) {
|
69
|
+
this.stepInProgress('checkProxySuccessResponse')
|
70
|
+
const { headers, params } = this.inputs
|
71
|
+
const { destination, source } = this.state
|
63
72
|
const alsReq = this.deps.partiesUtils.alsRequestDto(destination, params)
|
64
73
|
|
65
74
|
const isExists = await this.deps.proxyCache.receivedSuccessResponse(alsReq)
|
@@ -72,24 +81,29 @@ class PutPartiesService extends BasePartiesService {
|
|
72
81
|
}
|
73
82
|
}
|
74
83
|
|
75
|
-
async identifyDestinationForSuccessCallback (
|
76
|
-
this.
|
84
|
+
async identifyDestinationForSuccessCallback () {
|
85
|
+
const { destination } = this.state
|
86
|
+
this.stepInProgress('identifyDestinationForSuccessCallback')
|
77
87
|
const destinationParticipant = await super.validateParticipant(destination)
|
78
88
|
if (destinationParticipant) {
|
79
|
-
|
89
|
+
this.state.requester = destinationParticipant.name
|
90
|
+
return
|
80
91
|
}
|
81
92
|
|
82
|
-
const proxyName = this.proxyEnabled && await this.deps.proxyCache.lookupProxyByDfspId(destination)
|
93
|
+
const proxyName = this.state.proxyEnabled && await this.deps.proxyCache.lookupProxyByDfspId(destination)
|
83
94
|
if (!proxyName) {
|
84
95
|
const errMessage = ERROR_MESSAGES.partyDestinationFspNotFound
|
85
96
|
this.log.warn(`${errMessage} and no proxy`, { destination })
|
86
97
|
throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.DESTINATION_FSP_ERROR, errMessage)
|
87
98
|
}
|
88
|
-
|
99
|
+
|
100
|
+
this.state.requester = proxyName
|
89
101
|
}
|
90
102
|
|
91
|
-
async sendSuccessCallback (
|
92
|
-
this.
|
103
|
+
async sendSuccessCallback () {
|
104
|
+
const { headers, params, dataUri } = this.inputs
|
105
|
+
const sendTo = this.state.requester
|
106
|
+
this.stepInProgress('sendSuccessCallback')
|
93
107
|
const payload = PutPartiesService.decodeDataUriPayload(dataUri)
|
94
108
|
const callbackEndpointType = this.deps.partiesUtils.putPartyCbType(params.SubId)
|
95
109
|
const options = this.deps.partiesUtils.partiesRequestOptionsDto(params)
|
@@ -97,11 +111,11 @@ class PutPartiesService extends BasePartiesService {
|
|
97
111
|
await this.deps.participant.sendRequest(
|
98
112
|
headers, sendTo, callbackEndpointType, RestMethods.PUT, payload, options
|
99
113
|
)
|
100
|
-
this.log.verbose('sendSuccessCallback is
|
114
|
+
this.log.verbose('sendSuccessCallback is sent', { sendTo, payload })
|
101
115
|
}
|
102
116
|
|
103
117
|
async #updateOracleWithParticipantMapping ({ source, headers, params }) {
|
104
|
-
this.
|
118
|
+
this.stepInProgress('#updateOracleWithParticipantMapping-3')
|
105
119
|
const mappingPayload = {
|
106
120
|
fspId: source
|
107
121
|
}
|
package/test/fixtures/index.js
CHANGED
@@ -1,3 +1,30 @@
|
|
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
|
+
|
1
28
|
const { randomUUID } = require('node:crypto')
|
2
29
|
const { Enum } = require('@mojaloop/central-services-shared')
|
3
30
|
const isoFixtures = require('./iso')
|
@@ -20,6 +47,16 @@ const headersDto = ({
|
|
20
47
|
'content-type': contentType || accept
|
21
48
|
})
|
22
49
|
|
50
|
+
const partiesParamsDto = ({
|
51
|
+
Type = 'MSISDN',
|
52
|
+
ID = String(Date.now()),
|
53
|
+
SubId
|
54
|
+
} = {}) => ({
|
55
|
+
Type,
|
56
|
+
ID,
|
57
|
+
...(SubId && { SubId })
|
58
|
+
})
|
59
|
+
|
23
60
|
const protocolVersionsDto = () => ({
|
24
61
|
CONTENT: {
|
25
62
|
DEFAULT: '2.1',
|
@@ -142,6 +179,7 @@ const mockHapiRequestDto = ({ // https://hapi.dev/api/?v=21.3.3#request-properti
|
|
142
179
|
module.exports = {
|
143
180
|
...isoFixtures,
|
144
181
|
partiesCallHeadersDto,
|
182
|
+
partiesParamsDto,
|
145
183
|
participantsCallHeadersDto,
|
146
184
|
oracleRequestResponseDto,
|
147
185
|
putPartiesSuccessResponseDto,
|
@@ -1369,7 +1369,7 @@ describe('participant Tests', () => {
|
|
1369
1369
|
|
1370
1370
|
// Assert
|
1371
1371
|
expect(logStub.getCall(0).firstArg).toBe(ERROR_MESSAGES.sourceFspNotFound)
|
1372
|
-
expect(logStub.getCall(
|
1372
|
+
expect(logStub.getCall(2).lastArg).toEqual(cbError)
|
1373
1373
|
})
|
1374
1374
|
|
1375
1375
|
it('handles error when `oracleBatchRequest` returns no result', async () => {
|