@milaboratories/pl-middle-layer 1.37.4 → 1.37.6
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/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +40 -14
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1747 -1468
- package/dist/index.mjs.map +1 -1
- package/dist/mutator/context_export.d.ts +2 -2
- package/dist/mutator/context_export.d.ts.map +1 -1
- package/dist/mutator/template/render_template.d.ts +2 -2
- package/dist/mutator/template/render_template.d.ts.map +1 -1
- package/dist/network_check/network_check.d.ts +40 -0
- package/dist/network_check/network_check.d.ts.map +1 -0
- package/dist/network_check/network_check.test.d.ts.map +1 -0
- package/dist/network_check/pings.d.ts +32 -0
- package/dist/network_check/pings.d.ts.map +1 -0
- package/dist/network_check/template.d.ts +33 -0
- package/dist/network_check/template.d.ts.map +1 -0
- package/dist/network_check/template.test.d.ts +2 -0
- package/dist/network_check/template.test.d.ts.map +1 -0
- package/dist/network_check/test_utils.d.ts +6 -0
- package/dist/network_check/test_utils.d.ts.map +1 -0
- package/package.json +10 -10
- package/src/index.ts +1 -1
- package/src/mutator/template/render_template.ts +2 -2
- package/src/{network_check.test.ts → network_check/network_check.test.ts} +3 -3
- package/src/network_check/network_check.ts +369 -0
- package/src/network_check/pings.ts +154 -0
- package/src/network_check/template.test.ts +83 -0
- package/src/network_check/template.ts +330 -0
- package/src/network_check/test_utils.ts +9 -0
- package/dist/network_check.d.ts +0 -29
- package/dist/network_check.d.ts.map +0 -1
- package/dist/network_check.test.d.ts.map +0 -1
- package/src/network_check.ts +0 -329
- /package/dist/{network_check.test.d.ts → network_check/network_check.test.d.ts} +0 -0
package/src/network_check.ts
DELETED
|
@@ -1,329 +0,0 @@
|
|
|
1
|
-
/** A utility to check network problems and gather statistics.
|
|
2
|
-
* It's useful when we cannot connect to the server of a company
|
|
3
|
-
* because of security reasons,
|
|
4
|
-
* but they can send us and their DevOps team this report. */
|
|
5
|
-
|
|
6
|
-
import { UnauthenticatedPlClient, plAddressToConfig } from '@milaboratories/pl-client';
|
|
7
|
-
import type { ValueOrError } from '@milaboratories/ts-helpers';
|
|
8
|
-
import { setTimeout } from 'node:timers/promises';
|
|
9
|
-
import type { Dispatcher } from 'undici';
|
|
10
|
-
import { request } from 'undici';
|
|
11
|
-
import { channel } from 'node:diagnostics_channel';
|
|
12
|
-
|
|
13
|
-
/** All reports we need to collect. */
|
|
14
|
-
interface networkReports {
|
|
15
|
-
plPings: networkReport<string>[];
|
|
16
|
-
|
|
17
|
-
blockRegistryOverviewChecks: httpNetworkReport[];
|
|
18
|
-
blockGARegistryOverviewChecks: httpNetworkReport[];
|
|
19
|
-
blockRegistryUiChecks: httpNetworkReport[];
|
|
20
|
-
blockGARegistryUiChecks: httpNetworkReport[];
|
|
21
|
-
|
|
22
|
-
autoUpdateCdnChecks: httpNetworkReport[];
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface CheckNetworkOpts {
|
|
26
|
-
/** Platforma Backend pings options. */
|
|
27
|
-
pingCheckDurationMs: number;
|
|
28
|
-
pingTimeoutMs: number;
|
|
29
|
-
maxPingsPerSecond: number;
|
|
30
|
-
|
|
31
|
-
/** An options for CDN and block registry. */
|
|
32
|
-
httpTimeoutMs: number;
|
|
33
|
-
|
|
34
|
-
/** Block registry pings options. */
|
|
35
|
-
blockRegistryDurationMs: number;
|
|
36
|
-
maxRegistryChecksPerSecond: number;
|
|
37
|
-
blockRegistryUrl: string;
|
|
38
|
-
blockGARegistryUrl: string;
|
|
39
|
-
blockOverviewPath: string;
|
|
40
|
-
blockUiPath: string;
|
|
41
|
-
|
|
42
|
-
/** CDN for auto-update pings options. */
|
|
43
|
-
autoUpdateCdnDurationMs: number;
|
|
44
|
-
maxAutoUpdateCdnChecksPerSecond: number;
|
|
45
|
-
autoUpdateCdnUrl: string;
|
|
46
|
-
|
|
47
|
-
bodyLimit: number;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/** A report about one concrete ping to the service. */
|
|
51
|
-
interface networkReport<T> {
|
|
52
|
-
elapsedMs: number;
|
|
53
|
-
response: ValueOrError<T>;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
type httpNetworkReport = networkReport<{
|
|
57
|
-
statusCode: number;
|
|
58
|
-
beginningOfBody: string;
|
|
59
|
-
}>;
|
|
60
|
-
|
|
61
|
-
// List of Undici diagnostic channels
|
|
62
|
-
const undiciEvents: string[] = [
|
|
63
|
-
'undici:request:create', // When a new request is created
|
|
64
|
-
'undici:request:bodySent', // When the request body is sent
|
|
65
|
-
'undici:request:headers', // When request headers are sent
|
|
66
|
-
'undici:request:error', // When a request encounters an error
|
|
67
|
-
'undici:request:trailers', // When a response completes.
|
|
68
|
-
|
|
69
|
-
'undici:client:sendHeaders',
|
|
70
|
-
'undici:client:beforeConnect',
|
|
71
|
-
'undici:client:connected',
|
|
72
|
-
'undici:client:connectError',
|
|
73
|
-
|
|
74
|
-
'undici:socket:close', // When a socket is closed
|
|
75
|
-
'undici:socket:connect', // When a socket connects
|
|
76
|
-
'undici:socket:error', // When a socket encounters an error
|
|
77
|
-
|
|
78
|
-
'undici:pool:request', // When a request is added to the pool
|
|
79
|
-
'undici:pool:connect', // When a pool creates a new connection
|
|
80
|
-
'undici:pool:disconnect', // When a pool connection is closed
|
|
81
|
-
'undici:pool:destroy', // When a pool is destroyed
|
|
82
|
-
'undici:dispatcher:request', // When a dispatcher processes a request
|
|
83
|
-
'undici:dispatcher:connect', // When a dispatcher connects
|
|
84
|
-
'undici:dispatcher:disconnect', // When a dispatcher disconnects
|
|
85
|
-
'undici:dispatcher:retry', // When a dispatcher retries a request
|
|
86
|
-
];
|
|
87
|
-
|
|
88
|
-
/** Checks connectivity to Platforma Backend, to block registry
|
|
89
|
-
* and to auto-update CDN,
|
|
90
|
-
* and generates a string report. */
|
|
91
|
-
export async function checkNetwork(
|
|
92
|
-
plCredentials: string,
|
|
93
|
-
optsOverrides: Partial<CheckNetworkOpts> = {},
|
|
94
|
-
): Promise<string> {
|
|
95
|
-
const ops: CheckNetworkOpts = {
|
|
96
|
-
pingCheckDurationMs: 10000,
|
|
97
|
-
pingTimeoutMs: 3000,
|
|
98
|
-
maxPingsPerSecond: 50,
|
|
99
|
-
|
|
100
|
-
httpTimeoutMs: 3000,
|
|
101
|
-
|
|
102
|
-
blockRegistryDurationMs: 3000,
|
|
103
|
-
maxRegistryChecksPerSecond: 1,
|
|
104
|
-
|
|
105
|
-
blockRegistryUrl: 'https://blocks.pl-open.science',
|
|
106
|
-
blockGARegistryUrl: 'https://blocks-ga.pl-open.science',
|
|
107
|
-
blockOverviewPath: 'v2/overview.json',
|
|
108
|
-
blockUiPath: 'v2/milaboratories/samples-and-data/1.7.0/ui.tgz',
|
|
109
|
-
|
|
110
|
-
autoUpdateCdnDurationMs: 5000,
|
|
111
|
-
maxAutoUpdateCdnChecksPerSecond: 1,
|
|
112
|
-
autoUpdateCdnUrl:
|
|
113
|
-
'https://cdn.platforma.bio/software/platforma-desktop-v2/windows/amd64/latest.yml',
|
|
114
|
-
|
|
115
|
-
bodyLimit: 300,
|
|
116
|
-
|
|
117
|
-
...optsOverrides,
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
const undiciLogs: any[] = [];
|
|
121
|
-
// Subscribe to all Undici diagnostic events
|
|
122
|
-
undiciEvents.forEach((event) => {
|
|
123
|
-
const diagnosticChannel = channel(event);
|
|
124
|
-
diagnosticChannel.subscribe((message: any) => {
|
|
125
|
-
const timestamp = new Date().toISOString();
|
|
126
|
-
if (message?.response?.headers)
|
|
127
|
-
message.response.headers = message.response.headers.map((h: any) => h.toString());
|
|
128
|
-
|
|
129
|
-
undiciLogs.push(
|
|
130
|
-
JSON.stringify({
|
|
131
|
-
timestamp,
|
|
132
|
-
event,
|
|
133
|
-
data: message,
|
|
134
|
-
}),
|
|
135
|
-
);
|
|
136
|
-
});
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
const report: networkReports = {
|
|
140
|
-
plPings: [],
|
|
141
|
-
blockRegistryOverviewChecks: [],
|
|
142
|
-
blockGARegistryOverviewChecks: [],
|
|
143
|
-
blockRegistryUiChecks: [],
|
|
144
|
-
blockGARegistryUiChecks: [],
|
|
145
|
-
|
|
146
|
-
autoUpdateCdnChecks: [],
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
const plConfig = plAddressToConfig(plCredentials, { defaultRequestTimeout: ops.pingTimeoutMs });
|
|
150
|
-
|
|
151
|
-
report.plPings = await recordPings(ops.pingCheckDurationMs, ops.maxPingsPerSecond, async () => {
|
|
152
|
-
const uaClient = new UnauthenticatedPlClient(plConfig);
|
|
153
|
-
const response = await uaClient.ping();
|
|
154
|
-
return JSON.stringify(response).slice(0, ops.bodyLimit) + '...';
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
const uaClient = new UnauthenticatedPlClient(plConfig);
|
|
158
|
-
const httpClient = uaClient.ll.httpDispatcher;
|
|
159
|
-
|
|
160
|
-
report.blockRegistryOverviewChecks = await recordPings(
|
|
161
|
-
ops.blockRegistryDurationMs,
|
|
162
|
-
ops.maxRegistryChecksPerSecond,
|
|
163
|
-
async () =>
|
|
164
|
-
await requestUrl(new URL(ops.blockOverviewPath, ops.blockRegistryUrl), ops, httpClient),
|
|
165
|
-
);
|
|
166
|
-
|
|
167
|
-
report.blockGARegistryOverviewChecks = await recordPings(
|
|
168
|
-
ops.blockRegistryDurationMs,
|
|
169
|
-
ops.maxRegistryChecksPerSecond,
|
|
170
|
-
async () =>
|
|
171
|
-
await requestUrl(new URL(ops.blockOverviewPath, ops.blockGARegistryUrl), ops, httpClient),
|
|
172
|
-
);
|
|
173
|
-
|
|
174
|
-
report.blockRegistryUiChecks = await recordPings(
|
|
175
|
-
ops.blockRegistryDurationMs,
|
|
176
|
-
ops.maxRegistryChecksPerSecond,
|
|
177
|
-
async () => await requestUrl(new URL(ops.blockUiPath, ops.blockRegistryUrl), ops, httpClient),
|
|
178
|
-
);
|
|
179
|
-
|
|
180
|
-
report.blockGARegistryUiChecks = await recordPings(
|
|
181
|
-
ops.blockRegistryDurationMs,
|
|
182
|
-
ops.maxRegistryChecksPerSecond,
|
|
183
|
-
async () => await requestUrl(new URL(ops.blockUiPath, ops.blockGARegistryUrl), ops, httpClient),
|
|
184
|
-
);
|
|
185
|
-
|
|
186
|
-
report.autoUpdateCdnChecks = await recordPings(
|
|
187
|
-
ops.autoUpdateCdnDurationMs,
|
|
188
|
-
ops.maxAutoUpdateCdnChecksPerSecond,
|
|
189
|
-
async () => await requestUrl(ops.autoUpdateCdnUrl, ops, httpClient),
|
|
190
|
-
);
|
|
191
|
-
|
|
192
|
-
return reportsToString(report, plCredentials, ops, undiciLogs);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/** Executes a body several times per second up to the given duration,
|
|
196
|
-
* and returns results and elapsed time for every result. */
|
|
197
|
-
async function recordPings<T>(
|
|
198
|
-
pingCheckDurationMs: number,
|
|
199
|
-
maxPingsPerSecond: number,
|
|
200
|
-
body: () => Promise<T>,
|
|
201
|
-
): Promise<networkReport<T>[]> {
|
|
202
|
-
const startPings = nowMs();
|
|
203
|
-
const reports: networkReport<T>[] = [];
|
|
204
|
-
|
|
205
|
-
while (elapsed(startPings) < pingCheckDurationMs) {
|
|
206
|
-
const startPing = nowMs();
|
|
207
|
-
let response: ValueOrError<T>;
|
|
208
|
-
try {
|
|
209
|
-
response = { ok: true, value: await body() };
|
|
210
|
-
} catch (e) {
|
|
211
|
-
response = { ok: false, error: e };
|
|
212
|
-
}
|
|
213
|
-
const elapsedPing = elapsed(startPing);
|
|
214
|
-
|
|
215
|
-
reports.push({
|
|
216
|
-
elapsedMs: elapsedPing,
|
|
217
|
-
response,
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
const sleepBetweenPings = 1000 / maxPingsPerSecond - elapsedPing;
|
|
221
|
-
|
|
222
|
-
if (sleepBetweenPings > 0) await setTimeout(sleepBetweenPings);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
return reports;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
async function requestUrl(url: string | URL, ops: CheckNetworkOpts, httpClient: Dispatcher) {
|
|
229
|
-
const { body: rawBody, statusCode } = await request(url, {
|
|
230
|
-
dispatcher: httpClient,
|
|
231
|
-
headersTimeout: ops.httpTimeoutMs,
|
|
232
|
-
bodyTimeout: ops.httpTimeoutMs,
|
|
233
|
-
});
|
|
234
|
-
const body = await rawBody.text();
|
|
235
|
-
|
|
236
|
-
return {
|
|
237
|
-
statusCode: statusCode,
|
|
238
|
-
beginningOfBody: body.slice(0, ops.bodyLimit) + '...',
|
|
239
|
-
};
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
function reportsToString(
|
|
243
|
-
report: networkReports,
|
|
244
|
-
plEndpoint: string,
|
|
245
|
-
opts: CheckNetworkOpts,
|
|
246
|
-
undiciLogs: any[],
|
|
247
|
-
): string {
|
|
248
|
-
const successPings = report.plPings.filter((p) => p.response.ok);
|
|
249
|
-
const failedPings = report.plPings.filter((p) => !p.response.ok);
|
|
250
|
-
const successPingsBodies = [
|
|
251
|
-
...new Set(successPings.map((p) => JSON.stringify((p.response as any).value))),
|
|
252
|
-
];
|
|
253
|
-
|
|
254
|
-
return `
|
|
255
|
-
Network report:
|
|
256
|
-
pl endpoint: ${plEndpoint};
|
|
257
|
-
options: ${JSON.stringify(opts, null, 2)}.
|
|
258
|
-
|
|
259
|
-
Platforma pings: ${reportToString(report.plPings)}
|
|
260
|
-
|
|
261
|
-
Block registry overview responses: ${reportToString(report.blockRegistryOverviewChecks)}
|
|
262
|
-
|
|
263
|
-
Block ga registry overview responses: ${reportToString(report.blockGARegistryOverviewChecks)}
|
|
264
|
-
|
|
265
|
-
Block registry ui responses: ${reportToString(report.blockRegistryUiChecks)}
|
|
266
|
-
|
|
267
|
-
Block ga registry ui responses: ${reportToString(report.blockGARegistryUiChecks)}
|
|
268
|
-
|
|
269
|
-
Auto-update CDN responses: ${reportToString(report.autoUpdateCdnChecks)}
|
|
270
|
-
|
|
271
|
-
Block registry overview dumps:
|
|
272
|
-
${JSON.stringify(report.blockRegistryOverviewChecks, null, 2)}
|
|
273
|
-
|
|
274
|
-
Block ga registry overview dumps:
|
|
275
|
-
${JSON.stringify(report.blockGARegistryOverviewChecks, null, 2)}
|
|
276
|
-
|
|
277
|
-
Block registry ui dumps:
|
|
278
|
-
${JSON.stringify(report.blockRegistryUiChecks, null, 2)}
|
|
279
|
-
|
|
280
|
-
Block ga registry ui dumps:
|
|
281
|
-
${JSON.stringify(report.blockGARegistryUiChecks, null, 2)}
|
|
282
|
-
|
|
283
|
-
Auto-update CDN dumps:
|
|
284
|
-
${JSON.stringify(report.autoUpdateCdnChecks, null, 2)}
|
|
285
|
-
|
|
286
|
-
Platforma pings error dumps:
|
|
287
|
-
${JSON.stringify(failedPings, null, 2)}
|
|
288
|
-
|
|
289
|
-
Platforma pings success dump examples:
|
|
290
|
-
${JSON.stringify(successPingsBodies, null, 2)}
|
|
291
|
-
|
|
292
|
-
Undici logs:
|
|
293
|
-
${undiciLogs.join('\n')}
|
|
294
|
-
`;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
function reportToString<T>(report: networkReport<T>[]): string {
|
|
298
|
-
const successes = report.filter((r) => r.response.ok);
|
|
299
|
-
const { mean: mean, median: median } = elapsedStat(report);
|
|
300
|
-
|
|
301
|
-
return `
|
|
302
|
-
total: ${report.length};
|
|
303
|
-
successes: ${successes.length};
|
|
304
|
-
errors: ${report.length - successes.length};
|
|
305
|
-
mean in ms: ${mean};
|
|
306
|
-
median in ms: ${median};
|
|
307
|
-
`;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
function elapsedStat(reports: { elapsedMs: number }[]) {
|
|
311
|
-
const checks = reports.map((p) => p.elapsedMs);
|
|
312
|
-
const mean = checks.reduce((sum, p) => sum + p) / checks.length;
|
|
313
|
-
|
|
314
|
-
let median = undefined;
|
|
315
|
-
if (checks.length > 0) {
|
|
316
|
-
const mid = Math.floor(checks.length / 2);
|
|
317
|
-
median = checks.length % 2 ? checks[mid] : (checks[mid - 1] + checks[mid]) / 2;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
return { mean, median };
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
function nowMs(): number {
|
|
324
|
-
return Date.now();
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
function elapsed(startMs: number): number {
|
|
328
|
-
return nowMs() - startMs;
|
|
329
|
-
}
|
|
File without changes
|