soajs.controller 4.3.1 → 4.3.3
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/.github/workflows/ci.yml +7 -2
- package/.github/workflows/release.yml +11 -4
- package/lib/parseURL.js +16 -3
- package/mw/gotoService/redirectToService.js +124 -2
- package/mw/lastSeen/index.js +18 -7
- package/mw/url/index.js +16 -2
- package/package.json +1 -1
- package/server/maintenance.js +17 -3
- package/test/unit/lib/parseURL.js +18 -4
- package/test/unit/mw/gotoService/monitorBuffer.js +306 -0
package/.github/workflows/ci.yml
CHANGED
|
@@ -6,9 +6,14 @@ on:
|
|
|
6
6
|
pull_request:
|
|
7
7
|
branches: [ master, develop ]
|
|
8
8
|
|
|
9
|
+
env:
|
|
10
|
+
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true'
|
|
11
|
+
|
|
9
12
|
jobs:
|
|
10
13
|
test:
|
|
11
14
|
runs-on: ubuntu-latest
|
|
15
|
+
env:
|
|
16
|
+
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true'
|
|
12
17
|
|
|
13
18
|
services:
|
|
14
19
|
mongodb:
|
|
@@ -18,10 +23,10 @@ jobs:
|
|
|
18
23
|
|
|
19
24
|
steps:
|
|
20
25
|
- name: Checkout code
|
|
21
|
-
uses: actions/checkout@v4
|
|
26
|
+
uses: actions/checkout@v4.2.2
|
|
22
27
|
|
|
23
28
|
- name: Setup Node.js
|
|
24
|
-
uses: actions/setup-node@v4
|
|
29
|
+
uses: actions/setup-node@v4.4.0
|
|
25
30
|
with:
|
|
26
31
|
node-version: '24'
|
|
27
32
|
|
|
@@ -6,9 +6,14 @@ on:
|
|
|
6
6
|
- release
|
|
7
7
|
- 'release/**'
|
|
8
8
|
|
|
9
|
+
env:
|
|
10
|
+
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true'
|
|
11
|
+
|
|
9
12
|
jobs:
|
|
10
13
|
test:
|
|
11
14
|
runs-on: ubuntu-latest
|
|
15
|
+
env:
|
|
16
|
+
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true'
|
|
12
17
|
|
|
13
18
|
services:
|
|
14
19
|
mongodb:
|
|
@@ -18,10 +23,10 @@ jobs:
|
|
|
18
23
|
|
|
19
24
|
steps:
|
|
20
25
|
- name: Checkout code
|
|
21
|
-
uses: actions/checkout@v4
|
|
26
|
+
uses: actions/checkout@v4.2.2
|
|
22
27
|
|
|
23
28
|
- name: Setup Node.js
|
|
24
|
-
uses: actions/setup-node@v4
|
|
29
|
+
uses: actions/setup-node@v4.4.0
|
|
25
30
|
with:
|
|
26
31
|
node-version: '24'
|
|
27
32
|
|
|
@@ -40,10 +45,12 @@ jobs:
|
|
|
40
45
|
publish-docker:
|
|
41
46
|
runs-on: ubuntu-latest
|
|
42
47
|
needs: test
|
|
48
|
+
env:
|
|
49
|
+
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true'
|
|
43
50
|
|
|
44
51
|
steps:
|
|
45
52
|
- name: Checkout code
|
|
46
|
-
uses: actions/checkout@v4
|
|
53
|
+
uses: actions/checkout@v4.2.2
|
|
47
54
|
|
|
48
55
|
- name: Extract version from package.json
|
|
49
56
|
id: version
|
|
@@ -85,7 +92,7 @@ jobs:
|
|
|
85
92
|
run: git push origin ${{ steps.version.outputs.PACKAGE_VERSION }} || true
|
|
86
93
|
|
|
87
94
|
- name: Create GitHub Release
|
|
88
|
-
uses: softprops/action-gh-release@
|
|
95
|
+
uses: softprops/action-gh-release@v2
|
|
89
96
|
with:
|
|
90
97
|
tag_name: ${{ steps.version.outputs.PACKAGE_VERSION }}
|
|
91
98
|
name: Release ${{ steps.version.outputs.PACKAGE_VERSION }}
|
package/lib/parseURL.js
CHANGED
|
@@ -8,7 +8,20 @@
|
|
|
8
8
|
* found in the LICENSE file at the root of this repository
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Parse URL using WHATWG URL API and return object compatible with legacy url.parse()
|
|
13
|
+
* @param {string} urlString - URL string to parse
|
|
14
|
+
* @returns {Object} - Object with pathname, path, and query properties
|
|
15
|
+
*/
|
|
16
|
+
const parseURL = (urlString) => {
|
|
17
|
+
const urlObj = new URL(urlString, 'http://localhost');
|
|
18
|
+
const search = urlObj.search || '';
|
|
19
|
+
return {
|
|
20
|
+
pathname: urlObj.pathname,
|
|
21
|
+
path: urlObj.pathname + search,
|
|
22
|
+
query: Object.fromEntries(urlObj.searchParams)
|
|
23
|
+
};
|
|
24
|
+
};
|
|
12
25
|
|
|
13
26
|
|
|
14
27
|
module.exports = (uri, parsedUrl) => {
|
|
@@ -16,7 +29,7 @@ module.exports = (uri, parsedUrl) => {
|
|
|
16
29
|
let service_nv = serviceInfo[1];
|
|
17
30
|
let service_n = service_nv;
|
|
18
31
|
let service_v = null;
|
|
19
|
-
|
|
32
|
+
|
|
20
33
|
//check if there is /v1 or v1.1 in the url
|
|
21
34
|
let matches = uri.match(/\/v[0-9]+(.[0-9]+)?\//);
|
|
22
35
|
if (matches && matches.length > 0) {
|
|
@@ -25,7 +38,7 @@ module.exports = (uri, parsedUrl) => {
|
|
|
25
38
|
service_v = hit.replace("v", '');
|
|
26
39
|
serviceInfo.splice(2, 1);
|
|
27
40
|
uri = uri.replace(matches[0], "/");
|
|
28
|
-
parsedUrl =
|
|
41
|
+
parsedUrl = parseURL(uri);
|
|
29
42
|
}
|
|
30
43
|
}
|
|
31
44
|
|
|
@@ -12,6 +12,121 @@ const get = (p, o) => p.reduce((xs, x) => (xs && xs[x]) ? xs[x] : null, o);
|
|
|
12
12
|
|
|
13
13
|
const { httpRequestLight, proxyRequestMonitor } = require("../../lib/request.js");
|
|
14
14
|
|
|
15
|
+
// Monitor buffer for batch uploads
|
|
16
|
+
let monitorBuffer = [];
|
|
17
|
+
let monitorFlushTimer = null;
|
|
18
|
+
let monitorFlushInProgress = false;
|
|
19
|
+
let lastReqForFlush = null;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Flush the monitor buffer to soamonitor
|
|
23
|
+
*/
|
|
24
|
+
function flushMonitorBuffer() {
|
|
25
|
+
if (monitorFlushInProgress || monitorBuffer.length === 0 || !lastReqForFlush) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
monitorFlushInProgress = true;
|
|
30
|
+
|
|
31
|
+
// Clear timer if exists
|
|
32
|
+
if (monitorFlushTimer) {
|
|
33
|
+
clearTimeout(monitorFlushTimer);
|
|
34
|
+
monitorFlushTimer = null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Take current buffer and reset
|
|
38
|
+
let itemsToSend = monitorBuffer;
|
|
39
|
+
let req = lastReqForFlush;
|
|
40
|
+
monitorBuffer = [];
|
|
41
|
+
|
|
42
|
+
let soamonitor = "soamonitor";
|
|
43
|
+
let soamonitorVersion = "1";
|
|
44
|
+
let port = get(["registry", "services", "soamonitor", "port"], req.soajs) || "4050";
|
|
45
|
+
let api = "/monitor/items"; // Batch endpoint
|
|
46
|
+
|
|
47
|
+
req.soajs.awareness.getHost(soamonitor, soamonitorVersion, function (host) {
|
|
48
|
+
monitorFlushInProgress = false;
|
|
49
|
+
|
|
50
|
+
if (!host) {
|
|
51
|
+
req.soajs.log.debug('Monitor buffer flush: Unable to find host for ' + soamonitor);
|
|
52
|
+
// Put items back in buffer for retry
|
|
53
|
+
monitorBuffer = itemsToSend.concat(monitorBuffer);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Convert body/response to strings
|
|
58
|
+
itemsToSend.forEach(doc => {
|
|
59
|
+
if (doc.body) {
|
|
60
|
+
doc.body = doc.body.toString();
|
|
61
|
+
}
|
|
62
|
+
if (doc.response) {
|
|
63
|
+
doc.response = doc.response.toString();
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
let uri = "http://" + host + ":" + port + api;
|
|
68
|
+
let options = {
|
|
69
|
+
"method": "POST",
|
|
70
|
+
"uri": uri,
|
|
71
|
+
"data": { "items": itemsToSend },
|
|
72
|
+
"json": true
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
httpRequestLight(options)
|
|
76
|
+
.then((body) => {
|
|
77
|
+
if (body && !body.result) {
|
|
78
|
+
req.soajs.log.debug('Monitor buffer flush response:', body);
|
|
79
|
+
} else {
|
|
80
|
+
req.soajs.log.debug('Monitor buffer flushed: ' + itemsToSend.length + ' items');
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
.catch((error) => {
|
|
84
|
+
req.soajs.log.debug('Monitor buffer flush failed: ' + error.message);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Add item to monitor buffer and trigger flush if needed
|
|
91
|
+
* @param {Object} req - Request object
|
|
92
|
+
* @param {Object} doc - Monitor document to buffer
|
|
93
|
+
* @param {Object} monitor - Monitor configuration
|
|
94
|
+
*/
|
|
95
|
+
function bufferMonitorItem(req, doc, monitor) {
|
|
96
|
+
monitorBuffer.push(doc);
|
|
97
|
+
lastReqForFlush = req;
|
|
98
|
+
|
|
99
|
+
let bufferConfig = monitor.buffer || {};
|
|
100
|
+
let bufferTime = bufferConfig.time;
|
|
101
|
+
let bufferLimit = bufferConfig.limit;
|
|
102
|
+
|
|
103
|
+
// Check count threshold
|
|
104
|
+
if (bufferLimit && monitorBuffer.length >= bufferLimit) {
|
|
105
|
+
flushMonitorBuffer();
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Start timer if not already running and time threshold is set
|
|
110
|
+
if (bufferTime && !monitorFlushTimer) {
|
|
111
|
+
monitorFlushTimer = setTimeout(() => {
|
|
112
|
+
monitorFlushTimer = null;
|
|
113
|
+
flushMonitorBuffer();
|
|
114
|
+
}, bufferTime);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Graceful shutdown - flush remaining buffer
|
|
119
|
+
process.on('SIGTERM', () => {
|
|
120
|
+
if (monitorBuffer.length > 0 && !monitorFlushInProgress) {
|
|
121
|
+
// Best-effort cleanup
|
|
122
|
+
monitorBuffer = [];
|
|
123
|
+
if (monitorFlushTimer) {
|
|
124
|
+
clearTimeout(monitorFlushTimer);
|
|
125
|
+
monitorFlushTimer = null;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
15
130
|
module.exports = (configuration) => {
|
|
16
131
|
|
|
17
132
|
let core = configuration.core;
|
|
@@ -26,12 +141,21 @@ module.exports = (configuration) => {
|
|
|
26
141
|
*/
|
|
27
142
|
return (req, res) => {
|
|
28
143
|
preRedirect(req, res, core, function (obj) {
|
|
144
|
+
let monitor = get(["registry", "custom", "gateway", "value", "gotoService", "monitor"], req.soajs);
|
|
29
145
|
let log_monitor_triggered = false;
|
|
30
146
|
let log_monitor = (doc) => {
|
|
31
147
|
if (log_monitor_triggered) {
|
|
32
148
|
return;
|
|
33
149
|
}
|
|
34
150
|
log_monitor_triggered = true;
|
|
151
|
+
|
|
152
|
+
// Check if buffering is enabled
|
|
153
|
+
if (monitor && monitor.buffer && (monitor.buffer.time || monitor.buffer.limit)) {
|
|
154
|
+
bufferMonitorItem(req, doc, monitor);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Original immediate send logic (unchanged)
|
|
35
159
|
let soamonitor = "soamonitor";
|
|
36
160
|
let soamonitorVersion = "1";
|
|
37
161
|
let port = get(["registry", "services", "soamonitor", "port"], req.soajs);
|
|
@@ -72,8 +196,6 @@ module.exports = (configuration) => {
|
|
|
72
196
|
}
|
|
73
197
|
});
|
|
74
198
|
};
|
|
75
|
-
|
|
76
|
-
let monitor = get(["registry", "custom", "gateway", "value", "gotoService", "monitor"], req.soajs);
|
|
77
199
|
let monitoObj = {
|
|
78
200
|
"time": {}
|
|
79
201
|
};
|
package/mw/lastSeen/index.js
CHANGED
|
@@ -52,11 +52,12 @@ function getRouteRegExp(route) {
|
|
|
52
52
|
* Check if the current request should trigger lastSeen
|
|
53
53
|
* @param {Object} include - The include whitelist configuration
|
|
54
54
|
* @param {string} serviceName - The current service name
|
|
55
|
-
* @param {string} apiPath - The current API path
|
|
55
|
+
* @param {string} apiPath - The current API path (relative to service, e.g., "/active")
|
|
56
|
+
* @param {string} fullPath - The full API path (e.g., "/connectspaces/active")
|
|
56
57
|
* @param {string} method - The HTTP method (GET, POST, etc.)
|
|
57
58
|
* @returns {boolean} - true if lastSeen should be triggered
|
|
58
59
|
*/
|
|
59
|
-
function shouldTriggerLastSeen(include, serviceName, apiPath, method) {
|
|
60
|
+
function shouldTriggerLastSeen(include, serviceName, apiPath, fullPath, method) {
|
|
60
61
|
// No include filter = trigger for all (backward compatible)
|
|
61
62
|
if (!include) {
|
|
62
63
|
return true;
|
|
@@ -74,9 +75,17 @@ function shouldTriggerLastSeen(include, serviceName, apiPath, method) {
|
|
|
74
75
|
return true;
|
|
75
76
|
}
|
|
76
77
|
|
|
77
|
-
// API-level match
|
|
78
|
+
// API-level match - check both relative path and full path for backward compatibility
|
|
78
79
|
if (typeof serviceConfig === 'object' && serviceConfig.apis) {
|
|
79
|
-
|
|
80
|
+
// Try relative path first (e.g., "/active")
|
|
81
|
+
if (apiPath && isApiMethodMatch(serviceConfig.apis, apiPath, method)) {
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
// Fallback to full path for backward compatibility (e.g., "/connectspaces/active")
|
|
85
|
+
if (fullPath && fullPath !== apiPath && isApiMethodMatch(serviceConfig.apis, fullPath, method)) {
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
return false;
|
|
80
89
|
}
|
|
81
90
|
|
|
82
91
|
return false;
|
|
@@ -150,11 +159,13 @@ module.exports = () => {
|
|
|
150
159
|
|
|
151
160
|
// Check include filter
|
|
152
161
|
let currentService = get(["soajs", "controller", "serviceParams", "name"], req);
|
|
153
|
-
|
|
162
|
+
// Get both relative path (e.g., "/active") and full path (e.g., "/connectspaces/active")
|
|
163
|
+
let currentApi = get(["soajs", "controller", "serviceParams", "path"], req);
|
|
164
|
+
let fullPath = get(["soajs", "controller", "serviceParams", "parsedUrl", "pathname"], req);
|
|
154
165
|
let currentMethod = req.method;
|
|
155
166
|
|
|
156
|
-
if (!shouldTriggerLastSeen(lastSeen.include, currentService, currentApi, currentMethod)) {
|
|
157
|
-
req.soajs.log.debug("lastSeen skipped for [" + currentService + " " + currentMethod + " " + currentApi + "]");
|
|
167
|
+
if (!shouldTriggerLastSeen(lastSeen.include, currentService, currentApi, fullPath, currentMethod)) {
|
|
168
|
+
req.soajs.log.debug("lastSeen skipped for [" + currentService + " " + currentMethod + " " + (currentApi || fullPath) + "]");
|
|
158
169
|
return;
|
|
159
170
|
}
|
|
160
171
|
|
package/mw/url/index.js
CHANGED
|
@@ -8,9 +8,23 @@
|
|
|
8
8
|
* found in the LICENSE file at the root of this repository
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
const url = require('url');
|
|
12
11
|
let libParseURL = require('../../lib/parseURL');
|
|
13
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Parse URL using WHATWG URL API and return object compatible with legacy url.parse()
|
|
15
|
+
* @param {string} urlString - URL string to parse
|
|
16
|
+
* @returns {Object} - Object with pathname, path, and query properties
|
|
17
|
+
*/
|
|
18
|
+
const parseURL = (urlString) => {
|
|
19
|
+
const urlObj = new URL(urlString, 'http://localhost');
|
|
20
|
+
const search = urlObj.search || '';
|
|
21
|
+
return {
|
|
22
|
+
pathname: urlObj.pathname,
|
|
23
|
+
path: urlObj.pathname + search,
|
|
24
|
+
query: Object.fromEntries(urlObj.searchParams)
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
|
|
14
28
|
/**
|
|
15
29
|
* Assure req param (soajs & soajs.controller)
|
|
16
30
|
* Parse url and fetch service info (soajs.controller.serviceParams)
|
|
@@ -30,7 +44,7 @@ module.exports = (configuration) => {
|
|
|
30
44
|
req.soajs.controller = {};
|
|
31
45
|
}
|
|
32
46
|
|
|
33
|
-
let parsedUrl =
|
|
47
|
+
let parsedUrl = parseURL(req.url);
|
|
34
48
|
if (!req.query && parsedUrl.query) {
|
|
35
49
|
req.query = parsedUrl.query;
|
|
36
50
|
}
|
package/package.json
CHANGED
package/server/maintenance.js
CHANGED
|
@@ -14,8 +14,22 @@ const registryModule = require("./../modules/registry");
|
|
|
14
14
|
|
|
15
15
|
const httpProxy = require('http-proxy');
|
|
16
16
|
const http = require('http');
|
|
17
|
-
const url = require('url');
|
|
18
17
|
const soajsLib = require("soajs.core.libs");
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Parse URL using WHATWG URL API and return object compatible with legacy url.parse()
|
|
21
|
+
* @param {string} urlString - URL string to parse
|
|
22
|
+
* @returns {Object} - Object with pathname, path, and query properties
|
|
23
|
+
*/
|
|
24
|
+
const parseURL = (urlString) => {
|
|
25
|
+
const urlObj = new URL(urlString, 'http://localhost');
|
|
26
|
+
const search = urlObj.search || '';
|
|
27
|
+
return {
|
|
28
|
+
pathname: urlObj.pathname,
|
|
29
|
+
path: urlObj.pathname + search,
|
|
30
|
+
query: Object.fromEntries(urlObj.searchParams)
|
|
31
|
+
};
|
|
32
|
+
};
|
|
19
33
|
const soajsUtils = soajsLib.utils;
|
|
20
34
|
|
|
21
35
|
const packageInfo = require("../package.json");
|
|
@@ -148,7 +162,7 @@ let Maintenance = (core, log, param, serviceIp, regEnvironment, awareness_mw, so
|
|
|
148
162
|
log.error('Internal proxy error: ' + error);
|
|
149
163
|
|
|
150
164
|
res.writeHead(200, {'Content-Type': 'application/json'});
|
|
151
|
-
let parsedUrl =
|
|
165
|
+
let parsedUrl = parseURL(req.url);
|
|
152
166
|
let response = maintenanceResponse(parsedUrl, param, '/proxySocket');
|
|
153
167
|
return res.end(JSON.stringify(response));
|
|
154
168
|
});
|
|
@@ -159,7 +173,7 @@ let Maintenance = (core, log, param, serviceIp, regEnvironment, awareness_mw, so
|
|
|
159
173
|
res.writeHead(200, {'Content-Type': 'image/x-icon'});
|
|
160
174
|
return res.end();
|
|
161
175
|
}
|
|
162
|
-
let parsedUrl =
|
|
176
|
+
let parsedUrl = parseURL(req.url);
|
|
163
177
|
|
|
164
178
|
if (parsedUrl.pathname === '/reloadRegistry') {
|
|
165
179
|
reloadRegistry(parsedUrl, core, log, param, serviceIp, (response) => {
|
|
@@ -1,13 +1,27 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
const url = require('url');
|
|
4
3
|
const helper = require("../../helper.js");
|
|
5
4
|
const lib = helper.requireModule('./lib/parseURL');
|
|
6
5
|
const assert = require('assert');
|
|
7
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Parse URL using WHATWG URL API and return object compatible with legacy url.parse()
|
|
9
|
+
* @param {string} urlString - URL string to parse
|
|
10
|
+
* @returns {Object} - Object with pathname, path, and query properties
|
|
11
|
+
*/
|
|
12
|
+
const parseURL = (urlString) => {
|
|
13
|
+
const urlObj = new URL(urlString, 'http://localhost');
|
|
14
|
+
const search = urlObj.search || '';
|
|
15
|
+
return {
|
|
16
|
+
pathname: urlObj.pathname,
|
|
17
|
+
path: urlObj.pathname + search,
|
|
18
|
+
query: Object.fromEntries(urlObj.searchParams)
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
|
|
8
22
|
describe("Unit test for: lib - parseURL", () => {
|
|
9
23
|
let uri = '/micro2/v1.1/hello';
|
|
10
|
-
let parsedURL =
|
|
24
|
+
let parsedURL = parseURL(uri);
|
|
11
25
|
it("Vanilla test with v1.1", (done) => {
|
|
12
26
|
let response = lib(uri, parsedURL);
|
|
13
27
|
assert.deepStrictEqual(response.name, 'micro2', "lib.parseURL vanilla test with v1.1 failed for name");
|
|
@@ -16,10 +30,10 @@ describe("Unit test for: lib - parseURL", () => {
|
|
|
16
30
|
});
|
|
17
31
|
it("Vanilla test with :1.1", (done) => {
|
|
18
32
|
uri = '/micro2:1.1/hello';
|
|
19
|
-
parsedURL =
|
|
33
|
+
parsedURL = parseURL(uri);
|
|
20
34
|
let response = lib(uri, parsedURL);
|
|
21
35
|
assert.deepStrictEqual(response.name, 'micro2', "lib.parseURL vanilla test with 1:1 failed for name");
|
|
22
36
|
assert.deepStrictEqual(response.service_v, '1.1', "lib.parseURL vanilla test with 1:1 failed version");
|
|
23
37
|
done();
|
|
24
38
|
});
|
|
25
|
-
});
|
|
39
|
+
});
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @license
|
|
5
|
+
* Copyright SOAJS All Rights Reserved.
|
|
6
|
+
*
|
|
7
|
+
* Use of this source code is governed by an Apache license that can be
|
|
8
|
+
* found in the LICENSE file at the root of this repository
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const assert = require('assert');
|
|
12
|
+
|
|
13
|
+
describe("Unit test for: mw - gotoService monitor buffer", function () {
|
|
14
|
+
|
|
15
|
+
// We need to test the buffer module functions directly
|
|
16
|
+
// Since they are module-level in redirectToService.js, we'll test the logic patterns
|
|
17
|
+
|
|
18
|
+
describe("Buffer configuration parsing", function () {
|
|
19
|
+
|
|
20
|
+
it("should recognize buffer config with time only", function (done) {
|
|
21
|
+
let monitor = {
|
|
22
|
+
"buffer": {
|
|
23
|
+
"time": 5000
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
let hasBuffer = monitor && monitor.buffer && (monitor.buffer.time || monitor.buffer.limit);
|
|
27
|
+
assert.strictEqual(hasBuffer, true);
|
|
28
|
+
done();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("should recognize buffer config with limit only", function (done) {
|
|
32
|
+
let monitor = {
|
|
33
|
+
"buffer": {
|
|
34
|
+
"limit": 100
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
let hasBuffer = monitor && monitor.buffer && (monitor.buffer.time || monitor.buffer.limit);
|
|
38
|
+
assert.strictEqual(hasBuffer, true);
|
|
39
|
+
done();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("should recognize buffer config with both time and limit", function (done) {
|
|
43
|
+
let monitor = {
|
|
44
|
+
"buffer": {
|
|
45
|
+
"time": 5000,
|
|
46
|
+
"limit": 100
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
let hasBuffer = monitor && monitor.buffer && (monitor.buffer.time || monitor.buffer.limit);
|
|
50
|
+
assert.strictEqual(hasBuffer, true);
|
|
51
|
+
done();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("should not recognize buffer config when buffer is not set", function (done) {
|
|
55
|
+
let monitor = {
|
|
56
|
+
"req_info": true
|
|
57
|
+
};
|
|
58
|
+
let hasBuffer = monitor && monitor.buffer && (monitor.buffer.time || monitor.buffer.limit);
|
|
59
|
+
assert.strictEqual(!!hasBuffer, false);
|
|
60
|
+
done();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("should not recognize buffer config when buffer is empty", function (done) {
|
|
64
|
+
let monitor = {
|
|
65
|
+
"buffer": {}
|
|
66
|
+
};
|
|
67
|
+
let hasBuffer = monitor && monitor.buffer && (monitor.buffer.time || monitor.buffer.limit);
|
|
68
|
+
assert.strictEqual(!!hasBuffer, false);
|
|
69
|
+
done();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("should not recognize buffer when monitor is null", function (done) {
|
|
73
|
+
let monitor = null;
|
|
74
|
+
let hasBuffer = monitor && monitor.buffer && (monitor.buffer.time || monitor.buffer.limit);
|
|
75
|
+
assert.strictEqual(!!hasBuffer, false);
|
|
76
|
+
done();
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe("Buffer limit threshold logic", function () {
|
|
81
|
+
|
|
82
|
+
it("should trigger flush when buffer reaches limit", function (done) {
|
|
83
|
+
let monitorBuffer = [];
|
|
84
|
+
let bufferLimit = 3;
|
|
85
|
+
|
|
86
|
+
// Simulate adding items
|
|
87
|
+
for (let i = 0; i < 3; i++) {
|
|
88
|
+
monitorBuffer.push({ name: "test", api: "/test" + i });
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
let shouldFlush = bufferLimit && monitorBuffer.length >= bufferLimit;
|
|
92
|
+
assert.strictEqual(shouldFlush, true);
|
|
93
|
+
done();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("should not trigger flush when buffer is below limit", function (done) {
|
|
97
|
+
let monitorBuffer = [];
|
|
98
|
+
let bufferLimit = 10;
|
|
99
|
+
|
|
100
|
+
// Simulate adding items
|
|
101
|
+
for (let i = 0; i < 5; i++) {
|
|
102
|
+
monitorBuffer.push({ name: "test", api: "/test" + i });
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
let shouldFlush = bufferLimit && monitorBuffer.length >= bufferLimit;
|
|
106
|
+
assert.strictEqual(shouldFlush, false);
|
|
107
|
+
done();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("should not trigger flush when no limit is set", function (done) {
|
|
111
|
+
let monitorBuffer = [];
|
|
112
|
+
let bufferLimit = null;
|
|
113
|
+
|
|
114
|
+
// Simulate adding items
|
|
115
|
+
for (let i = 0; i < 100; i++) {
|
|
116
|
+
monitorBuffer.push({ name: "test", api: "/test" + i });
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
let shouldFlush = bufferLimit && monitorBuffer.length >= bufferLimit;
|
|
120
|
+
assert.strictEqual(shouldFlush, false);
|
|
121
|
+
done();
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe("Buffer time threshold logic", function () {
|
|
126
|
+
|
|
127
|
+
it("should start timer when time threshold is set and no timer exists", function (done) {
|
|
128
|
+
let monitorFlushTimer = null;
|
|
129
|
+
let bufferTime = 5000;
|
|
130
|
+
|
|
131
|
+
let shouldStartTimer = bufferTime && !monitorFlushTimer;
|
|
132
|
+
assert.strictEqual(shouldStartTimer, true);
|
|
133
|
+
done();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("should not start timer when timer already exists", function (done) {
|
|
137
|
+
let monitorFlushTimer = setTimeout(() => {}, 5000);
|
|
138
|
+
let bufferTime = 5000;
|
|
139
|
+
|
|
140
|
+
let shouldStartTimer = bufferTime && !monitorFlushTimer;
|
|
141
|
+
assert.strictEqual(shouldStartTimer, false);
|
|
142
|
+
|
|
143
|
+
clearTimeout(monitorFlushTimer);
|
|
144
|
+
done();
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it("should not start timer when no time threshold is set", function (done) {
|
|
148
|
+
let monitorFlushTimer = null;
|
|
149
|
+
let bufferTime = null;
|
|
150
|
+
|
|
151
|
+
let shouldStartTimer = bufferTime && !monitorFlushTimer;
|
|
152
|
+
assert.strictEqual(!!shouldStartTimer, false);
|
|
153
|
+
done();
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
describe("Buffer flush conditions", function () {
|
|
158
|
+
|
|
159
|
+
it("should not flush when buffer is empty", function (done) {
|
|
160
|
+
let monitorBuffer = [];
|
|
161
|
+
let monitorFlushInProgress = false;
|
|
162
|
+
|
|
163
|
+
let shouldFlush = !(monitorFlushInProgress || monitorBuffer.length === 0);
|
|
164
|
+
assert.strictEqual(shouldFlush, false);
|
|
165
|
+
done();
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("should not flush when flush is already in progress", function (done) {
|
|
169
|
+
let monitorBuffer = [{ name: "test" }];
|
|
170
|
+
let monitorFlushInProgress = true;
|
|
171
|
+
|
|
172
|
+
let shouldFlush = !(monitorFlushInProgress || monitorBuffer.length === 0);
|
|
173
|
+
assert.strictEqual(shouldFlush, false);
|
|
174
|
+
done();
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it("should flush when buffer has items and no flush in progress", function (done) {
|
|
178
|
+
let monitorBuffer = [{ name: "test" }];
|
|
179
|
+
let monitorFlushInProgress = false;
|
|
180
|
+
|
|
181
|
+
let shouldFlush = !(monitorFlushInProgress || monitorBuffer.length === 0);
|
|
182
|
+
assert.strictEqual(shouldFlush, true);
|
|
183
|
+
done();
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
describe("Monitor document transformation", function () {
|
|
188
|
+
|
|
189
|
+
it("should convert body to string before sending", function (done) {
|
|
190
|
+
let doc = {
|
|
191
|
+
name: "test",
|
|
192
|
+
body: Buffer.from("test body")
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
if (doc.body) {
|
|
196
|
+
doc.body = doc.body.toString();
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
assert.strictEqual(typeof doc.body, "string");
|
|
200
|
+
assert.strictEqual(doc.body, "test body");
|
|
201
|
+
done();
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it("should convert response to string before sending", function (done) {
|
|
205
|
+
let doc = {
|
|
206
|
+
name: "test",
|
|
207
|
+
response: Buffer.from('{"result": true}')
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
if (doc.response) {
|
|
211
|
+
doc.response = doc.response.toString();
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
assert.strictEqual(typeof doc.response, "string");
|
|
215
|
+
assert.strictEqual(doc.response, '{"result": true}');
|
|
216
|
+
done();
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it("should handle doc without body or response", function (done) {
|
|
220
|
+
let doc = {
|
|
221
|
+
name: "test",
|
|
222
|
+
api: "/test"
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
if (doc.body) {
|
|
226
|
+
doc.body = doc.body.toString();
|
|
227
|
+
}
|
|
228
|
+
if (doc.response) {
|
|
229
|
+
doc.response = doc.response.toString();
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
assert.strictEqual(doc.body, undefined);
|
|
233
|
+
assert.strictEqual(doc.response, undefined);
|
|
234
|
+
done();
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
describe("Backward compatibility", function () {
|
|
239
|
+
|
|
240
|
+
it("should send immediately when no buffer config (backward compatible)", function (done) {
|
|
241
|
+
let monitor = {
|
|
242
|
+
"req_info": true,
|
|
243
|
+
"req_response": true
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
let hasBuffer = monitor && monitor.buffer && (monitor.buffer.time || monitor.buffer.limit);
|
|
247
|
+
let shouldSendImmediately = !hasBuffer;
|
|
248
|
+
|
|
249
|
+
assert.strictEqual(shouldSendImmediately, true);
|
|
250
|
+
done();
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it("should use buffer when buffer config is present", function (done) {
|
|
254
|
+
let monitor = {
|
|
255
|
+
"req_info": true,
|
|
256
|
+
"req_response": true,
|
|
257
|
+
"buffer": {
|
|
258
|
+
"time": 5000,
|
|
259
|
+
"limit": 100
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
let hasBuffer = monitor && monitor.buffer && (monitor.buffer.time || monitor.buffer.limit);
|
|
264
|
+
let shouldSendImmediately = !hasBuffer;
|
|
265
|
+
|
|
266
|
+
assert.strictEqual(shouldSendImmediately, false);
|
|
267
|
+
done();
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
describe("Buffer retry logic", function () {
|
|
272
|
+
|
|
273
|
+
it("should put items back in buffer when host not found", function (done) {
|
|
274
|
+
let monitorBuffer = [];
|
|
275
|
+
let itemsToSend = [{ name: "test1" }, { name: "test2" }];
|
|
276
|
+
let host = null;
|
|
277
|
+
|
|
278
|
+
// Simulate host not found - items should go back to buffer
|
|
279
|
+
if (!host) {
|
|
280
|
+
monitorBuffer = itemsToSend.concat(monitorBuffer);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
assert.strictEqual(monitorBuffer.length, 2);
|
|
284
|
+
assert.strictEqual(monitorBuffer[0].name, "test1");
|
|
285
|
+
done();
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
it("should preserve order when putting items back", function (done) {
|
|
289
|
+
let monitorBuffer = [{ name: "new1" }];
|
|
290
|
+
let itemsToSend = [{ name: "old1" }, { name: "old2" }];
|
|
291
|
+
let host = null;
|
|
292
|
+
|
|
293
|
+
// Simulate host not found - items should go back to buffer
|
|
294
|
+
if (!host) {
|
|
295
|
+
monitorBuffer = itemsToSend.concat(monitorBuffer);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
assert.strictEqual(monitorBuffer.length, 3);
|
|
299
|
+
assert.strictEqual(monitorBuffer[0].name, "old1");
|
|
300
|
+
assert.strictEqual(monitorBuffer[1].name, "old2");
|
|
301
|
+
assert.strictEqual(monitorBuffer[2].name, "new1");
|
|
302
|
+
done();
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
});
|