@mojaloop/central-services-shared 18.4.0-snapshot.8 → 18.4.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/.circleci/config.yml +2 -2
- package/CHANGELOG.md +64 -0
- package/CODEOWNERS +1 -1
- package/LICENSE.md +1 -1
- package/audit-ci.jsonc +3 -1
- package/package.json +29 -18
- package/src/enums/endpoints.js +1 -1
- package/src/enums/events.js +9 -0
- package/src/enums/http.js +11 -1
- package/src/enums/kafka.js +24 -0
- package/src/enums/transfers.js +1 -0
- package/src/index.d.ts +51 -1
- package/src/util/endpoints.js +1 -1
- package/src/util/id.js +40 -0
- package/src/util/index.js +2 -0
- package/src/util/kafka/index.js +1 -1
- package/src/util/request.js +4 -0
- package/test/unit/util/endpoints.test.js +23 -0
- package/test/unit/util/id.test.js +64 -0
- package/test/unit/util/request.test.js +1 -0
package/.circleci/config.yml
CHANGED
|
@@ -76,7 +76,7 @@ defaults_configure_nvm: &defaults_configure_nvm
|
|
|
76
76
|
|
|
77
77
|
if [ -f "$NVM_DIR" ]; then
|
|
78
78
|
echo "==> $NVM_DIR exists. Skipping steps 2-4!"
|
|
79
|
-
else
|
|
79
|
+
else
|
|
80
80
|
echo "==> $NVM_DIR does not exists. Executing steps 2-4!"
|
|
81
81
|
|
|
82
82
|
echo "2. Installing NVM"
|
|
@@ -123,7 +123,7 @@ executors:
|
|
|
123
123
|
BASH_ENV: /etc/profile ## Ref: https://circleci.com/docs/env-vars/#alpine-linux
|
|
124
124
|
NVM_ARCH_UNOFFICIAL_OVERRIDE: x64-musl ## Ref: https://github.com/nvm-sh/nvm/issues/1102#issuecomment-550572252
|
|
125
125
|
docker:
|
|
126
|
-
- image: node:
|
|
126
|
+
- image: node:18.17.1-alpine
|
|
127
127
|
|
|
128
128
|
default-machine:
|
|
129
129
|
working_directory: *WORKING_DIR
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,70 @@
|
|
|
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.4.0](https://github.com/mojaloop/central-services-shared/compare/v18.3.8...v18.4.0) (2024-06-20)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* add shared resources for fx functionality ([#384](https://github.com/mojaloop/central-services-shared/issues/384)) ([8bec55c](https://github.com/mojaloop/central-services-shared/commit/8bec55c7077882e8c1b9e767a15396f2c59b7220)), closes [mojaloop/#3689](https://github.com/mojaloop/project/issues/3689)
|
|
11
|
+
|
|
12
|
+
### [18.3.8](https://github.com/mojaloop/central-services-shared/compare/v18.3.7...v18.3.8) (2024-06-11)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Chore
|
|
16
|
+
|
|
17
|
+
* dependency updates and minor maintenance ([#383](https://github.com/mojaloop/central-services-shared/issues/383)) ([764f6b0](https://github.com/mojaloop/central-services-shared/commit/764f6b082f04a187f561f0fc17ea1eafa4736929))
|
|
18
|
+
|
|
19
|
+
### [18.3.7](https://github.com/mojaloop/central-services-shared/compare/v18.3.6...v18.3.7) (2024-06-07)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### Chore
|
|
23
|
+
|
|
24
|
+
* dependency updates to sub dependencies in widdershins for ajv ([#381](https://github.com/mojaloop/central-services-shared/issues/381)) ([b64403d](https://github.com/mojaloop/central-services-shared/commit/b64403d4936f7ca584938dd5614b10d532d2b48b))
|
|
25
|
+
|
|
26
|
+
### [18.3.6](https://github.com/mojaloop/central-services-shared/compare/v18.3.5...v18.3.6) (2024-05-24)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
### Bug Fixes
|
|
30
|
+
|
|
31
|
+
* await span.audit ([#375](https://github.com/mojaloop/central-services-shared/issues/375)) ([3b1c8cb](https://github.com/mojaloop/central-services-shared/commit/3b1c8cb9c7357dbe28b55dba5b2885387827329b))
|
|
32
|
+
|
|
33
|
+
### [18.3.5](https://github.com/mojaloop/central-services-shared/compare/v18.3.4...v18.3.5) (2024-04-23)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
### Bug Fixes
|
|
37
|
+
|
|
38
|
+
* excessive logging of agent internals ([0073c37](https://github.com/mojaloop/central-services-shared/commit/0073c37724b96909cdabd43055ac8ce653f1516a))
|
|
39
|
+
|
|
40
|
+
### [18.3.4](https://github.com/mojaloop/central-services-shared/compare/v18.3.3...v18.3.4) (2024-04-09)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
### Chore
|
|
44
|
+
|
|
45
|
+
* updated 3p dependencies to address moderate vulns ([#367](https://github.com/mojaloop/central-services-shared/issues/367)) ([08d1c74](https://github.com/mojaloop/central-services-shared/commit/08d1c7474e00fd12585274f39f349f8dea65ffb9))
|
|
46
|
+
|
|
47
|
+
### [18.3.3](https://github.com/mojaloop/central-services-shared/compare/v18.3.2...v18.3.3) (2024-04-05)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
### Chore
|
|
51
|
+
|
|
52
|
+
* overrides to 3p dependencies to address vulnerabilities ([#366](https://github.com/mojaloop/central-services-shared/issues/366)) ([8e87809](https://github.com/mojaloop/central-services-shared/commit/8e87809c8f552d18dd644fe1242987cec178414c))
|
|
53
|
+
|
|
54
|
+
### [18.3.2](https://github.com/mojaloop/central-services-shared/compare/v18.3.1...v18.3.2) (2024-04-05)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
### Chore
|
|
58
|
+
|
|
59
|
+
* dependency updates to address issues ([#365](https://github.com/mojaloop/central-services-shared/issues/365)) ([6dd4a53](https://github.com/mojaloop/central-services-shared/commit/6dd4a53fd5092a3af566032fd7a8f4a968778d4d))
|
|
60
|
+
|
|
61
|
+
### [18.3.1](https://github.com/mojaloop/central-services-shared/compare/v18.3.0...v18.3.1) (2024-04-04)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
### Chore
|
|
65
|
+
|
|
66
|
+
* **deps:** bump express from 4.18.2 to 4.19.2 ([#363](https://github.com/mojaloop/central-services-shared/issues/363)) ([248bad2](https://github.com/mojaloop/central-services-shared/commit/248bad29d36ad9a09832792f45d3a2d8d66ae2da))
|
|
67
|
+
* **deps:** bump follow-redirects from 1.15.5 to 1.15.6 ([#364](https://github.com/mojaloop/central-services-shared/issues/364)) ([eed3eeb](https://github.com/mojaloop/central-services-shared/commit/eed3eebf0adefeed5f954d5aa31eddc730e04325))
|
|
68
|
+
|
|
5
69
|
## [18.3.0](https://github.com/mojaloop/central-services-shared/compare/v18.2.0...v18.3.0) (2024-03-07)
|
|
6
70
|
|
|
7
71
|
|
package/CODEOWNERS
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
## @global-owner1 and @global-owner2 will be requested for
|
|
7
7
|
## review when someone opens a pull request.
|
|
8
8
|
#* @global-owner1 @global-owner2
|
|
9
|
-
* @mdebarros @elnyry-sam-k @vijayg10 @kleyow
|
|
9
|
+
* @mdebarros @elnyry-sam-k @vijayg10 @kleyow @oderayi
|
|
10
10
|
|
|
11
11
|
## Order is important; the last matching pattern takes the most
|
|
12
12
|
## precedence. When someone opens a pull request that only
|
package/LICENSE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# LICENSE
|
|
2
2
|
|
|
3
|
-
Copyright ©
|
|
3
|
+
Copyright © 2020-2024 Mojaloop Foundation
|
|
4
4
|
|
|
5
5
|
The Mojaloop files are made available by the Mojaloop Foundation under the Apache License, Version 2.0
|
|
6
6
|
(the "License") and you may not use these files except in compliance with the [License](http://www.apache.org/licenses/LICENSE-2.0).
|
package/audit-ci.jsonc
CHANGED
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
"GHSA-w5p7-h5w8-2hfq",
|
|
15
15
|
"GHSA-p9pc-299p-vxgp",
|
|
16
16
|
"GHSA-7fh5-64p2-3v2j",
|
|
17
|
-
"GHSA-rm97-x556-q36h" // https://github.com/advisories/GHSA-rm97-x556-q36h
|
|
17
|
+
"GHSA-rm97-x556-q36h", // https://github.com/advisories/GHSA-rm97-x556-q36h
|
|
18
|
+
"GHSA-ghr5-ch3p-vcr6", // https://github.com/advisories/GHSA-ghr5-ch3p-vcr6
|
|
19
|
+
"GHSA-cgfm-xwp7-2cvr" // https://github.com/advisories/GHSA-cgfm-xwp7-2cvr
|
|
18
20
|
]
|
|
19
21
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mojaloop/central-services-shared",
|
|
3
|
-
"version": "18.4.0
|
|
3
|
+
"version": "18.4.0",
|
|
4
4
|
"description": "Shared code for mojaloop central services",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "ModusBox",
|
|
@@ -54,12 +54,12 @@
|
|
|
54
54
|
"dependencies": {
|
|
55
55
|
"@hapi/catbox": "12.1.1",
|
|
56
56
|
"@hapi/catbox-memory": "5.0.1",
|
|
57
|
-
"axios": "1.
|
|
57
|
+
"axios": "1.7.2",
|
|
58
58
|
"clone": "2.1.2",
|
|
59
59
|
"dotenv": "16.4.5",
|
|
60
|
-
"env-var": "7.
|
|
60
|
+
"env-var": "7.5.0",
|
|
61
61
|
"event-stream": "4.0.1",
|
|
62
|
-
"immutable": "4.3.
|
|
62
|
+
"immutable": "4.3.6",
|
|
63
63
|
"lodash": "4.17.21",
|
|
64
64
|
"mustache": "4.2.0",
|
|
65
65
|
"openapi-backend": "5.10.6",
|
|
@@ -68,41 +68,52 @@
|
|
|
68
68
|
"shins": "2.6.0",
|
|
69
69
|
"uuid4": "2.0.3",
|
|
70
70
|
"widdershins": "^4.0.1",
|
|
71
|
-
"yaml": "2.4.
|
|
71
|
+
"yaml": "2.4.5"
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|
|
74
|
-
"@hapi/hapi": "21.3.
|
|
74
|
+
"@hapi/hapi": "21.3.10",
|
|
75
75
|
"@hapi/joi": "17.1.1",
|
|
76
|
-
"audit-ci": "^
|
|
76
|
+
"audit-ci": "^7.0.1",
|
|
77
77
|
"base64url": "3.0.1",
|
|
78
78
|
"chance": "1.1.11",
|
|
79
|
-
"npm-check-updates": "16.14.
|
|
80
|
-
"nyc": "
|
|
79
|
+
"npm-check-updates": "16.14.20",
|
|
80
|
+
"nyc": "17.0.0",
|
|
81
81
|
"pre-commit": "1.2.2",
|
|
82
82
|
"proxyquire": "2.1.3",
|
|
83
83
|
"replace": "^1.2.2",
|
|
84
84
|
"rewire": "7.0.0",
|
|
85
|
-
"sinon": "
|
|
85
|
+
"sinon": "18.0.0",
|
|
86
86
|
"standard": "17.1.0",
|
|
87
87
|
"standard-version": "9.5.0",
|
|
88
88
|
"tap-spec": "^5.0.0",
|
|
89
89
|
"tap-xunit": "2.4.1",
|
|
90
|
-
"tape": "5.
|
|
90
|
+
"tape": "5.8.1",
|
|
91
91
|
"tapes": "4.1.0"
|
|
92
92
|
},
|
|
93
|
+
"overrides": {
|
|
94
|
+
"shins": {
|
|
95
|
+
"ejs": "^3.1.7",
|
|
96
|
+
"sanitize-html": "2.12.1",
|
|
97
|
+
"jsonpointer": "5.0.0",
|
|
98
|
+
"markdown-it": "12.3.2",
|
|
99
|
+
"yargs-parser": "13.1.2",
|
|
100
|
+
"postcss": "8.4.31"
|
|
101
|
+
},
|
|
102
|
+
"widdershins": {
|
|
103
|
+
"yargs-parser": "13.1.2",
|
|
104
|
+
"markdown-it": "12.3.2",
|
|
105
|
+
"swagger2openapi": "7.0.8"
|
|
106
|
+
},
|
|
107
|
+
"markdown-it": "12.3.2"
|
|
108
|
+
},
|
|
93
109
|
"peerDependencies": {
|
|
94
|
-
"@mojaloop/central-services-error-handling": ">=
|
|
110
|
+
"@mojaloop/central-services-error-handling": ">=13.x.x",
|
|
95
111
|
"@mojaloop/central-services-logger": ">=11.x.x",
|
|
96
112
|
"@mojaloop/central-services-metrics": ">=12.x.x",
|
|
97
|
-
"@mojaloop/event-sdk": ">=14.
|
|
113
|
+
"@mojaloop/event-sdk": ">=14.1.1",
|
|
98
114
|
"ajv": "8.x.x",
|
|
99
115
|
"ajv-keywords": "5.x.x"
|
|
100
116
|
},
|
|
101
|
-
"overrides": {
|
|
102
|
-
"shins": {
|
|
103
|
-
"ejs": "3.1.7"
|
|
104
|
-
}
|
|
105
|
-
},
|
|
106
117
|
"peerDependenciesMeta": {
|
|
107
118
|
"@mojaloop/central-services-error-handling": {
|
|
108
119
|
"optional": false
|
package/src/enums/endpoints.js
CHANGED
|
@@ -62,8 +62,8 @@ const FspEndpointTypes = {
|
|
|
62
62
|
NET_DEBIT_CAP_ADJUSTMENT_EMAIL: 'NET_DEBIT_CAP_ADJUSTMENT_EMAIL',
|
|
63
63
|
SETTLEMENT_TRANSFER_POSITION_CHANGE_EMAIL: 'SETTLEMENT_TRANSFER_POSITION_CHANGE_EMAIL',
|
|
64
64
|
FSPIOP_CALLBACK_URL_QUOTES: 'FSPIOP_CALLBACK_URL_QUOTES',
|
|
65
|
-
FSPIOP_CALLBACK_URL_BULK_QUOTES: 'FSPIOP_CALLBACK_URL_BULK_QUOTES',
|
|
66
65
|
FSPIOP_CALLBACK_URL_FX_QUOTES: 'FSPIOP_CALLBACK_URL_FX_QUOTES',
|
|
66
|
+
FSPIOP_CALLBACK_URL_BULK_QUOTES: 'FSPIOP_CALLBACK_URL_BULK_QUOTES',
|
|
67
67
|
FSPIOP_CALLBACK_URL_BULK_TRANSFER_POST: 'FSPIOP_CALLBACK_URL_BULK_TRANSFER_POST',
|
|
68
68
|
FSPIOP_CALLBACK_URL_BULK_TRANSFER_PUT: 'FSPIOP_CALLBACK_URL_BULK_TRANSFER_PUT',
|
|
69
69
|
FSPIOP_CALLBACK_URL_BULK_TRANSFER_ERROR: 'FSPIOP_CALLBACK_URL_BULK_TRANSFER_ERROR',
|
package/src/enums/events.js
CHANGED
|
@@ -79,11 +79,20 @@ const Event = {
|
|
|
79
79
|
FAIL: 'fail',
|
|
80
80
|
FULFIL: 'fulfil',
|
|
81
81
|
FULFIL_DUPLICATE: 'fulfil-duplicate',
|
|
82
|
+
FX_FULFIL: 'fx-fulfil',
|
|
82
83
|
FX_ABORT: 'fx-abort',
|
|
83
84
|
FX_COMMIT: 'fx-commit',
|
|
84
85
|
FX_PREPARE: 'fx-prepare',
|
|
85
86
|
FX_REJECT: 'fx-reject',
|
|
86
87
|
FX_RESERVE: 'fx-reserve',
|
|
88
|
+
FX_PREPARE_DUPLICATE: 'fx-prepare-duplicate',
|
|
89
|
+
FX_ABORT_VALIDATION: 'fx-abort-validation',
|
|
90
|
+
FX_RESERVED_ABORTED: 'fx-reserved-aborted',
|
|
91
|
+
FX_FULFIL_DUPLICATE: 'fx-fulfil-duplicate',
|
|
92
|
+
FX_ABORT_DUPLICATE: 'fx-abort-duplicate',
|
|
93
|
+
FX_TIMEOUT_RECEIVED: 'fx-timeout-received',
|
|
94
|
+
FX_TIMEOUT_RESERVED: 'fx-timeout-reserved',
|
|
95
|
+
FX_GET: 'fx-get',
|
|
87
96
|
GET: 'get',
|
|
88
97
|
INITIATE: 'initiate',
|
|
89
98
|
LIMIT_ADJUSTMENT: 'limit-adjustment',
|
package/src/enums/http.js
CHANGED
|
@@ -73,6 +73,10 @@ const Headers = {
|
|
|
73
73
|
contentVersion: '1.0',
|
|
74
74
|
acceptVersion: '1'
|
|
75
75
|
},
|
|
76
|
+
fxQuotes: {
|
|
77
|
+
contentVersion: '2.0',
|
|
78
|
+
acceptVersion: '2'
|
|
79
|
+
},
|
|
76
80
|
bulkQuotes: {
|
|
77
81
|
contentVersion: '1.0',
|
|
78
82
|
acceptVersion: '1'
|
|
@@ -93,6 +97,10 @@ const Headers = {
|
|
|
93
97
|
contentVersion: '1.0',
|
|
94
98
|
acceptVersion: '1'
|
|
95
99
|
},
|
|
100
|
+
fxTransfers: {
|
|
101
|
+
contentVersion: '2.0',
|
|
102
|
+
acceptVersion: '2'
|
|
103
|
+
},
|
|
96
104
|
custom: {
|
|
97
105
|
contentVersion: '1.0',
|
|
98
106
|
acceptVersion: '1'
|
|
@@ -121,7 +129,9 @@ const HeaderResources = {
|
|
|
121
129
|
ORACLE: 'oracle',
|
|
122
130
|
SWITCH: 'switch',
|
|
123
131
|
TRANSFERS: 'transfers',
|
|
124
|
-
|
|
132
|
+
FX_TRANSFERS: 'fxTransfers',
|
|
133
|
+
QUOTES: 'quotes',
|
|
134
|
+
FX_QUOTES: 'fxQuotes'
|
|
125
135
|
}
|
|
126
136
|
|
|
127
137
|
const ServiceType = {
|
package/src/enums/kafka.js
CHANGED
|
@@ -146,10 +146,18 @@ const TopicMap = {
|
|
|
146
146
|
functionality: transferEventType.NOTIFICATION,
|
|
147
147
|
action: transferEventAction.EVENT
|
|
148
148
|
},
|
|
149
|
+
'fx-abort-duplicate': {
|
|
150
|
+
functionality: transferEventType.TRANSFER,
|
|
151
|
+
action: transferEventAction.POSITION
|
|
152
|
+
},
|
|
149
153
|
'fx-commit': {
|
|
150
154
|
functionality: transferEventType.NOTIFICATION,
|
|
151
155
|
action: transferEventAction.EVENT
|
|
152
156
|
},
|
|
157
|
+
'fx-fulfil-duplicate': {
|
|
158
|
+
functionality: transferEventType.TRANSFER,
|
|
159
|
+
action: transferEventAction.POSITION
|
|
160
|
+
},
|
|
153
161
|
'fx-prepare': {
|
|
154
162
|
functionality: transferEventType.NOTIFICATION,
|
|
155
163
|
action: transferEventAction.EVENT
|
|
@@ -162,6 +170,14 @@ const TopicMap = {
|
|
|
162
170
|
functionality: transferEventType.NOTIFICATION,
|
|
163
171
|
action: transferEventAction.EVENT
|
|
164
172
|
},
|
|
173
|
+
'fx-timeout-received': {
|
|
174
|
+
functionality: transferEventType.NOTIFICATION,
|
|
175
|
+
action: transferEventAction.EVENT
|
|
176
|
+
},
|
|
177
|
+
'fx-timeout-reserved': {
|
|
178
|
+
functionality: transferEventType.NOTIFICATION,
|
|
179
|
+
action: transferEventAction.EVENT
|
|
180
|
+
},
|
|
165
181
|
'prepare-duplicate': {
|
|
166
182
|
functionality: transferEventType.NOTIFICATION,
|
|
167
183
|
action: transferEventAction.EVENT
|
|
@@ -216,6 +232,10 @@ const TopicMap = {
|
|
|
216
232
|
functionality: transferEventType.TRANSFER,
|
|
217
233
|
action: transferEventAction.POSITION
|
|
218
234
|
},
|
|
235
|
+
'fx-abort-validation': {
|
|
236
|
+
functionality: transferEventType.TRANSFER,
|
|
237
|
+
action: transferEventAction.POSITION
|
|
238
|
+
},
|
|
219
239
|
'fx-commit': {
|
|
220
240
|
functionality: transferEventType.TRANSFER,
|
|
221
241
|
action: transferEventAction.POSITION
|
|
@@ -244,6 +264,10 @@ const TopicMap = {
|
|
|
244
264
|
functionality: transferEventType.TRANSFER,
|
|
245
265
|
action: transferEventAction.POSITION
|
|
246
266
|
},
|
|
267
|
+
'fx-timeout-reserved': {
|
|
268
|
+
functionality: transferEventType.TRANSFER,
|
|
269
|
+
action: transferEventAction.POSITION
|
|
270
|
+
},
|
|
247
271
|
reject: {
|
|
248
272
|
functionality: transferEventType.TRANSFER,
|
|
249
273
|
action: transferEventAction.POSITION
|
package/src/enums/transfers.js
CHANGED
|
@@ -34,6 +34,7 @@ const TransferInternalState = {
|
|
|
34
34
|
INVALID: 'INVALID',
|
|
35
35
|
RECEIVED_ERROR: 'RECEIVED_ERROR',
|
|
36
36
|
RECEIVED_FULFIL: 'RECEIVED_FULFIL',
|
|
37
|
+
RECEIVED_FULFIL_DEPENDENT: 'RECEIVED_FULFIL_DEPENDENT',
|
|
37
38
|
RECEIVED_PREPARE: 'RECEIVED_PREPARE',
|
|
38
39
|
RECEIVED_REJECT: 'RECEIVED_REJECT',
|
|
39
40
|
RESERVED: 'RESERVED',
|
package/src/index.d.ts
CHANGED
|
@@ -60,12 +60,17 @@ declare namespace CentralServicesShared {
|
|
|
60
60
|
FSPIOP_CALLBACK_URL_TRANSFER_POST = 'FSPIOP_CALLBACK_URL_TRANSFER_POST',
|
|
61
61
|
FSPIOP_CALLBACK_URL_TRANSFER_PUT = 'FSPIOP_CALLBACK_URL_TRANSFER_PUT',
|
|
62
62
|
FSPIOP_CALLBACK_URL_TRANSFER_ERROR = 'FSPIOP_CALLBACK_URL_TRANSFER_ERROR',
|
|
63
|
+
FSPIOP_CALLBACK_URL_FX_TRANSFER_POST = 'FSPIOP_CALLBACK_URL_FX_TRANSFER_POST',
|
|
64
|
+
FSPIOP_CALLBACK_URL_FX_TRANSFER_PUT = 'FSPIOP_CALLBACK_URL_FX_TRANSFER_PUT',
|
|
65
|
+
FSPIOP_CALLBACK_URL_FX_TRANSFER_ERROR = 'FSPIOP_CALLBACK_URL_FX_TRANSFER_ERROR',
|
|
63
66
|
ALARM_NOTIFICATION_URL = 'ALARM_NOTIFICATION_URL',
|
|
64
67
|
ALARM_NOTIFICATION_TOPIC = 'ALARM_NOTIFICATION_TOPIC',
|
|
65
68
|
NET_DEBIT_CAP_THRESHOLD_BREACH_EMAIL = 'NET_DEBIT_CAP_THRESHOLD_BREACH_EMAIL',
|
|
66
69
|
NET_DEBIT_CAP_ADJUSTMENT_EMAIL = 'NET_DEBIT_CAP_ADJUSTMENT_EMAIL',
|
|
67
70
|
SETTLEMENT_TRANSFER_POSITION_CHANGE_EMAIL = 'SETTLEMENT_TRANSFER_POSITION_CHANGE_EMAIL',
|
|
68
71
|
FSPIOP_CALLBACK_URL_QUOTES = 'FSPIOP_CALLBACK_URL_QUOTES',
|
|
72
|
+
FSPIOP_CALLBACK_URL_FX_QUOTES = 'FSPIOP_CALLBACK_URL_FX_QUOTES',
|
|
73
|
+
FSPIOP_CALLBACK_URL_BULK_QUOTES = 'FSPIOP_CALLBACK_URL_BULK_QUOTES',
|
|
69
74
|
FSPIOP_CALLBACK_URL_BULK_TRANSFER_POST = 'FSPIOP_CALLBACK_URL_BULK_TRANSFER_POST',
|
|
70
75
|
FSPIOP_CALLBACK_URL_BULK_TRANSFER_PUT = 'FSPIOP_CALLBACK_URL_BULK_TRANSFER_PUT',
|
|
71
76
|
FSPIOP_CALLBACK_URL_BULK_TRANSFER_ERROR = 'FSPIOP_CALLBACK_URL_BULK_TRANSFER_ERROR',
|
|
@@ -125,6 +130,9 @@ declare namespace CentralServicesShared {
|
|
|
125
130
|
FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_PUT: FspEndpointTypesEnum.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_PUT;
|
|
126
131
|
FSPIOP_CALLBACK_URL_PARTIES_PUT_ERROR: FspEndpointTypesEnum.FSPIOP_CALLBACK_URL_PARTIES_PUT_ERROR;
|
|
127
132
|
FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_PUT_ERROR: FspEndpointTypesEnum.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_PUT_ERROR;
|
|
133
|
+
FSPIOP_CALLBACK_URL_FX_TRANSFER_POST: FspEndpointTypesEnum.FSPIOP_CALLBACK_URL_FX_TRANSFER_POST;
|
|
134
|
+
FSPIOP_CALLBACK_URL_FX_TRANSFER_PUT: FspEndpointTypesEnum.FSPIOP_CALLBACK_URL_FX_TRANSFER_PUT;
|
|
135
|
+
FSPIOP_CALLBACK_URL_FX_TRANSFER_ERROR: FspEndpointTypesEnum.FSPIOP_CALLBACK_URL_FX_TRANSFER_ERROR;
|
|
128
136
|
FSPIOP_CALLBACK_URL_TRANSFER_POST: FspEndpointTypesEnum.FSPIOP_CALLBACK_URL_TRANSFER_POST;
|
|
129
137
|
FSPIOP_CALLBACK_URL_TRANSFER_PUT: FspEndpointTypesEnum.FSPIOP_CALLBACK_URL_TRANSFER_PUT;
|
|
130
138
|
FSPIOP_CALLBACK_URL_TRANSFER_ERROR: FspEndpointTypesEnum.FSPIOP_CALLBACK_URL_TRANSFER_ERROR;
|
|
@@ -184,12 +192,20 @@ declare namespace CentralServicesShared {
|
|
|
184
192
|
ORACLE_PARTICIPANTS_TYPE_ID_SUB_ID: string;
|
|
185
193
|
ORACLE_PARTICIPANTS_TYPE_ID_CURRENCY_SUB_ID: string;
|
|
186
194
|
ORACLE_PARTICIPANTS_BATCH: string;
|
|
195
|
+
FX_TRANSFERS_POST: string;
|
|
196
|
+
FX_TRANSFERS_PUT: string;
|
|
197
|
+
FX_TRANSFERS_PUT_ERROR: string;
|
|
198
|
+
FX_QUOTES_POST: string;
|
|
199
|
+
FX_QUOTES_PUT: string;
|
|
200
|
+
FX_QUOTES_ERROR_PUT: string;
|
|
187
201
|
TRANSFERS_POST: string;
|
|
188
202
|
TRANSFERS_PUT: string;
|
|
189
203
|
TRANSFERS_PUT_ERROR: string;
|
|
190
204
|
BULK_TRANSFERS_POST: string;
|
|
191
205
|
BULK_TRANSFERS_PUT: string;
|
|
192
206
|
BULK_TRANSFERS_PUT_ERROR: string;
|
|
207
|
+
BULK_QUOTES_POST: string;
|
|
208
|
+
BULK_QUOTES_ERROR_PUT: string;
|
|
193
209
|
TP_TRANSACTION_REQUEST_GET: string;
|
|
194
210
|
TP_TRANSACTION_REQUEST_POST: string;
|
|
195
211
|
TP_TRANSACTION_REQUEST_PUT: string;
|
|
@@ -241,6 +257,7 @@ declare namespace CentralServicesShared {
|
|
|
241
257
|
ENDPOINTCACHE = 'endpointcache',
|
|
242
258
|
EVENT = 'event',
|
|
243
259
|
FULFIL = 'fulfil',
|
|
260
|
+
FX_QUOTE = 'fx-quote',
|
|
244
261
|
GET = 'get',
|
|
245
262
|
NOTIFICATION = 'notification',
|
|
246
263
|
ORACLE = 'oracle',
|
|
@@ -255,6 +272,8 @@ declare namespace CentralServicesShared {
|
|
|
255
272
|
TRANSFER = 'transfer',
|
|
256
273
|
PARTY = 'party',
|
|
257
274
|
PARTICIPANT = 'participant',
|
|
275
|
+
DEFERRED_SETTLEMENT = 'deferred-settlement',
|
|
276
|
+
GROSS_SETTLEMENT = 'gross-settlement',
|
|
258
277
|
VERIFICATION = 'verification'
|
|
259
278
|
}
|
|
260
279
|
|
|
@@ -279,6 +298,20 @@ declare namespace CentralServicesShared {
|
|
|
279
298
|
FAIL = 'fail',
|
|
280
299
|
FULFIL = 'fulfil',
|
|
281
300
|
FULFIL_DUPLICATE = 'fulfil-duplicate',
|
|
301
|
+
FX_FULFIL = 'fx-fulfil',
|
|
302
|
+
FX_ABORT = 'fx-abort',
|
|
303
|
+
FX_COMMIT = 'fx-commit',
|
|
304
|
+
FX_PREPARE = 'fx-prepare',
|
|
305
|
+
FX_REJECT = 'fx-reject',
|
|
306
|
+
FX_RESERVE = 'fx-reserve',
|
|
307
|
+
FX_PREPARE_DUPLICATE = 'fx-prepare-duplicate',
|
|
308
|
+
FX_ABORT_VALIDATION = 'fx-abort-validation',
|
|
309
|
+
FX_RESERVED_ABORTED = 'fx-reserved-aborted',
|
|
310
|
+
FX_FULFIL_DUPLICATE = 'fx-fulfil-duplicate',
|
|
311
|
+
FX_ABORT_DUPLICATE = 'fx-abort-duplicate',
|
|
312
|
+
FX_TIMEOUT_RECEIVED = 'fx-timeout-received',
|
|
313
|
+
FX_TIMEOUT_RESERVED = 'fx-timeout-reserved',
|
|
314
|
+
FX_GET = 'fx-get',
|
|
282
315
|
GET = 'get',
|
|
283
316
|
INITIATE = 'initiate',
|
|
284
317
|
LIMIT_ADJUSTMENT = 'limit-adjustment',
|
|
@@ -416,6 +449,20 @@ declare namespace CentralServicesShared {
|
|
|
416
449
|
FAIL: EventActionEnum.FAIL;
|
|
417
450
|
FULFIL: EventActionEnum.FULFIL;
|
|
418
451
|
FULFIL_DUPLICATE: EventActionEnum.FULFIL_DUPLICATE;
|
|
452
|
+
FX_FULFIL: EventActionEnum.FX_FULFIL;
|
|
453
|
+
FX_ABORT: EventActionEnum.FX_ABORT,
|
|
454
|
+
FX_COMMIT: EventActionEnum.FX_COMMIT,
|
|
455
|
+
FX_PREPARE: EventActionEnum.FX_PREPARE,
|
|
456
|
+
FX_REJECT: EventActionEnum.FX_REJECT,
|
|
457
|
+
FX_RESERVE: EventActionEnum.FX_RESERVE,
|
|
458
|
+
FX_PREPARE_DUPLICATE: EventActionEnum.FX_PREPARE_DUPLICATE,
|
|
459
|
+
FX_ABORT_VALIDATION: EventActionEnum.FX_ABORT_VALIDATION,
|
|
460
|
+
FX_RESERVED_ABORTED: EventActionEnum.FX_RESERVED_ABORTED,
|
|
461
|
+
FX_FULFIL_DUPLICATE: EventActionEnum.FX_FULFIL_DUPLICATE,
|
|
462
|
+
FX_ABORT_DUPLICATE: EventActionEnum.FX_ABORT_DUPLICATE,
|
|
463
|
+
FX_TIMEOUT_RECEIVED: EventActionEnum.FX_TIMEOUT_RECEIVED,
|
|
464
|
+
FX_TIMEOUT_RESERVED: EventActionEnum.FX_TIMEOUT_RESERVED,
|
|
465
|
+
FX_GET: EventActionEnum.FX_GET,
|
|
419
466
|
GET: EventActionEnum.GET;
|
|
420
467
|
INITIATE: EventActionEnum.INITIATE;
|
|
421
468
|
LIMIT_ADJUSTMENT: EventActionEnum.LIMIT_ADJUSTMENT;
|
|
@@ -457,6 +504,7 @@ declare namespace CentralServicesShared {
|
|
|
457
504
|
ENDPOINTCACHE: EventTypeEnum.ENDPOINTCACHE;
|
|
458
505
|
EVENT: EventTypeEnum.EVENT;
|
|
459
506
|
FULFIL: EventTypeEnum.FULFIL;
|
|
507
|
+
FX_QUOTE: EventTypeEnum.FX_QUOTE;
|
|
460
508
|
GET: EventTypeEnum.GET;
|
|
461
509
|
NOTIFICATION: EventTypeEnum.NOTIFICATION;
|
|
462
510
|
ORACLE: EventTypeEnum.ORACLE;
|
|
@@ -471,6 +519,8 @@ declare namespace CentralServicesShared {
|
|
|
471
519
|
TRANSFER: EventTypeEnum.TRANSFER;
|
|
472
520
|
PARTY: EventTypeEnum.PARTY;
|
|
473
521
|
PARTICIPANT: EventTypeEnum.PARTICIPANT;
|
|
522
|
+
DEFERRED_SETTLEMENT: EventTypeEnum.DEFERRED_SETTLEMENT,
|
|
523
|
+
GROSS_SETTLEMENT: EventTypeEnum.GROSS_SETTLEMENT,
|
|
474
524
|
VERIFICATION: EventTypeEnum.VERIFICATION;
|
|
475
525
|
};
|
|
476
526
|
};
|
|
@@ -547,7 +597,7 @@ declare namespace CentralServicesShared {
|
|
|
547
597
|
AdminNotificationActions: {
|
|
548
598
|
LIMIT_ADJUSTMENT: AdminNotificationActionsEnum.LIMIT_ADJUSTMENT;
|
|
549
599
|
};
|
|
550
|
-
}
|
|
600
|
+
};
|
|
551
601
|
}
|
|
552
602
|
interface Endpoints {
|
|
553
603
|
fetchEndpoints(fspId: string): Promise<any>
|
package/src/util/endpoints.js
CHANGED
|
@@ -29,13 +29,13 @@ const Logger = require('@mojaloop/central-services-logger')
|
|
|
29
29
|
const ErrorHandler = require('@mojaloop/central-services-error-handling')
|
|
30
30
|
const Catbox = require('@hapi/catbox')
|
|
31
31
|
const CatboxMemory = require('@hapi/catbox-memory')
|
|
32
|
-
const Mustache = require('mustache')
|
|
33
32
|
const { Map } = require('immutable')
|
|
34
33
|
const Enum = require('../enums')
|
|
35
34
|
const Http = require('./http')
|
|
36
35
|
const request = require('./request')
|
|
37
36
|
const partition = 'endpoint-cache'
|
|
38
37
|
const clientOptions = { partition }
|
|
38
|
+
const Mustache = require('mustache')
|
|
39
39
|
const Metrics = require('@mojaloop/central-services-metrics')
|
|
40
40
|
|
|
41
41
|
let client
|
package/src/util/id.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
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
|
+
Contributors
|
|
9
|
+
--------------
|
|
10
|
+
This is the official list of the Mojaloop project contributors for this file.
|
|
11
|
+
Names of the original copyright holders (individuals or organizations)
|
|
12
|
+
should be listed with a '*' in the first column. People who have
|
|
13
|
+
contributed from an organization can be listed under the organization
|
|
14
|
+
that actually holds the copyright for their contributions (see the
|
|
15
|
+
Gates Foundation organization for an example). Those individuals should have
|
|
16
|
+
their names indented and be marked with a '-'. Email address can be added
|
|
17
|
+
optionally within square brackets <email>.
|
|
18
|
+
* Gates Foundation
|
|
19
|
+
|
|
20
|
+
* Infitx
|
|
21
|
+
- Kalin Krustev <kalin.krustev@infitx.com>
|
|
22
|
+
|
|
23
|
+
--------------
|
|
24
|
+
******/
|
|
25
|
+
|
|
26
|
+
const crypto = require('crypto')
|
|
27
|
+
|
|
28
|
+
const generators = {
|
|
29
|
+
// generate UUID compliant with https://datatracker.ietf.org/doc/html/rfc9562
|
|
30
|
+
uuid: ({ version = 7 }) => ({
|
|
31
|
+
4: crypto.randomUUID,
|
|
32
|
+
7: () => {
|
|
33
|
+
const timestamp = Date.now().toString(16).padStart(12, 0)
|
|
34
|
+
const random = crypto.randomUUID()
|
|
35
|
+
return `${timestamp.substring(0, 8)}-${timestamp.substring(8, 12)}-7${random.substring(15, 36)}`
|
|
36
|
+
}
|
|
37
|
+
})[version]
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = ({ type = 'uuid', ...config } = {}) => generators[type](config)
|
package/src/util/index.js
CHANGED
|
@@ -45,6 +45,7 @@ const Settlement = require('./settlement')
|
|
|
45
45
|
const EventFramework = require('./eventFramework')
|
|
46
46
|
const Schema = require('./schema')
|
|
47
47
|
const OpenapiBackend = require('./openapiBackend')
|
|
48
|
+
const id = require('./id')
|
|
48
49
|
|
|
49
50
|
const omitNil = (object) => {
|
|
50
51
|
return _.omitBy(object, _.isNil)
|
|
@@ -252,5 +253,6 @@ module.exports = {
|
|
|
252
253
|
Settlement,
|
|
253
254
|
Schema,
|
|
254
255
|
OpenapiBackend,
|
|
256
|
+
id,
|
|
255
257
|
resourceVersions: Helpers.resourceVersions
|
|
256
258
|
}
|
package/src/util/kafka/index.js
CHANGED
|
@@ -236,7 +236,7 @@ const produceGeneralMessage = async (defaultKafkaConfig, kafkaProducer, function
|
|
|
236
236
|
const kafkaConfig = getKafkaConfig(defaultKafkaConfig, Enum.Kafka.Config.PRODUCER, functionalityMapped.toUpperCase(), actionMapped.toUpperCase())
|
|
237
237
|
if (span) {
|
|
238
238
|
messageProtocol = await span.injectContextToMessage(messageProtocol)
|
|
239
|
-
span.audit(messageProtocol, EventSdk.AuditEventAction.egress)
|
|
239
|
+
await span.audit(messageProtocol, EventSdk.AuditEventAction.egress)
|
|
240
240
|
}
|
|
241
241
|
await kafkaProducer.produceMessage(messageProtocol, topicConfig, kafkaConfig)
|
|
242
242
|
return true
|
package/src/util/request.js
CHANGED
|
@@ -39,6 +39,10 @@ const MISSING_FUNCTION_PARAMETERS = 'Missing parameters for function'
|
|
|
39
39
|
// By default it would insert `"Accept":"application/json, text/plain, */*"`.
|
|
40
40
|
delete request.defaults.headers.common.Accept
|
|
41
41
|
|
|
42
|
+
// Enable keepalive for http
|
|
43
|
+
request.defaults.httpAgent = new http.Agent({ keepAlive: true })
|
|
44
|
+
request.defaults.httpAgent.toJSON = () => ({})
|
|
45
|
+
|
|
42
46
|
/**
|
|
43
47
|
* @function sendRequest
|
|
44
48
|
*
|
|
@@ -100,6 +100,29 @@ Test('Cache Test', cacheTest => {
|
|
|
100
100
|
}
|
|
101
101
|
})
|
|
102
102
|
|
|
103
|
+
getEndpointTest.test('return the endpoint if catbox returns decoratedValue object', async (test) => {
|
|
104
|
+
const fsp = 'fsp'
|
|
105
|
+
const url = Mustache.render(Config.ENDPOINT_SOURCE_URL + Enum.EndPoints.FspEndpointTemplates.PARTICIPANT_ENDPOINTS_GET, { fsp })
|
|
106
|
+
const endpointType = FSPIOP_CALLBACK_URL_TRANSFER_PUT
|
|
107
|
+
const expected = 'http://localhost:1080/transfers/97b01bd3-b223-415b-b37b-ab5bef9bdbed'
|
|
108
|
+
|
|
109
|
+
await Cache.initializeCache({
|
|
110
|
+
...Config.ENDPOINT_CACHE_CONFIG,
|
|
111
|
+
getDecoratedValue: true
|
|
112
|
+
})
|
|
113
|
+
request.sendRequest.withArgs(url, Helper.defaultHeaders()).returns(Promise.resolve(Helper.getEndPointsResponse))
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
const result = await Cache.getEndpoint(Config.ENDPOINT_SOURCE_URL, fsp, endpointType, { transferId: '97b01bd3-b223-415b-b37b-ab5bef9bdbed' })
|
|
117
|
+
test.equal(result, expected, 'The results match')
|
|
118
|
+
await Cache.stopCache()
|
|
119
|
+
test.end()
|
|
120
|
+
} catch (err) {
|
|
121
|
+
test.fail('Error thrown', err)
|
|
122
|
+
test.end()
|
|
123
|
+
}
|
|
124
|
+
})
|
|
125
|
+
|
|
103
126
|
getEndpointTest.test('return throw an error if array not returned in response object', async (test) => {
|
|
104
127
|
const fsp = 'fsp'
|
|
105
128
|
const url = Mustache.render(Config.ENDPOINT_SOURCE_URL + Enum.EndPoints.FspEndpointTemplates.PARTICIPANT_ENDPOINTS_GET, { fsp })
|
|
@@ -0,0 +1,64 @@
|
|
|
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
|
+
Contributors
|
|
9
|
+
--------------
|
|
10
|
+
This is the official list of the Mojaloop project contributors for this file.
|
|
11
|
+
Names of the original copyright holders (individuals or organizations)
|
|
12
|
+
should be listed with a '*' in the first column. People who have
|
|
13
|
+
contributed from an organization can be listed under the organization
|
|
14
|
+
that actually holds the copyright for their contributions (see the
|
|
15
|
+
Gates Foundation organization for an example). Those individuals should have
|
|
16
|
+
their names indented and be marked with a '-'. Email address can be added
|
|
17
|
+
optionally within square brackets <email>.
|
|
18
|
+
* Gates Foundation
|
|
19
|
+
|
|
20
|
+
* Infitx
|
|
21
|
+
- Kalin Krustev <kalin.krustev@infitx.com>
|
|
22
|
+
--------------
|
|
23
|
+
******/
|
|
24
|
+
'use strict'
|
|
25
|
+
|
|
26
|
+
const Test = require('tapes')(require('tape'))
|
|
27
|
+
const Sinon = require('sinon')
|
|
28
|
+
const idGenerator = require('../../../src/util/id')
|
|
29
|
+
const uuidRegex = version => new RegExp(`[a-f0-9]{8}-[a-f0-9]{4}-${version}[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}`)
|
|
30
|
+
|
|
31
|
+
Test('Id util', idTest => {
|
|
32
|
+
let sandbox
|
|
33
|
+
|
|
34
|
+
idTest.beforeEach(t => {
|
|
35
|
+
sandbox = Sinon.createSandbox()
|
|
36
|
+
t.end()
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
idTest.afterEach(t => {
|
|
40
|
+
sandbox.restore()
|
|
41
|
+
t.end()
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
idTest.test('Id should', generateSha256Test => {
|
|
45
|
+
generateSha256Test.test('generate UUID v4', test => {
|
|
46
|
+
const uuid4 = idGenerator({ type: 'uuid', version: 4 })
|
|
47
|
+
test.match(uuid4(), uuidRegex(4))
|
|
48
|
+
test.end()
|
|
49
|
+
})
|
|
50
|
+
generateSha256Test.test('generate UUID v7', test => {
|
|
51
|
+
const uuid7 = idGenerator({ type: 'uuid', version: 7 })
|
|
52
|
+
test.match(uuid7(), uuidRegex(7))
|
|
53
|
+
test.end()
|
|
54
|
+
})
|
|
55
|
+
generateSha256Test.test('generate UUID v7 by default', test => {
|
|
56
|
+
const uuid = idGenerator()
|
|
57
|
+
test.match(uuid(), uuidRegex(7))
|
|
58
|
+
test.end()
|
|
59
|
+
})
|
|
60
|
+
generateSha256Test.end()
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
idTest.end()
|
|
64
|
+
})
|