@mojaloop/central-services-shared 18.36.0-snapshot.4 → 18.36.1
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/.circleci/config.yml +1 -1
- package/.grype.yaml +13 -35
- package/.ncurc.yaml +1 -1
- package/.nvmrc +1 -1
- package/CHANGELOG.md +43 -0
- package/audit-ci.jsonc +6 -2
- package/package.json +40 -38
- package/src/config.js +0 -7
- package/src/enums/endpoints.js +30 -10
- package/src/index.d.ts +11 -30
- package/src/util/hapi/plugins/headerValidation.js +4 -3
- package/src/util/hapi/plugins/loggingPlugin.js +15 -70
- package/src/util/request.js +14 -83
- package/test/unit/util/hapi/plugins/headerValidation.test.js +21 -6
- package/test/unit/util/hapi/plugins/loggingPlugin.test.js +1 -1
- package/test/unit/util/request.test.js +6 -37
- package/src/util/otelDto.js +0 -75
package/.circleci/config.yml
CHANGED
package/.grype.yaml
CHANGED
|
@@ -1,40 +1,18 @@
|
|
|
1
|
-
# Grype vulnerability scanning configuration for central-services-shared
|
|
2
|
-
# This is a library project without Docker images, so we use source scanning
|
|
3
1
|
scan-type: source
|
|
4
|
-
|
|
5
|
-
# Enable vulnerability scanning
|
|
6
2
|
disabled: false
|
|
7
|
-
|
|
8
|
-
# Vulnerability ignore rules
|
|
9
|
-
# Add specific CVEs here if they are false positives or acceptable risks
|
|
10
3
|
ignore:
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
# - vulnerability: "GHSA-xxxx-xxxx-xxxx"
|
|
15
|
-
# package:
|
|
16
|
-
# name: "package-name"
|
|
17
|
-
# version: "1.0.0"
|
|
18
|
-
# reason: "Not exploitable in our usage context"
|
|
19
|
-
|
|
20
|
-
# Output formats for scan results
|
|
4
|
+
- vulnerability: GHSA-rpr9-rxv7-x643
|
|
5
|
+
include-aliases: true
|
|
6
|
+
reason: "[05-16-2026][Critical][sanitize-html 2.12.1] No available fix. Apostrophe has default XSS via `xmp` raw-text passthrough in `sanitize-html"
|
|
21
7
|
output:
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
add-cpes-if-none: false # Don't add CPEs if none are found
|
|
30
|
-
by-cve: false # Group by vulnerability rather than CVE
|
|
31
|
-
|
|
32
|
-
# Database settings
|
|
8
|
+
- table
|
|
9
|
+
- json
|
|
10
|
+
quiet: false
|
|
11
|
+
check-for-app-update: false
|
|
12
|
+
only-fixed: false
|
|
13
|
+
add-cpes-if-none: false
|
|
14
|
+
by-cve: false
|
|
33
15
|
db:
|
|
34
|
-
auto-update: true
|
|
35
|
-
validate-age: true
|
|
36
|
-
max-allowed-built-age: 120h
|
|
37
|
-
|
|
38
|
-
# Severity thresholds (handled by the orb, but documented here for clarity)
|
|
39
|
-
# The build will fail on Critical, High, or Medium severity vulnerabilities
|
|
40
|
-
# Low and Negligible severities are reported but won't fail the build
|
|
16
|
+
auto-update: true
|
|
17
|
+
validate-age: true
|
|
18
|
+
max-allowed-built-age: 120h
|
package/.ncurc.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
reject: [
|
|
3
3
|
# TODO: Added "@hapi/catbox-memory" to ncurc for dep:check to ignore updates due to breaking changes which should be handled by another story
|
|
4
4
|
"@hapi/catbox-memory",
|
|
5
|
-
"ioredis" # version 5.7.0 caused failures of unit-tests in QS
|
|
5
|
+
"ioredis", # version 5.7.0 caused failures of unit-tests in QS
|
|
6
6
|
]
|
package/.nvmrc
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
22.
|
|
1
|
+
22.22.2
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,49 @@
|
|
|
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.36.1](https://github.com/mojaloop/central-services-shared/compare/v18.36.0...v18.36.1) (2026-05-18)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* issue 4297 aligning tpp accounts request naming ([#521](https://github.com/mojaloop/central-services-shared/issues/521)) ([da0335d](https://github.com/mojaloop/central-services-shared/commit/da0335d46fabe5547ba622c9d9a3f446936827e0))
|
|
11
|
+
|
|
12
|
+
## [18.36.0](https://github.com/mojaloop/central-services-shared/compare/v18.35.7...v18.36.0) (2026-05-13)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
|
|
17
|
+
* add new endpoints for tppConsentRequest ([#519](https://github.com/mojaloop/central-services-shared/issues/519)) ([635a383](https://github.com/mojaloop/central-services-shared/commit/635a3830b22ee1e01914b9a17be6df802772dc3c))
|
|
18
|
+
* add tppConsents request endpoints ([#520](https://github.com/mojaloop/central-services-shared/issues/520)) ([5fb9873](https://github.com/mojaloop/central-services-shared/commit/5fb98739f330736ca74d3a9934dbf971d4c46c0b))
|
|
19
|
+
|
|
20
|
+
### [18.35.7](https://github.com/mojaloop/central-services-shared/compare/v18.35.6...v18.35.7) (2026-04-07)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### Bug Fixes
|
|
24
|
+
|
|
25
|
+
* require Accept header for initiating methods per FSPIOP spec ([#516](https://github.com/mojaloop/central-services-shared/issues/516)) ([4d29c23](https://github.com/mojaloop/central-services-shared/commit/4d29c23464ceaf31961991fa82d6ac986d47e4d4)), closes [mojaloop/project#4183](https://github.com/mojaloop/project/issues/4183)
|
|
26
|
+
|
|
27
|
+
### [18.35.6](https://github.com/mojaloop/central-services-shared/compare/v18.35.5...v18.35.6) (2026-02-26)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
### Chore
|
|
31
|
+
|
|
32
|
+
* rm circular dependency on sdk-standard-components ([#510](https://github.com/mojaloop/central-services-shared/issues/510)) ([7346920](https://github.com/mojaloop/central-services-shared/commit/7346920e3c3e0996aeebfd7cce4e24ac54d59313))
|
|
33
|
+
|
|
34
|
+
### [18.35.5](https://github.com/mojaloop/central-services-shared/compare/v18.35.4...v18.35.5) (2026-02-20)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
### Chore
|
|
38
|
+
|
|
39
|
+
* update orb and dep ver ([#509](https://github.com/mojaloop/central-services-shared/issues/509)) ([7293cff](https://github.com/mojaloop/central-services-shared/commit/7293cffea15145d115b25625eb4352ceb651bb35))
|
|
40
|
+
|
|
41
|
+
### [18.35.4](https://github.com/mojaloop/central-services-shared/compare/v18.35.3...v18.35.4) (2026-02-19)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
### Chore
|
|
45
|
+
|
|
46
|
+
* update dependencies, node.js 22.22.0, and security patches ([#508](https://github.com/mojaloop/central-services-shared/issues/508)) ([51281d8](https://github.com/mojaloop/central-services-shared/commit/51281d8eaaa8b9da53214f2b6543d1aef165a682))
|
|
47
|
+
|
|
5
48
|
### [18.35.3](https://github.com/mojaloop/central-services-shared/compare/v18.35.2...v18.35.3) (2026-02-06)
|
|
6
49
|
|
|
7
50
|
|
package/audit-ci.jsonc
CHANGED
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
// Only use one of ["low": true, "moderate": true, "high": true, "critical": true]
|
|
5
5
|
"moderate": true,
|
|
6
6
|
"allowlist": [ // NOTE: Please add as much information as possible to any items added to the allowList
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
[05-16-2026] - No fix available for this vulnerability
|
|
9
|
+
Apostrophe has default XSS via `xmp` raw-text passthrough in `sanitize-html`
|
|
10
|
+
**/
|
|
11
|
+
"GHSA-rpr9-rxv7-x643"
|
|
8
12
|
]
|
|
9
|
-
}
|
|
13
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mojaloop/central-services-shared",
|
|
3
|
-
"version": "18.36.
|
|
3
|
+
"version": "18.36.1",
|
|
4
4
|
"description": "Shared code for mojaloop central services",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "ModusBox",
|
|
@@ -48,7 +48,6 @@
|
|
|
48
48
|
"test:endpoints": "npx tape 'test/unit/util/endpoints.test.js'",
|
|
49
49
|
"test:mysql": "npx tape 'test/unit/mysql/**/*.test.js'",
|
|
50
50
|
"test:participants": "npx tape 'test/unit/util/participants.test.js'",
|
|
51
|
-
"test:request": "npx tape 'test/unit/util/request.test.js'",
|
|
52
51
|
"test:trans": "npx tape 'test/unit/util/headers/transformer.test.js'",
|
|
53
52
|
"test:unit": "npx tape 'test/unit/**/*.test.js' | tap-spec",
|
|
54
53
|
"test:xunit": "npx tape 'test/unit/**/**.test.js' | tap-xunit > ./test/results/xunit.xml",
|
|
@@ -68,26 +67,25 @@
|
|
|
68
67
|
"dependencies": {
|
|
69
68
|
"@hapi/catbox": "12.1.1",
|
|
70
69
|
"@hapi/catbox-memory": "5.0.1",
|
|
71
|
-
"@hapi/hapi": "21.4.
|
|
70
|
+
"@hapi/hapi": "21.4.9",
|
|
72
71
|
"@hapi/joi-date": "2.0.1",
|
|
73
|
-
"@mojaloop/inter-scheme-proxy-cache-lib": "2.
|
|
74
|
-
"@opentelemetry/api": "1.9.
|
|
75
|
-
"@opentelemetry/semantic-conventions": "1.39.0",
|
|
72
|
+
"@mojaloop/inter-scheme-proxy-cache-lib": "2.10.0",
|
|
73
|
+
"@opentelemetry/api": "1.9.1",
|
|
76
74
|
"async-exit-hook": "2.0.1",
|
|
77
75
|
"async-retry": "1.3.3",
|
|
78
|
-
"axios": "1.
|
|
76
|
+
"axios": "1.16.1",
|
|
79
77
|
"clone": "2.1.2",
|
|
80
|
-
"convict": "
|
|
81
|
-
"dotenv": "17.2
|
|
78
|
+
"convict": "6.2.5",
|
|
79
|
+
"dotenv": "17.4.2",
|
|
82
80
|
"env-var": "7.5.0",
|
|
83
81
|
"event-stream": "4.0.1",
|
|
84
82
|
"fast-safe-stringify": "2.1.1",
|
|
85
|
-
"immutable": "5.1.
|
|
83
|
+
"immutable": "5.1.5",
|
|
86
84
|
"ioredis": "5.6.1",
|
|
87
|
-
"joi": "18.
|
|
88
|
-
"lodash": "4.
|
|
85
|
+
"joi": "18.2.1",
|
|
86
|
+
"lodash": "4.18.1",
|
|
89
87
|
"mustache": "4.2.0",
|
|
90
|
-
"openapi-backend": "5.
|
|
88
|
+
"openapi-backend": "5.17.0",
|
|
91
89
|
"raw-body": "3.0.2",
|
|
92
90
|
"rc": "1.2.8",
|
|
93
91
|
"redlock": "5.0.0-beta.2",
|
|
@@ -95,30 +93,29 @@
|
|
|
95
93
|
"ulidx": "2.4.1",
|
|
96
94
|
"uuid4": "2.0.3",
|
|
97
95
|
"widdershins": "4.0.1",
|
|
98
|
-
"yaml": "2.
|
|
96
|
+
"yaml": "2.9.0"
|
|
99
97
|
},
|
|
100
98
|
"devDependencies": {
|
|
101
|
-
"@mojaloop/central-services-error-handling": "13.1.
|
|
102
|
-
"@mojaloop/central-services-logger": "11.
|
|
103
|
-
"@mojaloop/central-services-metrics": "12.8.
|
|
104
|
-
"@mojaloop/event-sdk": "14.8.
|
|
105
|
-
"@
|
|
106
|
-
"@opentelemetry/auto-instrumentations-node": "^0.69.0",
|
|
99
|
+
"@mojaloop/central-services-error-handling": "13.1.6",
|
|
100
|
+
"@mojaloop/central-services-logger": "11.10.4",
|
|
101
|
+
"@mojaloop/central-services-metrics": "12.8.5",
|
|
102
|
+
"@mojaloop/event-sdk": "14.8.4",
|
|
103
|
+
"@opentelemetry/auto-instrumentations-node": "^0.76.0",
|
|
107
104
|
"@types/hapi__joi": "17.1.15",
|
|
108
|
-
"ajv": "
|
|
105
|
+
"ajv": "8.20.0",
|
|
109
106
|
"ajv-formats": "^3.0.1",
|
|
110
107
|
"ajv-keywords": "^5.1.0",
|
|
111
108
|
"audit-ci": "7.1.0",
|
|
112
109
|
"base64url": "3.0.1",
|
|
113
110
|
"chance": "1.1.13",
|
|
114
|
-
"npm-check-updates": "
|
|
115
|
-
"nyc": "
|
|
111
|
+
"npm-check-updates": "22.2.0",
|
|
112
|
+
"nyc": "18.0.0",
|
|
116
113
|
"portfinder": "1.0.38",
|
|
117
|
-
"pre-commit": "
|
|
114
|
+
"pre-commit": "2.0.0",
|
|
118
115
|
"proxyquire": "2.1.3",
|
|
119
116
|
"replace": "1.2.2",
|
|
120
117
|
"rewire": "9.0.1",
|
|
121
|
-
"sinon": "
|
|
118
|
+
"sinon": "22.0.0",
|
|
122
119
|
"standard": "17.1.2",
|
|
123
120
|
"standard-version": "9.5.0",
|
|
124
121
|
"tap-spec": "5.0.0",
|
|
@@ -127,15 +124,13 @@
|
|
|
127
124
|
"tapes": "4.1.0"
|
|
128
125
|
},
|
|
129
126
|
"overrides": {
|
|
130
|
-
"
|
|
131
|
-
"
|
|
132
|
-
"
|
|
133
|
-
"
|
|
134
|
-
"
|
|
135
|
-
"nanoid": "
|
|
136
|
-
"postcss":
|
|
137
|
-
"nanoid": "5.1.5"
|
|
138
|
-
},
|
|
127
|
+
"axios": "1.16.1",
|
|
128
|
+
"qs": "6.14.2",
|
|
129
|
+
"brace-expansion": "1.1.13",
|
|
130
|
+
"form-data": "4.0.5",
|
|
131
|
+
"convict": "6.2.5",
|
|
132
|
+
"nanoid": "3.3.11",
|
|
133
|
+
"postcss": "8.5.10",
|
|
139
134
|
"shins": {
|
|
140
135
|
"ejs": "3.1.10",
|
|
141
136
|
"sanitize-html": "2.12.1",
|
|
@@ -148,15 +143,22 @@
|
|
|
148
143
|
"swagger2openapi": "7.0.8"
|
|
149
144
|
},
|
|
150
145
|
"markdown-it": "12.3.2",
|
|
151
|
-
"fast-xml-parser": "5.
|
|
146
|
+
"fast-xml-parser": "5.7.0",
|
|
152
147
|
"trim": "0.0.3",
|
|
153
148
|
"cross-spawn": "7.0.6",
|
|
154
149
|
"yargs-parser": "21.1.1",
|
|
155
150
|
"jws": "3.2.3",
|
|
156
151
|
"validator": "13.15.22",
|
|
157
|
-
"lodash": "4.
|
|
158
|
-
"lodash-es": "4.
|
|
159
|
-
"undici": "
|
|
152
|
+
"lodash": "4.18.1",
|
|
153
|
+
"lodash-es": "4.18.1",
|
|
154
|
+
"undici": "6.25.0",
|
|
155
|
+
"@hapi/content": "6.0.1",
|
|
156
|
+
"replace": {
|
|
157
|
+
"minimatch": "3.1.4"
|
|
158
|
+
},
|
|
159
|
+
"path-to-regexp": "0.1.13",
|
|
160
|
+
"picomatch": "2.3.2",
|
|
161
|
+
"yaml": "2.9.0"
|
|
160
162
|
},
|
|
161
163
|
"peerDependencies": {
|
|
162
164
|
"@mojaloop/central-services-error-handling": "13.x.x",
|
package/src/config.js
CHANGED
|
@@ -9,13 +9,6 @@ const config = convict({
|
|
|
9
9
|
env: 'SHARED_CACHE_LOG_LEVEL'
|
|
10
10
|
},
|
|
11
11
|
|
|
12
|
-
httpLogLevel: {
|
|
13
|
-
doc: 'Log level for HTTP wrapper.',
|
|
14
|
-
format: logLevelValues,
|
|
15
|
-
default: logLevelsMap.warn,
|
|
16
|
-
env: 'LOG_LEVEL_HTTP'
|
|
17
|
-
},
|
|
18
|
-
|
|
19
12
|
defaultTtlSec: {
|
|
20
13
|
doc: 'Default cache TTL.',
|
|
21
14
|
format: Number,
|
package/src/enums/endpoints.js
CHANGED
|
@@ -102,12 +102,22 @@ const FspEndpointTypes = {
|
|
|
102
102
|
TP_CB_URL_SERVICES_GET: 'TP_CB_URL_SERVICES_GET',
|
|
103
103
|
TP_CB_URL_SERVICES_PUT: 'TP_CB_URL_SERVICES_PUT',
|
|
104
104
|
TP_CB_URL_SERVICES_PUT_ERROR: 'TP_CB_URL_SERVICES_PUT_ERROR',
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
105
|
+
TPP_CB_URL_ACCOUNTS_REQUEST_POST: 'TPP_CB_URL_ACCOUNTS_REQUEST_POST',
|
|
106
|
+
TPP_CB_URL_ACCOUNTS_REQUEST_PUT: 'TPP_CB_URL_ACCOUNTS_REQUEST_PUT',
|
|
107
|
+
TPP_CB_URL_ACCOUNTS_REQUEST_PUT_ERROR: 'TPP_CB_URL_ACCOUNTS_REQUEST_PUT_ERROR',
|
|
108
|
+
TPP_CB_URL_ACCOUNTS_REQUEST_GET: 'TPP_CB_URL_ACCOUNTS_REQUEST_GET',
|
|
109
109
|
TPP_CB_URL_ACCOUNTS_GET: 'TPP_CB_URL_ACCOUNTS_GET',
|
|
110
|
-
TPP_CB_URL_ACCOUNTS_PUT: 'TPP_CB_URL_ACCOUNTS_PUT'
|
|
110
|
+
TPP_CB_URL_ACCOUNTS_PUT: 'TPP_CB_URL_ACCOUNTS_PUT',
|
|
111
|
+
TPP_CB_URL_CONSENT_REQUEST_POST: 'TPP_CB_URL_CONSENT_REQUEST_POST',
|
|
112
|
+
TPP_CB_URL_CONSENT_REQUEST_GET: 'TPP_CB_URL_CONSENT_REQUEST_GET',
|
|
113
|
+
TPP_CB_URL_CONSENT_REQUEST_PUT: 'TPP_CB_URL_CONSENT_REQUEST_PUT',
|
|
114
|
+
TPP_CB_URL_CONSENT_REQUEST_PATCH: 'TPP_CB_URL_CONSENT_REQUEST_PATCH',
|
|
115
|
+
TPP_CB_URL_CONSENT_REQUEST_PUT_ERROR: 'TPP_CB_URL_CONSENT_REQUEST_PUT_ERROR',
|
|
116
|
+
TPP_CB_URL_CONSENTS_POST: 'TPP_CB_URL_CONSENTS_POST',
|
|
117
|
+
TPP_CB_URL_CONSENTS_GET: 'TPP_CB_URL_CONSENTS_GET',
|
|
118
|
+
TPP_CB_URL_CONSENTS_PUT: 'TPP_CB_URL_CONSENTS_PUT',
|
|
119
|
+
TPP_CB_URL_CONSENTS_PUT_ERROR: 'TPP_CB_URL_CONSENTS_PUT_ERROR',
|
|
120
|
+
TPP_CB_URL_CONSENTS_DELETE: 'TPP_CB_URL_CONSENTS_DELETE'
|
|
111
121
|
}
|
|
112
122
|
|
|
113
123
|
const FspEndpointTemplates = {
|
|
@@ -170,13 +180,23 @@ const FspEndpointTemplates = {
|
|
|
170
180
|
TP_SERVICES_GET: '/services/{{ServiceType}}',
|
|
171
181
|
TP_SERVICES_PUT: '/services/{{ServiceType}}',
|
|
172
182
|
TP_SERVICES_PUT_ERROR: '/services/{{ServiceType}}/error',
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
183
|
+
TPP_ACCOUNTS_REQUEST_POST: '/tppAccountsRequest',
|
|
184
|
+
TPP_ACCOUNTS_REQUEST_PUT: '/tppAccountsRequest/{{ID}}',
|
|
185
|
+
TPP_ACCOUNTS_REQUEST_PUT_ERROR: '/tppAccountsRequest/{{ID}}/error',
|
|
186
|
+
TPP_ACCOUNTS_REQUEST_GET: '/tppAccountsRequest/{{ID}}',
|
|
177
187
|
TPP_ACCOUNTS_GET: '/tppAccounts/{{ID}}/{{SignedChallenge}}',
|
|
178
188
|
TPP_ACCOUNTS_PUT: '/tppAccounts/{{ID}}',
|
|
179
|
-
TPP_ACCOUNTS_PUT_ERROR: '/tppAccounts/{{ID}}/error'
|
|
189
|
+
TPP_ACCOUNTS_PUT_ERROR: '/tppAccounts/{{ID}}/error',
|
|
190
|
+
TPP_CONSENT_REQUEST_POST: '/tppConsentRequests',
|
|
191
|
+
TPP_CONSENT_REQUEST_GET: '/tppConsentRequests/{{ID}}',
|
|
192
|
+
TPP_CONSENT_REQUEST_PUT: '/tppConsentRequests/{{ID}}',
|
|
193
|
+
TPP_CONSENT_REQUEST_PATCH: '/tppConsentRequests/{{ID}}',
|
|
194
|
+
TPP_CONSENT_REQUEST_PUT_ERROR: '/tppConsentRequests/{{ID}}/error',
|
|
195
|
+
TPP_CONSENTS_POST: '/tppConsents',
|
|
196
|
+
TPP_CONSENTS_GET: '/tppConsents/{{ID}}',
|
|
197
|
+
TPP_CONSENTS_PUT: '/tppConsents/{{ID}}',
|
|
198
|
+
TPP_CONSENTS_PUT_ERROR: '/tppConsents/{{ID}}/error',
|
|
199
|
+
TPP_CONSENTS_DELETE: '/tppConsents/{{ID}}'
|
|
180
200
|
}
|
|
181
201
|
|
|
182
202
|
module.exports = {
|
package/src/index.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Utils as HapiUtil, Server } from '@hapi/hapi'
|
|
2
2
|
import { ILogger } from '@mojaloop/central-services-logger/src/contextLogger'
|
|
3
3
|
import { Knex } from 'knex';
|
|
4
|
-
import { AxiosRequestConfig, AxiosResponse, ResponseType as AxiosResponseType } from 'axios'
|
|
5
4
|
import IORedis from 'ioredis';
|
|
6
5
|
|
|
7
6
|
declare namespace CentralServicesShared {
|
|
@@ -116,10 +115,10 @@ declare namespace CentralServicesShared {
|
|
|
116
115
|
TP_CB_URL_SERVICES_GET = 'TP_CB_URL_SERVICES_GET',
|
|
117
116
|
TP_CB_URL_SERVICES_PUT = 'TP_CB_URL_SERVICES_PUT',
|
|
118
117
|
TP_CB_URL_SERVICES_PUT_ERROR = 'TP_CB_URL_SERVICES_PUT_ERROR',
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
118
|
+
TP_CB_URL_ACCOUNTS_REQUEST_POST = 'TP_CB_URL_ACCOUNTS_REQUEST_POST',
|
|
119
|
+
TP_CB_URL_ACCOUNTS_REQUEST_PUT = 'TP_CB_URL_ACCOUNTS_REQUEST_PUT',
|
|
120
|
+
TP_CB_URL_ACCOUNTS_REQUEST_PUT_ERROR = 'TP_CB_URL_ACCOUNTS_REQUEST_PUT_ERROR',
|
|
121
|
+
TP_CB_URL_ACCOUNTS_REQUEST_GET = 'TP_CB_URL_ACCOUNTS_REQUEST_GET',
|
|
123
122
|
}
|
|
124
123
|
interface EndPointsEnum {
|
|
125
124
|
EndpointType: {
|
|
@@ -192,10 +191,10 @@ declare namespace CentralServicesShared {
|
|
|
192
191
|
TP_CB_URL_SERVICES_GET: FspEndpointTypesEnum.TP_CB_URL_SERVICES_GET;
|
|
193
192
|
TP_CB_URL_SERVICES_PUT: FspEndpointTypesEnum.TP_CB_URL_SERVICES_PUT;
|
|
194
193
|
TP_CB_URL_SERVICES_PUT_ERROR: FspEndpointTypesEnum.TP_CB_URL_SERVICES_PUT_ERROR;
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
194
|
+
TP_CB_URL_ACCOUNTS_REQUEST_POST: FspEndpointTypesEnum.TP_CB_URL_ACCOUNTS_REQUEST_POST;
|
|
195
|
+
TP_CB_URL_ACCOUNTS_REQUEST_PUT: FspEndpointTypesEnum.TP_CB_URL_ACCOUNTS_REQUEST_PUT;
|
|
196
|
+
TP_CB_URL_ACCOUNTS_REQUEST_PUT_ERROR: FspEndpointTypesEnum.TP_CB_URL_ACCOUNTS_REQUEST_PUT_ERROR;
|
|
197
|
+
TP_CB_URL_ACCOUNTS_REQUEST_GET: FspEndpointTypesEnum.TP_CB_URL_ACCOUNTS_REQUEST_GET;
|
|
199
198
|
};
|
|
200
199
|
FspEndpointTemplates: {
|
|
201
200
|
TRANSACTION_REQUEST_POST: string;
|
|
@@ -764,27 +763,9 @@ declare namespace CentralServicesShared {
|
|
|
764
763
|
accept: string
|
|
765
764
|
}
|
|
766
765
|
|
|
767
|
-
type RequestParams = {
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
source: string,
|
|
771
|
-
destination?: string,
|
|
772
|
-
hubNameRegex: RegExp,
|
|
773
|
-
method?: RestMethodsEnum,
|
|
774
|
-
payload?: any,
|
|
775
|
-
params?: AxiosRequestConfig['params'],
|
|
776
|
-
responseType?: AxiosResponseType,
|
|
777
|
-
span?: any,
|
|
778
|
-
jwsSigner?: any,
|
|
779
|
-
protocolVersions?: ProtocolVersionsType,
|
|
780
|
-
apiType?: ApiTypeValues,
|
|
781
|
-
axiosRequestOptionsOverride?: Partial<AxiosRequestConfig>,
|
|
782
|
-
logger?: ILogger,
|
|
783
|
-
peerService?: string
|
|
784
|
-
}
|
|
785
|
-
export interface Request {
|
|
786
|
-
sendRequest(params: RequestParams): Promise<AxiosResponse>
|
|
787
|
-
sendBaseRequest(params?: AxiosRequestConfig & { logger?: ILogger, peerService?: string }): Promise<AxiosResponse>
|
|
766
|
+
type RequestParams = { url: string, headers: HapiUtil.Dictionary<string>, source: string, destination: string, hubNameRegex: RegExp, method?: RestMethodsEnum, payload?: any, responseType?: string, span?: any, jwsSigner?: any, protocolVersions?: ProtocolVersionsType }
|
|
767
|
+
interface Request {
|
|
768
|
+
sendRequest(params: RequestParams): Promise<any>
|
|
788
769
|
}
|
|
789
770
|
|
|
790
771
|
interface Kafka {
|
|
@@ -81,9 +81,10 @@ const plugin = {
|
|
|
81
81
|
|
|
82
82
|
if (needProxySourceValidation) validateProxySourceHeaders(request.headers)
|
|
83
83
|
|
|
84
|
-
//
|
|
85
|
-
//
|
|
86
|
-
|
|
84
|
+
// Require accept header for request-initiating methods (GET, POST, DELETE)
|
|
85
|
+
// per FSPIOP API spec. PUT/PATCH callbacks do not require Accept.
|
|
86
|
+
const methodRequiresAccept = ['get', 'post', 'delete'].includes(request.method.toLowerCase())
|
|
87
|
+
if (methodRequiresAccept || request.headers.accept) {
|
|
87
88
|
if (request.headers.accept === undefined) {
|
|
88
89
|
throw createFSPIOPError(Enums.FSPIOPErrorCodes.MISSING_ELEMENT, errorMessages.REQUIRE_ACCEPT_HEADER)
|
|
89
90
|
}
|
|
@@ -30,7 +30,6 @@
|
|
|
30
30
|
const { env } = require('node:process')
|
|
31
31
|
const { asyncStorage } = require('@mojaloop/central-services-logger/src/contextLogger')
|
|
32
32
|
const { logger } = require('../../../logger')
|
|
33
|
-
const { incomingRequestAttributesDto } = require('../../otelDto')
|
|
34
33
|
|
|
35
34
|
const INTERNAL_ROUTES = env.LOG_INTERNAL_ROUTES ? env.LOG_INTERNAL_ROUTES.split(',') : ['/health', '/metrics', '/live']
|
|
36
35
|
const TRACE_ID_HEADER = env.LOG_TRACE_ID_HEADER ?? 'traceid'
|
|
@@ -62,11 +61,13 @@ const loggingPlugin = {
|
|
|
62
61
|
server.ext({
|
|
63
62
|
type: 'onRequest',
|
|
64
63
|
method: (request, h) => {
|
|
65
|
-
const
|
|
64
|
+
const { path, method, headers, payload, query } = request
|
|
65
|
+
const { remoteAddress } = request.info
|
|
66
|
+
const requestId = request.info.id = `${request.info.id}__${headers[traceIdHeader]}`
|
|
66
67
|
asyncStorage.enterWith({ requestId })
|
|
67
68
|
|
|
68
|
-
if (shouldLog(
|
|
69
|
-
|
|
69
|
+
if (shouldLog(path)) {
|
|
70
|
+
log.info(`[==> req] ${method.toUpperCase()} ${path}`, { headers, payload, query, remoteAddress })
|
|
70
71
|
}
|
|
71
72
|
return h.continue
|
|
72
73
|
}
|
|
@@ -76,7 +77,16 @@ const loggingPlugin = {
|
|
|
76
77
|
type: 'onPreResponse',
|
|
77
78
|
method: (request, h) => {
|
|
78
79
|
if (shouldLog(request.path)) {
|
|
79
|
-
|
|
80
|
+
const { path, method, payload, response } = request
|
|
81
|
+
const { received } = request.info
|
|
82
|
+
|
|
83
|
+
const statusCode = response instanceof Error
|
|
84
|
+
? response.output?.statusCode
|
|
85
|
+
: response.statusCode
|
|
86
|
+
const { output } = response
|
|
87
|
+
const respTimeSec = ((Date.now() - received) / 1000).toFixed(1)
|
|
88
|
+
|
|
89
|
+
log.info(`[<== ${statusCode}] ${method.toUpperCase()} ${path} [${respTimeSec} sec]`, { payload, output })
|
|
80
90
|
}
|
|
81
91
|
return h.continue
|
|
82
92
|
}
|
|
@@ -84,69 +94,4 @@ const loggingPlugin = {
|
|
|
84
94
|
}
|
|
85
95
|
}
|
|
86
96
|
|
|
87
|
-
/**
|
|
88
|
-
* @param {import('@hapi/hapi').Request} request
|
|
89
|
-
* @param {ILogger} log
|
|
90
|
-
* @returns OTelAttributes
|
|
91
|
-
*/
|
|
92
|
-
const logRequest = (request, log) => {
|
|
93
|
-
log.info(`[==> req] ${request.method.toUpperCase()} ${request.path} `, {
|
|
94
|
-
headers: extractHeadersForLogs(request.headers),
|
|
95
|
-
// payload is not parsed yet
|
|
96
|
-
...extractAttributes({ request })
|
|
97
|
-
})
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* @param {import('@hapi/hapi').Request} request
|
|
102
|
-
* @param {ILogger} log
|
|
103
|
-
* @returns OTelAttributes
|
|
104
|
-
*/
|
|
105
|
-
const logResponse = (request, log) => {
|
|
106
|
-
const { method, path, response } = request
|
|
107
|
-
|
|
108
|
-
const statusCode = response instanceof Error
|
|
109
|
-
? response.output?.statusCode
|
|
110
|
-
: response?.statusCode
|
|
111
|
-
|
|
112
|
-
const errorType = response instanceof Error
|
|
113
|
-
? response.output?.payload?.error
|
|
114
|
-
: undefined
|
|
115
|
-
|
|
116
|
-
const durationSec = (Date.now() - request.info.received) / 1000
|
|
117
|
-
|
|
118
|
-
log.info(`[<== ${statusCode}] ${method.toUpperCase()} ${path} [${durationSec} s]`, {
|
|
119
|
-
headers: extractHeadersForLogs(response?.output?.headers),
|
|
120
|
-
payload: response?.output?.payload, // think if we need to log payload only with debug severity
|
|
121
|
-
...extractAttributes({ request, durationSec, statusCode, errorType })
|
|
122
|
-
})
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const extractAttributes = ({ request, durationSec, statusCode, errorType }) => {
|
|
126
|
-
return incomingRequestAttributesDto({
|
|
127
|
-
method: request.method,
|
|
128
|
-
path: request.path,
|
|
129
|
-
url: getFullUrl(request),
|
|
130
|
-
route: request.route.path,
|
|
131
|
-
serverAddress: request.info.hostname,
|
|
132
|
-
clientAddress: request.info.remoteAddress,
|
|
133
|
-
userAgent: request.headers['user-agent'],
|
|
134
|
-
requestId: request.info.id,
|
|
135
|
-
durationSec,
|
|
136
|
-
statusCode,
|
|
137
|
-
errorType
|
|
138
|
-
})
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/** @param {import('@hapi/hapi').Request} req */
|
|
142
|
-
const getFullUrl = (req) => {
|
|
143
|
-
const search = req.url?.search || ''
|
|
144
|
-
return `${req.server.info.protocol}://${req.info.host}${req.path}${search}`
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const extractHeadersForLogs = (headers = {}) => {
|
|
148
|
-
// todo: add impl.
|
|
149
|
-
return headers
|
|
150
|
-
}
|
|
151
|
-
|
|
152
97
|
module.exports = loggingPlugin
|
package/src/util/request.js
CHANGED
|
@@ -30,31 +30,29 @@
|
|
|
30
30
|
'use strict'
|
|
31
31
|
|
|
32
32
|
const http = require('node:http')
|
|
33
|
-
const
|
|
33
|
+
const request = require('axios')
|
|
34
34
|
const stringify = require('fast-safe-stringify')
|
|
35
35
|
const EventSdk = require('@mojaloop/event-sdk')
|
|
36
36
|
const ErrorHandler = require('@mojaloop/central-services-error-handling')
|
|
37
37
|
const Metrics = require('@mojaloop/central-services-metrics')
|
|
38
|
-
|
|
39
|
-
const
|
|
38
|
+
const Headers = require('./headers/transformer')
|
|
39
|
+
const enums = require('../enums')
|
|
40
|
+
const { logger } = require('../logger')
|
|
40
41
|
const { API_TYPES } = require('../constants')
|
|
41
42
|
const config = require('../config')
|
|
42
|
-
const enums = require('../enums')
|
|
43
|
-
const Headers = require('./headers/transformer')
|
|
44
|
-
const { outgoingRequestAttributesDto } = require('./otelDto')
|
|
45
43
|
|
|
46
44
|
const MISSING_FUNCTION_PARAMETERS = 'Missing parameters for function'
|
|
47
45
|
|
|
48
46
|
// Delete the default headers that the `axios` module inserts as they can brake our conventions.
|
|
49
47
|
// By default it would insert `"Accept":"application/json, text/plain, */*"`.
|
|
50
|
-
delete
|
|
48
|
+
delete request.defaults.headers.common.Accept
|
|
51
49
|
|
|
52
50
|
const keepAlive = (process.env.HTTP_AGENT_KEEP_ALIVE ?? 'true') === 'true'
|
|
53
|
-
|
|
51
|
+
logger.verbose('http keepAlive:', { keepAlive })
|
|
54
52
|
|
|
55
53
|
// Enable keepalive for http
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
request.defaults.httpAgent = new http.Agent({ keepAlive })
|
|
55
|
+
request.defaults.httpAgent.toJSON = () => ({})
|
|
58
56
|
|
|
59
57
|
/**
|
|
60
58
|
* @function sendRequest
|
|
@@ -79,12 +77,11 @@ axios.defaults.httpAgent.toJSON = () => ({})
|
|
|
79
77
|
* @param {SendRequestProtocolVersions | undefined} protocolVersions the config for Protocol versions to be used
|
|
80
78
|
* @param {'fspiop' | 'iso20022'} apiType the API type of the request being sent
|
|
81
79
|
* @param {object} axiosRequestOptionsOverride axios request options to override https://axios-http.com/docs/req_config
|
|
82
|
-
* @param {ILogger} [logger] ContextLogger instance with specific context
|
|
83
|
-
* @param {string} [peerService] Logical service name to call (for OTel)
|
|
84
80
|
* @param {regex} hubNameRegex hubName Regex
|
|
85
81
|
*
|
|
86
82
|
*@return {Promise<any>} The response for the request being sent or error object with response included
|
|
87
83
|
*/
|
|
84
|
+
|
|
88
85
|
const sendRequest = async ({
|
|
89
86
|
url,
|
|
90
87
|
headers,
|
|
@@ -99,8 +96,6 @@ const sendRequest = async ({
|
|
|
99
96
|
protocolVersions = undefined,
|
|
100
97
|
apiType = API_TYPES.fspiop,
|
|
101
98
|
axiosRequestOptionsOverride = {},
|
|
102
|
-
logger = createHttpLogger(),
|
|
103
|
-
peerService = '',
|
|
104
99
|
hubNameRegex
|
|
105
100
|
}) => {
|
|
106
101
|
const histTimerEnd = Metrics.getHistogram(
|
|
@@ -118,9 +113,6 @@ const sendRequest = async ({
|
|
|
118
113
|
// think, if we can just avoid checking "destination"
|
|
119
114
|
throw ErrorHandler.Factory.createInternalServerFSPIOPError(MISSING_FUNCTION_PARAMETERS)
|
|
120
115
|
}
|
|
121
|
-
|
|
122
|
-
const log = logger.child({ component: 'httpRequest' })
|
|
123
|
-
|
|
124
116
|
try {
|
|
125
117
|
const transformedHeaders = Headers.transformHeaders(headers, {
|
|
126
118
|
httpMethod: method,
|
|
@@ -134,10 +126,9 @@ const sendRequest = async ({
|
|
|
134
126
|
url,
|
|
135
127
|
method,
|
|
136
128
|
headers: transformedHeaders,
|
|
137
|
-
data: payload,
|
|
129
|
+
data: payload, // todo: think, if it's better to transform to ISO format here (based on apiType)
|
|
138
130
|
params,
|
|
139
131
|
responseType,
|
|
140
|
-
peerService,
|
|
141
132
|
timeout: config.get('httpRequestTimeoutMs'),
|
|
142
133
|
...axiosRequestOptionsOverride
|
|
143
134
|
}
|
|
@@ -158,18 +149,14 @@ const sendRequest = async ({
|
|
|
158
149
|
}
|
|
159
150
|
span.audit({ ...rest, payload }, EventSdk.AuditEventAction.egress)
|
|
160
151
|
}
|
|
161
|
-
|
|
162
|
-
const response = await
|
|
163
|
-
...requestOptions,
|
|
164
|
-
logger: log,
|
|
165
|
-
peerService
|
|
166
|
-
})
|
|
152
|
+
logger.debug('sendRequest::requestOptions:', { requestOptions })
|
|
153
|
+
const response = await request(requestOptions)
|
|
167
154
|
|
|
168
155
|
!!sendRequestSpan && await sendRequestSpan.finish()
|
|
169
156
|
histTimerEnd({ success: true, source, destination, method })
|
|
170
157
|
return response
|
|
171
158
|
} catch (error) {
|
|
172
|
-
|
|
159
|
+
logger.error('error in request.sendRequest:', {
|
|
173
160
|
code: error.code,
|
|
174
161
|
message: error.message,
|
|
175
162
|
stack: error.stack,
|
|
@@ -260,62 +247,6 @@ const sendRequest = async ({
|
|
|
260
247
|
}
|
|
261
248
|
}
|
|
262
249
|
|
|
263
|
-
// todo: think better name
|
|
264
|
-
// it's for http calls without params validation and transformHeaders
|
|
265
|
-
const sendBaseRequest = async ({
|
|
266
|
-
logger = createHttpLogger(),
|
|
267
|
-
peerService = '',
|
|
268
|
-
...reqOptions
|
|
269
|
-
} = {}) => {
|
|
270
|
-
const log = logger.child({ component: 'sendBaseRequest' })
|
|
271
|
-
const { method, url } = reqOptions
|
|
272
|
-
const methodUrl = `${method?.toUpperCase()} ${url}`
|
|
273
|
-
const startTime = Date.now()
|
|
274
|
-
|
|
275
|
-
let statusCode
|
|
276
|
-
let errorType
|
|
277
|
-
|
|
278
|
-
try {
|
|
279
|
-
log.debug(`[-->] options for ${methodUrl}: `, { reqOptions })
|
|
280
|
-
|
|
281
|
-
const response = await axios(reqOptions)
|
|
282
|
-
|
|
283
|
-
statusCode = response?.status
|
|
284
|
-
log.verbose(`[<--] details of ${methodUrl}: `, {
|
|
285
|
-
data: response?.data,
|
|
286
|
-
headers: response?.headers, // todo: extract only needed headers
|
|
287
|
-
statusCode,
|
|
288
|
-
reqOptions
|
|
289
|
-
})
|
|
290
|
-
|
|
291
|
-
return response
|
|
292
|
-
} catch (error) {
|
|
293
|
-
statusCode = error.response?.status
|
|
294
|
-
errorType = error.code
|
|
295
|
-
throw error // todo: think, if we need to rethrow our custom error here
|
|
296
|
-
} finally {
|
|
297
|
-
const severity = typeof statusCode === 'number'
|
|
298
|
-
? (statusCode >= 200 && statusCode < 300 ? 'info' : 'warn')
|
|
299
|
-
: 'error'
|
|
300
|
-
const durationSec = (Date.now() - startTime) / 1000
|
|
301
|
-
log[severity](`[<-- ${statusCode || errorType || ''}] ${methodUrl} [${durationSec} s]:`, outgoingRequestAttributesDto({
|
|
302
|
-
method,
|
|
303
|
-
url,
|
|
304
|
-
statusCode,
|
|
305
|
-
durationSec,
|
|
306
|
-
errorType,
|
|
307
|
-
peerService
|
|
308
|
-
}))
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
const createHttpLogger = () => {
|
|
313
|
-
const logger = globalLogger.child()
|
|
314
|
-
logger.setLevel(config.get('httpLogLevel'))
|
|
315
|
-
return logger
|
|
316
|
-
}
|
|
317
|
-
|
|
318
250
|
module.exports = {
|
|
319
|
-
sendRequest
|
|
320
|
-
sendBaseRequest
|
|
251
|
+
sendRequest
|
|
321
252
|
}
|
|
@@ -151,7 +151,8 @@ Test('headerValidation plugin test', async (pluginTest) => {
|
|
|
151
151
|
t.end()
|
|
152
152
|
})
|
|
153
153
|
|
|
154
|
-
pluginTest.test('accept validation is
|
|
154
|
+
pluginTest.test('accept validation is performed on post requests without an accept header', async t => {
|
|
155
|
+
const fspiopCode = ErrorHandling.Enums.FSPIOPErrorCodes.MISSING_ELEMENT
|
|
155
156
|
const opts = {
|
|
156
157
|
url: `/${resource}`,
|
|
157
158
|
headers: {
|
|
@@ -159,11 +160,25 @@ Test('headerValidation plugin test', async (pluginTest) => {
|
|
|
159
160
|
date: new Date().toUTCString()
|
|
160
161
|
}
|
|
161
162
|
}
|
|
162
|
-
await
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
163
|
+
const res = await server.inject({ ...opts, method: 'post' })
|
|
164
|
+
t.is(res.statusCode, fspiopCode.httpStatusCode)
|
|
165
|
+
const payload = JSON.parse(res.payload)
|
|
166
|
+
t.is(payload.apiErrorCode.code, fspiopCode.code)
|
|
167
|
+
t.is(payload.message, errorMessages.REQUIRE_ACCEPT_HEADER)
|
|
168
|
+
t.end()
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
pluginTest.test('accept validation is not required for put requests without an accept header', async t => {
|
|
172
|
+
const opts = {
|
|
173
|
+
url: `/${resource}`,
|
|
174
|
+
headers: {
|
|
175
|
+
'content-type': generateContentTypeHeader(resource, 1),
|
|
176
|
+
date: new Date().toUTCString()
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
const res = await server.inject({ ...opts, method: 'put' })
|
|
180
|
+
t.is(res.payload, '')
|
|
181
|
+
t.is(res.statusCode, 202)
|
|
167
182
|
t.end()
|
|
168
183
|
})
|
|
169
184
|
|
|
@@ -134,7 +134,7 @@ Tape('loggingPlugin Tests -->', (pluginTests) => {
|
|
|
134
134
|
t.true(statusCode === 500, 'handler failed')
|
|
135
135
|
t.true(log.info.callCount === 2, 'log request/response')
|
|
136
136
|
t.true(log.info.lastCall.firstArg.startsWith('[<== 500]'), 'error code is logged')
|
|
137
|
-
t.ok(log.info.lastCall.lastArg.payload, 'error output is logged')
|
|
137
|
+
t.ok(log.info.lastCall.lastArg.output.payload, 'error output is logged')
|
|
138
138
|
}))
|
|
139
139
|
|
|
140
140
|
pluginTests.test('should not log requests on internal routes', tryCatchEndTest(async t => {
|
|
@@ -10,35 +10,6 @@ const Enum = require('../../../src/enums')
|
|
|
10
10
|
const Helper = require('../../util/helper')
|
|
11
11
|
const Metrics = require('@mojaloop/central-services-metrics')
|
|
12
12
|
const Uuid = require('uuid4')
|
|
13
|
-
const JwsSigner = require('@mojaloop/sdk-standard-components').Jws.signer
|
|
14
|
-
|
|
15
|
-
const signingKey = `-----BEGIN RSA PRIVATE KEY-----
|
|
16
|
-
MIIEowIBAAKCAQEA0eJEh3Op5p6x137lRkAsvmEBbd32dbRChrCUItZbtxjf/qfB
|
|
17
|
-
yD5k8Hn4n4vbqzP8XSGS0f6KmNC+iRaP74HVgzAqc4Uid4J8dtSBq3VmucYQYzLc
|
|
18
|
-
101QjuvD+SKmZwlw/q0PtulmqlASI2SbMfwcAraMi6ab7v5W4EGNeIPLEIo3BXsQ
|
|
19
|
-
DTCWqiZb7aXkHkcY7sOjAzK/2bNGYFmAthdYrHzvCkqnJ7LAHX3Oj7rJea5MqtuN
|
|
20
|
-
B9POZYaD10n9JuYWdwPqLrw6/hVgPSFEy+ulrVbXf54ZH0dfMThAYRvFrT81yulk
|
|
21
|
-
H95JhXWGdi6cTp6t8LVOKFhnNfxjWw0Jayj9xwIDAQABAoIBADB2u/Y/CgNbr5sg
|
|
22
|
-
DRccqHhJdAgHkep59kadrYch0knEL6zg1clERxCUSYmlxNKSjXp/zyQ4T46b3PNQ
|
|
23
|
-
x2m5pDDHxXWpT10jP1Q9G7gYwuCw0IXnb8EzdB+cZ0M28g+myXW1RoSo/nDjTlzn
|
|
24
|
-
1UJEgb9Kocd5cFZOWocr+9vRKumlZULMsA8yiNwlAfJHcMBM7acsa3myCqVhLyWt
|
|
25
|
-
4BQylVuLFa+A6QzpMXEwFCq8EOXf07gl1XVzC6LJ1fTa9gVM3N+YE+oEXKrsHCxG
|
|
26
|
-
/ACgKsjepL27QjJ7qvecWPP0F2LxEZYOm5tbXaKJTobzQUJHgUokanZMhjYprDsZ
|
|
27
|
-
zumLw9kCgYEA/DUWcnLeImlfq/EYdhejkl3J+WX3vhS23OqVgY1amu7CZzaai6vt
|
|
28
|
-
H0TRc8Zsbi4jgmFDU8PFzytP6qz6Tgom4R736z6oBi7bjnGyN17/NSbf+DaRVcM6
|
|
29
|
-
vnZr7jNC2FJlECmIN+dkwUA/YCr2SA7hxZXM9mIYSc+6+glDiIO5Cf0CgYEA1Qo/
|
|
30
|
-
uQbVHhW+Cp8H0kdMuhwUbkBquRrxRZlXS1Vrf3f9me9JLUy9UPWb3y3sKVurG5+O
|
|
31
|
-
SIlr4hDcZyXdE198MtDMhBIGqU9ORSjppJDNDVvtt+n2FD4XmWIU70vKBJBivX0+
|
|
32
|
-
Bow6yduis+p12fuvpvpnKCz8UjOgOQJhLZ4GQBMCgYBP6gpozVjxkm4ML2LO2IKt
|
|
33
|
-
+CXtbo/nnOysZ3BkEoQpH4pd5gFmTF3gUJAFnVPyPZBm2abZvejJ0jGKbLELVVAo
|
|
34
|
-
eQWZdssK2oIbSo9r2CAJmX3SSogWorvUafWdDoUZwlHfoylUfW+BhHgQYsyS3JRR
|
|
35
|
-
ZTwCveZwTPA0FgdeFE7niQKBgQCHaD8+ZFhbCejDqXb4MXdUJ3rY5Lqwsq491YwF
|
|
36
|
-
huKPn32iNNQnJcqCxclv3iln1Cr6oLx34Fig1KSyLv/IS32OcuY635Y6UPznumxe
|
|
37
|
-
u+aJIjADIILXNOwdAplZy6s4oWkRFaSx1rmbCa3tew2zImTv1eJxR76MpOGmupt3
|
|
38
|
-
uiQw3wKBgFjBT/aVKdBeHeP1rIHHldQV5QQxZNkc6D3qn/oAFcwpj9vcGfRjQWjO
|
|
39
|
-
ARzXM2vUWEet4OVn3DXyOdaWFR1ppehz7rAWBiPgsMg4fjAusYb9Mft1GMxMzuwT
|
|
40
|
-
Oyqsp6pzAWFrCD3JAoTLxClV+j5m+SXZ/ItD6ziGpl/h7DyayrFZ
|
|
41
|
-
-----END RSA PRIVATE KEY-----`
|
|
42
13
|
|
|
43
14
|
Test('ParticipantEndpoint Model Test', modelTest => {
|
|
44
15
|
let sandbox
|
|
@@ -483,10 +454,9 @@ Test('ParticipantEndpoint Model Test', modelTest => {
|
|
|
483
454
|
method: 'post',
|
|
484
455
|
headers: Helper.defaultHeaders(fsp, Enum.Http.HeaderResources.PARTICIPANTS, payeefsp)
|
|
485
456
|
}
|
|
486
|
-
const jwsSigner =
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
})
|
|
457
|
+
const jwsSigner = {
|
|
458
|
+
getSignature: () => 'mock-jws-signature'
|
|
459
|
+
}
|
|
490
460
|
request = sandbox.stub().returns({ status: 200 })
|
|
491
461
|
Model = proxyquire('../../../src/util/request', { axios: request })
|
|
492
462
|
const signSpy = Sinon.spy(jwsSigner, 'getSignature')
|
|
@@ -517,10 +487,9 @@ Test('ParticipantEndpoint Model Test', modelTest => {
|
|
|
517
487
|
method: 'post',
|
|
518
488
|
headers: Helper.defaultHeaders(fsp, Enum.Http.HeaderResources.PARTICIPANTS, payeefsp)
|
|
519
489
|
}
|
|
520
|
-
const jwsSigner =
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
})
|
|
490
|
+
const jwsSigner = {
|
|
491
|
+
getSignature: () => 'mock-jws-signature'
|
|
492
|
+
}
|
|
524
493
|
request = sandbox.stub().returns({ status: 200 })
|
|
525
494
|
Model = proxyquire('../../../src/util/request', { axios: request })
|
|
526
495
|
const signSpy = Sinon.spy(jwsSigner, 'getSignature')
|
package/src/util/otelDto.js
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
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
|
-
/* istanbul ignore file */
|
|
28
|
-
|
|
29
|
-
const otel = require('@opentelemetry/semantic-conventions')
|
|
30
|
-
|
|
31
|
-
const ATTR_SERVICE_PEER_NAME = 'service.peer.name' // using string literal because ATTR_SERVICE_PEER_NAME is only available in @opentelemetry/semantic-conventions/incubating as of now
|
|
32
|
-
const CUSTOM_REQUEST_ID = 'request.id'
|
|
33
|
-
|
|
34
|
-
/** @typedef { attributes: Record<string, any> } OTelAttributes */
|
|
35
|
-
|
|
36
|
-
/** @returns OTelAttributes */
|
|
37
|
-
const outgoingRequestAttributesDto = ({
|
|
38
|
-
method, url, durationSec, statusCode, errorType, peerService
|
|
39
|
-
}) => ({
|
|
40
|
-
attributes: {
|
|
41
|
-
[otel.ATTR_HTTP_REQUEST_METHOD]: method,
|
|
42
|
-
[otel.ATTR_URL_FULL]: url,
|
|
43
|
-
[otel.METRIC_HTTP_CLIENT_REQUEST_DURATION]: durationSec, // 'duration.ms' is a custom attribute
|
|
44
|
-
...(statusCode && { [otel.ATTR_HTTP_RESPONSE_STATUS_CODE]: statusCode }),
|
|
45
|
-
...(errorType && { [otel.ATTR_ERROR_TYPE]: errorType }),
|
|
46
|
-
...(peerService && { [ATTR_SERVICE_PEER_NAME]: peerService })
|
|
47
|
-
// peerService - logical service name, must be explicitly provided by caller (not derived from URL hostname)
|
|
48
|
-
// think if we should extract it for internal http://... calls from url hostname
|
|
49
|
-
}
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
/** @returns OTelAttributes */
|
|
53
|
-
const incomingRequestAttributesDto = ({
|
|
54
|
-
method, url, path, route,
|
|
55
|
-
serverAddress, clientAddress, userAgent, durationSec, requestId, statusCode, errorType
|
|
56
|
-
}) => ({
|
|
57
|
-
attributes: {
|
|
58
|
-
[otel.ATTR_HTTP_REQUEST_METHOD]: method,
|
|
59
|
-
[otel.ATTR_URL_FULL]: url,
|
|
60
|
-
[otel.ATTR_URL_PATH]: path,
|
|
61
|
-
[otel.ATTR_HTTP_ROUTE]: route,
|
|
62
|
-
[otel.ATTR_SERVER_ADDRESS]: serverAddress,
|
|
63
|
-
[otel.ATTR_CLIENT_ADDRESS]: clientAddress,
|
|
64
|
-
[otel.ATTR_USER_AGENT_ORIGINAL]: userAgent,
|
|
65
|
-
[CUSTOM_REQUEST_ID]: requestId,
|
|
66
|
-
...(durationSec && { [otel.METRIC_HTTP_SERVER_REQUEST_DURATION]: durationSec }), // 'duration.ms' is a custom attribute
|
|
67
|
-
...(statusCode && { [otel.ATTR_HTTP_RESPONSE_STATUS_CODE]: statusCode }),
|
|
68
|
-
...(errorType && { [otel.ATTR_ERROR_TYPE]: errorType })
|
|
69
|
-
}
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
module.exports = {
|
|
73
|
-
outgoingRequestAttributesDto,
|
|
74
|
-
incomingRequestAttributesDto
|
|
75
|
-
}
|