@mojaloop/central-services-shared 18.6.3 → 18.7.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/.nycrc.yml +2 -1
- package/CHANGELOG.md +7 -0
- package/package.json +5 -4
- package/src/enums/endpoints.js +1 -0
- package/src/enums/http.js +1 -0
- package/src/healthCheck/HealthCheck.js +2 -2
- package/src/healthCheck/HealthCheckEnums.js +2 -1
- package/src/index.d.ts +19 -5
- package/src/util/index.js +2 -0
- package/src/util/participants.js +6 -0
- package/src/util/proxies.js +182 -0
- package/src/util/request.js +16 -11
- package/src/util/streaming/protocol/index.js +1 -1
- package/test/unit/request.test.js +48 -0
package/.nycrc.yml
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
## [18.7.0](https://github.com/mojaloop/central-services-shared/compare/v18.6.3...v18.7.0) (2024-07-26)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* **csi-16:** added getAllProxiesNames method ([#387](https://github.com/mojaloop/central-services-shared/issues/387)) ([3fd95ac](https://github.com/mojaloop/central-services-shared/commit/3fd95ac128a1cb0c60afb4e359aef75230b49b69)), closes [#393](https://github.com/mojaloop/central-services-shared/issues/393)
|
|
11
|
+
|
|
5
12
|
### [18.6.3](https://github.com/mojaloop/central-services-shared/compare/v18.6.2...v18.6.3) (2024-07-12)
|
|
6
13
|
|
|
7
14
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mojaloop/central-services-shared",
|
|
3
|
-
"version": "18.
|
|
3
|
+
"version": "18.7.0",
|
|
4
4
|
"description": "Shared code for mojaloop central services",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "ModusBox",
|
|
@@ -56,13 +56,14 @@
|
|
|
56
56
|
"dependencies": {
|
|
57
57
|
"@hapi/catbox": "12.1.1",
|
|
58
58
|
"@hapi/catbox-memory": "5.0.1",
|
|
59
|
-
"@mojaloop/inter-scheme-proxy-cache-lib": "
|
|
59
|
+
"@mojaloop/inter-scheme-proxy-cache-lib": "2.0.0-snapshot.1",
|
|
60
60
|
"axios": "1.7.2",
|
|
61
61
|
"clone": "2.1.2",
|
|
62
62
|
"dotenv": "16.4.5",
|
|
63
63
|
"env-var": "7.5.0",
|
|
64
64
|
"event-stream": "4.0.1",
|
|
65
|
-
"
|
|
65
|
+
"fast-safe-stringify": "^2.1.1",
|
|
66
|
+
"immutable": "4.3.7",
|
|
66
67
|
"lodash": "4.17.21",
|
|
67
68
|
"mustache": "4.2.0",
|
|
68
69
|
"openapi-backend": "5.10.6",
|
|
@@ -71,7 +72,7 @@
|
|
|
71
72
|
"shins": "2.6.0",
|
|
72
73
|
"uuid4": "2.0.3",
|
|
73
74
|
"widdershins": "^4.0.1",
|
|
74
|
-
"yaml": "2.
|
|
75
|
+
"yaml": "2.5.0"
|
|
75
76
|
},
|
|
76
77
|
"devDependencies": {
|
|
77
78
|
"@hapi/hapi": "21.3.10",
|
package/src/enums/endpoints.js
CHANGED
|
@@ -105,6 +105,7 @@ const FspEndpointTemplates = {
|
|
|
105
105
|
TRANSACTION_REQUEST_GET: '/transactionRequests/{{ID}}',
|
|
106
106
|
TRANSACTION_REQUEST_PUT_ERROR: '/transactionRequests/{{ID}}/error',
|
|
107
107
|
PARTICIPANT_ENDPOINTS_GET: '/participants/{{fsp}}/endpoints',
|
|
108
|
+
PARTICIPANTS_GET_ALL: '/participants',
|
|
108
109
|
PARTICIPANTS_GET: '/participants/{{fsp}}',
|
|
109
110
|
PARTICIPANTS_POST: '/participants',
|
|
110
111
|
PARTIES_GET: '/parties/{{fsp}}',
|
package/src/enums/http.js
CHANGED
|
@@ -88,7 +88,7 @@ class HealthCheck {
|
|
|
88
88
|
* @description Gets the health of the service along with sub-services
|
|
89
89
|
*
|
|
90
90
|
*/
|
|
91
|
-
async getHealth () {
|
|
91
|
+
async getHealth (context = undefined) {
|
|
92
92
|
// Default values
|
|
93
93
|
let status = statusEnum.OK
|
|
94
94
|
let isHealthy = true
|
|
@@ -100,7 +100,7 @@ class HealthCheck {
|
|
|
100
100
|
const versionNumber = this.packageJson.version
|
|
101
101
|
|
|
102
102
|
try {
|
|
103
|
-
const services = await Promise.all(this.serviceChecks.map(s => s()))
|
|
103
|
+
const services = await Promise.all(this.serviceChecks.map(s => s(context)))
|
|
104
104
|
isHealthy = HealthCheck.evaluateServiceHealth(services)
|
|
105
105
|
subServices = {
|
|
106
106
|
services
|
package/src/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ declare namespace CentralServicesShared {
|
|
|
9
9
|
FSPIOP: {
|
|
10
10
|
SOURCE: string;
|
|
11
11
|
DESTINATION: string;
|
|
12
|
+
PROXY: string;
|
|
12
13
|
HTTP_METHOD: string;
|
|
13
14
|
SIGNATURE: string;
|
|
14
15
|
URI: string;
|
|
@@ -599,16 +600,26 @@ declare namespace CentralServicesShared {
|
|
|
599
600
|
};
|
|
600
601
|
};
|
|
601
602
|
}
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
getEndpoint(switchUrl: string, fsp: string, endpointType: FspEndpointTypesEnum, options?: any): Promise<string>
|
|
603
|
+
|
|
604
|
+
interface Cacheable {
|
|
605
605
|
initializeCache(policyOptions: object, config: { hubName: string, hubNameRegex: RegExp }): Promise<boolean>
|
|
606
|
+
stopCache(): Promise<void>
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
interface Endpoints extends Cacheable {
|
|
610
|
+
getEndpoint(switchUrl: string, fsp: string, endpointType: FspEndpointTypesEnum, options?: any): Promise<string>
|
|
606
611
|
getEndpointAndRender(switchUrl: string, fsp: string, endpointType: FspEndpointTypesEnum, path: string, options?: any): Promise<string>
|
|
607
612
|
}
|
|
608
613
|
|
|
609
|
-
interface Participants {
|
|
614
|
+
interface Participants extends Cacheable {
|
|
610
615
|
getParticipant(switchUrl: string, fsp: string): Promise<object>
|
|
611
|
-
|
|
616
|
+
invalidateParticipantCache(fsp: string): Promise<void>
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
type ProxyNames = string[]
|
|
620
|
+
interface Proxies extends Cacheable {
|
|
621
|
+
getAllProxiesNames(switchUrl: string): Promise<ProxyNames>
|
|
622
|
+
invalidateProxiesCache(): Promise<void>
|
|
612
623
|
}
|
|
613
624
|
|
|
614
625
|
interface ProtocolVersionsType {
|
|
@@ -625,8 +636,10 @@ declare namespace CentralServicesShared {
|
|
|
625
636
|
createGeneralTopicConf(template: string, functionality: string, action: string, key?: string, partition?: number, opaqueKey?: any, topicNameOverride?: string): {topicName: string, key: string | null, partition: number | null, opaqueKey: any }
|
|
626
637
|
}
|
|
627
638
|
|
|
639
|
+
type MimeTypes = 'text/plain' | 'application/json' | 'application/vnd.interoperability.'
|
|
628
640
|
interface StreamingProtocol {
|
|
629
641
|
decodePayload(input: string, options: Object): Object
|
|
642
|
+
encodePayload(input: string | Buffer, mimeType: MimeTypes): string
|
|
630
643
|
}
|
|
631
644
|
|
|
632
645
|
interface HeaderValidation {
|
|
@@ -643,6 +656,7 @@ declare namespace CentralServicesShared {
|
|
|
643
656
|
interface Util {
|
|
644
657
|
Endpoints: Endpoints;
|
|
645
658
|
Participants: Participants;
|
|
659
|
+
proxies: Proxies;
|
|
646
660
|
Hapi: any;
|
|
647
661
|
Kafka: Kafka;
|
|
648
662
|
OpenapiBackend: any;
|
package/src/util/index.js
CHANGED
|
@@ -30,6 +30,7 @@ const _ = require('lodash')
|
|
|
30
30
|
const Kafka = require('./kafka')
|
|
31
31
|
const Endpoints = require('./endpoints')
|
|
32
32
|
const Participants = require('./participants')
|
|
33
|
+
const proxies = require('./proxies')
|
|
33
34
|
const Request = require('./request')
|
|
34
35
|
const Http = require('./http')
|
|
35
36
|
const Hapi = require('./hapi')
|
|
@@ -238,6 +239,7 @@ module.exports = {
|
|
|
238
239
|
filterExtensions,
|
|
239
240
|
Kafka,
|
|
240
241
|
Participants,
|
|
242
|
+
proxies,
|
|
241
243
|
Endpoints,
|
|
242
244
|
Request,
|
|
243
245
|
Http,
|
package/src/util/participants.js
CHANGED
|
@@ -145,6 +145,12 @@ exports.getParticipant = async (switchUrl, fsp) => {
|
|
|
145
145
|
histTimer({ success: true, hit: false })
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
+
/* istanbul ignore next */
|
|
149
|
+
if (!participant) {
|
|
150
|
+
Logger.isWarnEnabled && Logger.warn('participantCache::getParticipant - no participant found')
|
|
151
|
+
return null
|
|
152
|
+
}
|
|
153
|
+
|
|
148
154
|
if (participant.errorInformation) {
|
|
149
155
|
// Drop error from cache
|
|
150
156
|
await policy.drop(fsp)
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/*****
|
|
2
|
+
License
|
|
3
|
+
--------------
|
|
4
|
+
Copyright © 2017 Bill & Melinda Gates Foundation
|
|
5
|
+
The Mojaloop files are made available by the Bill & Melinda Gates 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
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
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.
|
|
8
|
+
|
|
9
|
+
Contributors
|
|
10
|
+
--------------
|
|
11
|
+
This is the official list of the Mojaloop project contributors for this file.
|
|
12
|
+
Names of the original copyright holders (individuals or organizations)
|
|
13
|
+
should be listed with a '*' in the first column. People who have
|
|
14
|
+
contributed from an organization can be listed under the organization
|
|
15
|
+
that actually holds the copyright for their contributions (see the
|
|
16
|
+
Gates Foundation organization for an example). Those individuals should have
|
|
17
|
+
their names indented and be marked with a '-'. Email address can be added
|
|
18
|
+
optionally within square brackets <email>.
|
|
19
|
+
* Gates Foundation
|
|
20
|
+
- Name Surname <name.surname@gatesfoundation.com>
|
|
21
|
+
|
|
22
|
+
* Eugen Klymniuk <eugen.klymniuk@infitx.com>
|
|
23
|
+
--------------
|
|
24
|
+
**********/
|
|
25
|
+
|
|
26
|
+
const Mustache = require('mustache')
|
|
27
|
+
const Catbox = require('@hapi/catbox')
|
|
28
|
+
const CatboxMemory = require('@hapi/catbox-memory')
|
|
29
|
+
const ErrorHandler = require('@mojaloop/central-services-error-handling')
|
|
30
|
+
const Logger = require('@mojaloop/central-services-logger')
|
|
31
|
+
const Metrics = require('@mojaloop/central-services-metrics')
|
|
32
|
+
|
|
33
|
+
const Enum = require('../enums')
|
|
34
|
+
const Http = require('./http')
|
|
35
|
+
const request = require('./request')
|
|
36
|
+
|
|
37
|
+
const partition = 'proxies-cache'
|
|
38
|
+
const clientOptions = { partition }
|
|
39
|
+
const cacheKey = 'allProxies'
|
|
40
|
+
|
|
41
|
+
let client
|
|
42
|
+
let policy
|
|
43
|
+
let switchEndpoint
|
|
44
|
+
let hubName
|
|
45
|
+
let hubNameRegex
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @function fetchProxies
|
|
49
|
+
* @description This populates the cache of proxies
|
|
50
|
+
* @returns {array} proxies Returns the list containing proxies
|
|
51
|
+
*/
|
|
52
|
+
const fetchProxies = async () => {
|
|
53
|
+
const histTimer = Metrics.getHistogram(
|
|
54
|
+
'fetchProxies',
|
|
55
|
+
'fetchProxies - Metrics for fetchProxies',
|
|
56
|
+
['success']
|
|
57
|
+
).startTimer()
|
|
58
|
+
try {
|
|
59
|
+
Logger.isDebugEnabled && Logger.debug('proxiesCache::fetchProxies := Refreshing proxies cache')
|
|
60
|
+
if (!hubName || !hubNameRegex) {
|
|
61
|
+
throw Error('No hubName or hubNameRegex! Initialize the cache first.')
|
|
62
|
+
}
|
|
63
|
+
const defaultHeaders = Http.SwitchDefaultHeaders(hubName, Enum.Http.HeaderResources.PARTICIPANTS, hubName)
|
|
64
|
+
const url = Mustache.render(switchEndpoint + Enum.EndPoints.FspEndpointTemplates.PARTICIPANTS_GET_ALL)
|
|
65
|
+
const params = { isProxy: true }
|
|
66
|
+
Logger.isDebugEnabled && Logger.debug(`proxiesCache::fetchProxies := URL: ${url} QS: ${JSON.stringify(params)}`)
|
|
67
|
+
const response = await request.sendRequest({
|
|
68
|
+
url,
|
|
69
|
+
headers: defaultHeaders,
|
|
70
|
+
source: hubName,
|
|
71
|
+
destination: hubName,
|
|
72
|
+
params,
|
|
73
|
+
hubNameRegex
|
|
74
|
+
})
|
|
75
|
+
const proxies = response.data
|
|
76
|
+
histTimer({ success: true })
|
|
77
|
+
return proxies
|
|
78
|
+
} catch (e) {
|
|
79
|
+
histTimer({ success: false })
|
|
80
|
+
Logger.isErrorEnabled && Logger.error(`proxiesCache::fetchProxies:: ERROR:'${e}'`)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @function initializeCache
|
|
86
|
+
*
|
|
87
|
+
* @description This initializes the cache for allProxies
|
|
88
|
+
* @param {object} policyOptions The Endpoint_Cache_Config for the Cache being stored https://hapi.dev/module/catbox/api/?v=12.1.1#policy
|
|
89
|
+
* @param {object} config The config object containing paramters used for the request function
|
|
90
|
+
* @returns {boolean} Returns true on successful initialization of the cache, throws error on failures
|
|
91
|
+
*/
|
|
92
|
+
exports.initializeCache = async (policyOptions, config) => {
|
|
93
|
+
try {
|
|
94
|
+
Logger.isDebugEnabled && Logger.debug(`proxiesCache::initializeCache::start::clientOptions - ${JSON.stringify(clientOptions)}`)
|
|
95
|
+
client = new Catbox.Client(CatboxMemory, clientOptions)
|
|
96
|
+
await client.start()
|
|
97
|
+
policyOptions.generateFunc = fetchProxies
|
|
98
|
+
Logger.isDebugEnabled && Logger.debug(`proxiesCache::initializeCache::start::policyOptions - ${JSON.stringify(policyOptions)}`)
|
|
99
|
+
policy = new Catbox.Policy(policyOptions, client, partition)
|
|
100
|
+
Logger.isDebugEnabled && Logger.debug('proxiesCache::initializeCache::Cache initialized successfully')
|
|
101
|
+
hubName = config.hubName
|
|
102
|
+
hubNameRegex = config.hubNameRegex
|
|
103
|
+
return true
|
|
104
|
+
} catch (err) {
|
|
105
|
+
Logger.isErrorEnabled && Logger.error(`proxiesCache::Cache error:: ERROR:'${err}'`)
|
|
106
|
+
throw ErrorHandler.Factory.reformatFSPIOPError(err)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* @function getAllProxiesNames
|
|
112
|
+
* @description It returns a list of allProxies names from the cache if the cache is still valid, otherwise it will refresh the cache and return the value
|
|
113
|
+
*
|
|
114
|
+
* @param {string} switchUrl the endpoint for the switch
|
|
115
|
+
*
|
|
116
|
+
* @returns {string[]} - Returns list of allProxies names, throws error if failure occurs
|
|
117
|
+
*/
|
|
118
|
+
exports.getAllProxiesNames = async (switchUrl) => {
|
|
119
|
+
const histTimer = Metrics.getHistogram(
|
|
120
|
+
'getAllProxiesNames',
|
|
121
|
+
'getAllProxiesNames - Metrics for getAllProxies with cache hit rate',
|
|
122
|
+
['success', 'hit']
|
|
123
|
+
).startTimer()
|
|
124
|
+
switchEndpoint = switchUrl
|
|
125
|
+
Logger.isDebugEnabled && Logger.debug('proxiesCache::getAllProxiesNames')
|
|
126
|
+
try {
|
|
127
|
+
// If a service passes in `getDecoratedValue` as true, then an object
|
|
128
|
+
// { value, cached, report } is returned, where value is the cached value,
|
|
129
|
+
// cached is null on a cache miss.
|
|
130
|
+
let proxies = await policy.get(cacheKey)
|
|
131
|
+
|
|
132
|
+
if ('value' in proxies && 'cached' in proxies) {
|
|
133
|
+
if (proxies.cached === null) {
|
|
134
|
+
histTimer({ success: true, hit: false })
|
|
135
|
+
} else {
|
|
136
|
+
histTimer({ success: true, hit: true })
|
|
137
|
+
}
|
|
138
|
+
proxies = proxies.value
|
|
139
|
+
} else {
|
|
140
|
+
histTimer({ success: true, hit: false })
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (proxies.errorInformation) {
|
|
144
|
+
// Drop error from cache
|
|
145
|
+
await policy.drop(cacheKey)
|
|
146
|
+
throw ErrorHandler.Factory.createFSPIOPErrorFromErrorInformation(proxies.errorInformation)
|
|
147
|
+
}
|
|
148
|
+
return proxies.map(p => p.name)
|
|
149
|
+
} catch (err) {
|
|
150
|
+
histTimer({ success: false, hit: false })
|
|
151
|
+
Logger.isErrorEnabled && Logger.error(`proxiesCache::getAllProxiesNames:: ERROR:'${err}'`)
|
|
152
|
+
throw ErrorHandler.Factory.reformatFSPIOPError(err)
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* @function invalidateProxiesCache
|
|
158
|
+
*
|
|
159
|
+
* @description It drops the cache for all proxies
|
|
160
|
+
*
|
|
161
|
+
* @returns {void}
|
|
162
|
+
*/
|
|
163
|
+
exports.invalidateProxiesCache = async () => {
|
|
164
|
+
Logger.isDebugEnabled && Logger.debug('proxiesCache::invalidateProxiesCache::Invalidating the cache')
|
|
165
|
+
if (policy) {
|
|
166
|
+
return policy.drop(cacheKey)
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* @function stopCache
|
|
172
|
+
*
|
|
173
|
+
* @description It stops the cache client
|
|
174
|
+
*
|
|
175
|
+
* @returns {boolean} - Returns the status
|
|
176
|
+
*/
|
|
177
|
+
exports.stopCache = async () => {
|
|
178
|
+
Logger.isDebugEnabled && Logger.debug('proxiesCache::stopCache::Stopping the cache')
|
|
179
|
+
if (client) {
|
|
180
|
+
return client.stop()
|
|
181
|
+
}
|
|
182
|
+
}
|
package/src/util/request.js
CHANGED
|
@@ -24,12 +24,13 @@
|
|
|
24
24
|
******/
|
|
25
25
|
'use strict'
|
|
26
26
|
|
|
27
|
-
const
|
|
27
|
+
const http = require('node:http')
|
|
28
28
|
const request = require('axios')
|
|
29
|
+
const stringify = require('fast-safe-stringify')
|
|
30
|
+
const EventSdk = require('@mojaloop/event-sdk')
|
|
29
31
|
const Logger = require('@mojaloop/central-services-logger')
|
|
30
32
|
const ErrorHandler = require('@mojaloop/central-services-error-handling')
|
|
31
33
|
const Metrics = require('@mojaloop/central-services-metrics')
|
|
32
|
-
const http = require('http')
|
|
33
34
|
const Headers = require('./headers/transformer')
|
|
34
35
|
const enums = require('../enums')
|
|
35
36
|
|
|
@@ -59,6 +60,7 @@ request.defaults.httpAgent.toJSON = () => ({})
|
|
|
59
60
|
* @param {string} source id for which callback is being sent from
|
|
60
61
|
* @param {string} destination id for which callback is being sent
|
|
61
62
|
* @param {object | undefined} payload the body of the request being sent
|
|
63
|
+
* @param {object | null} params URL parameters to be sent with the request. Must be a plain object, URLSearchParams object or null/undefined
|
|
62
64
|
* @param {string} responseType the type of the response object
|
|
63
65
|
* @param {object | undefined} span a span for event logging if this request is within a span
|
|
64
66
|
* @param {object | undefined} jwsSigner the jws signer for signing the requests
|
|
@@ -75,6 +77,7 @@ const sendRequest = async ({
|
|
|
75
77
|
destination,
|
|
76
78
|
method = enums.Http.RestMethods.GET,
|
|
77
79
|
payload = undefined,
|
|
80
|
+
params,
|
|
78
81
|
responseType = enums.Http.ResponseTypes.JSON,
|
|
79
82
|
span = undefined,
|
|
80
83
|
jwsSigner = undefined,
|
|
@@ -93,7 +96,8 @@ const sendRequest = async ({
|
|
|
93
96
|
sendRequestSpan.setTags({ source, destination, method, url })
|
|
94
97
|
}
|
|
95
98
|
let requestOptions
|
|
96
|
-
if (!url || !method || !headers || (method !== enums.Http.RestMethods.GET && method !== enums.Http.RestMethods.DELETE && !payload) || !source || !
|
|
99
|
+
if (!url || !method || !headers || (method !== enums.Http.RestMethods.GET && method !== enums.Http.RestMethods.DELETE && !payload) || !source || !hubNameRegex) {
|
|
100
|
+
// think, if we can just avoid checking "destination"
|
|
97
101
|
throw ErrorHandler.Factory.createInternalServerFSPIOPError(MISSING_FUNCTION_PARAMETERS)
|
|
98
102
|
}
|
|
99
103
|
try {
|
|
@@ -109,6 +113,7 @@ const sendRequest = async ({
|
|
|
109
113
|
method,
|
|
110
114
|
headers: transformedHeaders,
|
|
111
115
|
data: payload,
|
|
116
|
+
params,
|
|
112
117
|
responseType,
|
|
113
118
|
httpAgent: new http.Agent({ keepAlive: true }),
|
|
114
119
|
...axiosRequestOptionsOverride
|
|
@@ -122,29 +127,29 @@ const sendRequest = async ({
|
|
|
122
127
|
requestOptions = span.injectContextToHttpRequest(requestOptions)
|
|
123
128
|
span.audit(requestOptions, EventSdk.AuditEventAction.egress)
|
|
124
129
|
}
|
|
125
|
-
Logger.isDebugEnabled && Logger.debug(`sendRequest::
|
|
130
|
+
Logger.isDebugEnabled && Logger.debug(`sendRequest::requestOptions ${stringify(requestOptions)}`)
|
|
126
131
|
const response = await request(requestOptions)
|
|
127
|
-
|
|
132
|
+
|
|
128
133
|
!!sendRequestSpan && await sendRequestSpan.finish()
|
|
129
134
|
histTimerEnd({ success: true, source, destination, method })
|
|
130
135
|
return response
|
|
131
136
|
} catch (error) {
|
|
132
|
-
Logger.isErrorEnabled && Logger.error(error)
|
|
137
|
+
Logger.isErrorEnabled && Logger.error(`error in request.sendRequest: ${error.stack}`)
|
|
133
138
|
const extensionArray = [
|
|
134
139
|
{ key: 'url', value: url },
|
|
135
140
|
{ key: 'sourceFsp', value: source },
|
|
136
141
|
{ key: 'destinationFsp', value: destination },
|
|
137
142
|
{ key: 'method', value: method },
|
|
138
|
-
{ key: 'request', value:
|
|
143
|
+
{ key: 'request', value: stringify(requestOptions) },
|
|
139
144
|
{ key: 'errorMessage', value: error.message }
|
|
140
145
|
]
|
|
141
146
|
const extensions = []
|
|
142
147
|
if (error.response) {
|
|
143
|
-
extensionArray.push({ key: 'status', value: error.response
|
|
144
|
-
extensionArray.push({ key: 'response', value: error.response
|
|
145
|
-
extensions.push({ key: 'status', value: error.response
|
|
148
|
+
extensionArray.push({ key: 'status', value: error.response?.status })
|
|
149
|
+
extensionArray.push({ key: 'response', value: error.response?.data })
|
|
150
|
+
extensions.push({ key: 'status', value: error.response?.status })
|
|
146
151
|
}
|
|
147
|
-
const cause =
|
|
152
|
+
const cause = stringify(extensionArray)
|
|
148
153
|
extensions.push({ key: 'cause', value: cause })
|
|
149
154
|
const fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.DESTINATION_COMMUNICATION_ERROR, 'Failed to send HTTP request to host', error, source, extensions)
|
|
150
155
|
if (sendRequestSpan) {
|
|
@@ -205,7 +205,7 @@ const createEventState = (status, code, description) => {
|
|
|
205
205
|
/**
|
|
206
206
|
* Encodes Payload to base64 encoded data URI
|
|
207
207
|
*
|
|
208
|
-
* @param {buffer
|
|
208
|
+
* @param {buffer|string} input - Buffer or String
|
|
209
209
|
* @param {MimeTypes} mimeType - mime type of the input
|
|
210
210
|
*
|
|
211
211
|
* @return {string} - Returns base64 encoded data URI string
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const Test = require('tapes')(require('tape'))
|
|
2
|
+
const sinon = require('sinon')
|
|
3
|
+
const proxyquire = require('proxyquire')
|
|
4
|
+
const { Http } = require('../../src/enums')
|
|
5
|
+
|
|
6
|
+
Test('sendRequest Tests -->', test => {
|
|
7
|
+
let sandbox
|
|
8
|
+
let axios
|
|
9
|
+
let request
|
|
10
|
+
|
|
11
|
+
test.beforeEach(t => {
|
|
12
|
+
sandbox = sinon.createSandbox()
|
|
13
|
+
axios = sandbox.stub()
|
|
14
|
+
request = proxyquire('../../src/util/request', { axios })
|
|
15
|
+
// sinon can't mock such way of using axios: axios(requestOptions)
|
|
16
|
+
t.end()
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
test.afterEach(t => {
|
|
20
|
+
sandbox.restore()
|
|
21
|
+
t.end()
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
test.test('should add fspiop-signature header if jwsSigner is passed ', async test => {
|
|
25
|
+
const signature = 'signature'
|
|
26
|
+
const jwsSigner = {
|
|
27
|
+
getSignature: sandbox.stub().callsFake(() => signature)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
await request.sendRequest({
|
|
31
|
+
url: 'http://localhost:1234',
|
|
32
|
+
jwsSigner,
|
|
33
|
+
headers: {
|
|
34
|
+
[Http.Headers.FSPIOP.SOURCE]: 'source'
|
|
35
|
+
},
|
|
36
|
+
source: 'source',
|
|
37
|
+
destination: 'destination',
|
|
38
|
+
hubNameRegex: 'hubNameRegex'
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
test.ok(axios.calledOnce)
|
|
42
|
+
const { headers } = axios.lastCall.args[0]
|
|
43
|
+
test.equal(headers['fspiop-signature'], signature)
|
|
44
|
+
test.end()
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
test.end()
|
|
48
|
+
})
|