querysub 0.395.0 → 0.397.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/package.json +1 -1
- package/src/-a-archives/archivesDisk.ts +2 -2
- package/src/-c-identity/IdentityController.ts +19 -1
- package/src/0-path-value-core/pathValueCore.ts +8 -8
- package/src/3-path-functions/pathFunctionLoader.ts +2 -2
- package/src/deployManager/machineApplyMainCode.ts +1 -1
- package/src/diagnostics/logs/IndexedLogs/LogViewer3.tsx +3 -4
- package/src/diagnostics/logs/IndexedLogs/bufferSearchFindMatcher.ts +1 -0
- package/src/diagnostics/logs/errorNotifications2/ErrorNotificationPage.tsx +3 -1
- package/src/diagnostics/logs/lifeCycleAnalysis/LifeCycleEntryEditor.tsx +358 -0
- package/src/diagnostics/logs/lifeCycleAnalysis/LifeCycleEntryReadMode.tsx +149 -0
- package/src/diagnostics/logs/lifeCycleAnalysis/LifeCyclePage.tsx +33 -705
- package/src/diagnostics/logs/lifeCycleAnalysis/LifeCycleRenderer.tsx +291 -0
- package/src/diagnostics/logs/lifeCycleAnalysis/NestedLifeCycleInfo.tsx +151 -0
- package/src/diagnostics/logs/lifeCycleAnalysis/lifeCycleSearch.tsx +61 -34
- package/src/diagnostics/logs/lifeCycleAnalysis/lifeCycles.tsx +3 -0
- package/src/diagnostics/misc-pages/ArchiveViewer.tsx +4 -3
- package/src/diagnostics/misc-pages/ArchiveViewerTree.tsx +6 -3
- package/src/library-components/StartEllipsis.tsx +1 -1
- package/tempnotes.txt +0 -0
|
@@ -18,13 +18,18 @@ import { MachineThreadInfo } from "../../MachineThreadInfo";
|
|
|
18
18
|
import { formatDateTime, formatTime } from "socket-function/src/formatting/format";
|
|
19
19
|
import { getPathStr } from "../../../path";
|
|
20
20
|
import { niceStringify } from "../../../niceStringify";
|
|
21
|
-
import { createLifeCycleSearch, LifecycleInstance
|
|
21
|
+
import { createLifeCycleSearch, LifecycleInstance } from "./lifeCycleSearch";
|
|
22
22
|
import { getLifecycleMatchesForDatum } from "./lifeCycleMatching";
|
|
23
23
|
import { managementPageURL } from "../../managementPages";
|
|
24
24
|
import { startTimeParam, endTimeParam } from "../TimeRangeSelector";
|
|
25
25
|
import { formatValue } from "../../../5-diagnostics/GenericFormat";
|
|
26
26
|
import { StartEllipsis } from "../../../library-components/StartEllipsis";
|
|
27
|
+
import { LifeCycleEntryReadMode } from "./LifeCycleEntryReadMode";
|
|
28
|
+
import { LifeCycleEntryEditor } from "./LifeCycleEntryEditor";
|
|
29
|
+
import { LifeCycleRenderer, LifeCycleInstanceRenderer } from "./LifeCycleRenderer";
|
|
27
30
|
export let lifecycleIdURL = new URLParam("lifecycleid", "");
|
|
31
|
+
export let limitURL = new URLParam("lifecyclelimit", 100);
|
|
32
|
+
export let additionalSearchURL = new URLParam("lifecyclesearch", "");
|
|
28
33
|
|
|
29
34
|
export class LifeCyclePage extends qreact.Component {
|
|
30
35
|
controller = LifeCyclesController(SocketFunction.browserNodeId());
|
|
@@ -39,13 +44,14 @@ export class LifeCyclePage extends qreact.Component {
|
|
|
39
44
|
phase2Results: t.atomic<LogDatum[]>([]),
|
|
40
45
|
phase2Stats: t.atomic<IndexedLogResults | undefined>(undefined),
|
|
41
46
|
phase2Searching: t.boolean(false),
|
|
47
|
+
phase2HitLimit: t.boolean(false),
|
|
42
48
|
lifecycleInstances: t.atomic<LifecycleInstance[]>([]),
|
|
43
49
|
});
|
|
44
50
|
|
|
45
51
|
search = createLifeCycleSearch(this);
|
|
46
52
|
|
|
47
53
|
searchLifeCycle = (lifeCycleId: string) => {
|
|
48
|
-
return this.search.searchLifeCycle(lifeCycleId);
|
|
54
|
+
return this.search.searchLifeCycle({ lifeCycleId, additionalSearch: additionalSearchURL.value.trim(), limit: limitURL.value });
|
|
49
55
|
};
|
|
50
56
|
|
|
51
57
|
componentDidMount() {
|
|
@@ -96,18 +102,29 @@ export class LifeCyclePage extends qreact.Component {
|
|
|
96
102
|
placeholder="Additional search filter..."
|
|
97
103
|
url={additionalSearchURL}
|
|
98
104
|
fillWidth
|
|
105
|
+
onKeyDown={(e) => {
|
|
106
|
+
if (e.key === "Enter" && lifecycleIdURL.value) {
|
|
107
|
+
void this.searchLifeCycle(lifecycleIdURL.value);
|
|
108
|
+
}
|
|
109
|
+
}}
|
|
99
110
|
/>
|
|
100
111
|
|
|
101
112
|
<div className={css.hbox(12).wrap}>
|
|
102
113
|
<InputLabel
|
|
103
114
|
placeholder="Add new life cycle"
|
|
104
115
|
onChangeValue={(value) => {
|
|
105
|
-
|
|
106
|
-
if (
|
|
116
|
+
value = value.trim();
|
|
117
|
+
if (value) {
|
|
107
118
|
let newLifeCycle: LifeCycle = {
|
|
108
119
|
id: nextId(),
|
|
109
|
-
title,
|
|
110
|
-
entries: [
|
|
120
|
+
title: value,
|
|
121
|
+
entries: [{
|
|
122
|
+
matchPattern: value,
|
|
123
|
+
groupByKeys: [],
|
|
124
|
+
isStart: true,
|
|
125
|
+
sourceType: "log",
|
|
126
|
+
variables: {},
|
|
127
|
+
}],
|
|
111
128
|
};
|
|
112
129
|
Querysub.onCommitFinished(async () => {
|
|
113
130
|
await this.controller.setLifeCycle.promise(newLifeCycle);
|
|
@@ -129,13 +146,8 @@ export class LifeCyclePage extends qreact.Component {
|
|
|
129
146
|
<Button
|
|
130
147
|
hue={120}
|
|
131
148
|
onClick={() => {
|
|
132
|
-
this.
|
|
133
|
-
|
|
134
|
-
this.state.phase1InvalidResults = [];
|
|
135
|
-
this.state.phase1Stats = undefined;
|
|
136
|
-
this.state.phase2Results = [];
|
|
137
|
-
this.state.phase2Stats = undefined;
|
|
138
|
-
this.state.lifecycleInstances = [];
|
|
149
|
+
this.search.resetAllSearchState(undefined);
|
|
150
|
+
lifecycleIdURL.value = "";
|
|
139
151
|
}}
|
|
140
152
|
>
|
|
141
153
|
Show All Life Cycles
|
|
@@ -225,9 +237,16 @@ export class LifeCyclePage extends qreact.Component {
|
|
|
225
237
|
<RenderSearchStats
|
|
226
238
|
stats={this.state.phase2Stats}
|
|
227
239
|
searching={this.state.phase2Searching}
|
|
228
|
-
limit={
|
|
240
|
+
limit={0}
|
|
229
241
|
/>
|
|
230
242
|
)}
|
|
243
|
+
{this.state.phase2HitLimit && (
|
|
244
|
+
<div className={css.pad2(12).bord2(30, 60, 70).hsl(30, 50, 95).vbox(4)}>
|
|
245
|
+
<div className={css.boldStyle.colorhsl(30, 80, 40)}>
|
|
246
|
+
⚠ BUG: Phase 2 search hit the limit, increase the constant factor at the top of life cycle search.
|
|
247
|
+
</div>
|
|
248
|
+
</div>
|
|
249
|
+
)}
|
|
231
250
|
</div>
|
|
232
251
|
)}
|
|
233
252
|
|
|
@@ -253,694 +272,3 @@ export class LifeCyclePage extends qreact.Component {
|
|
|
253
272
|
</div>;
|
|
254
273
|
}
|
|
255
274
|
}
|
|
256
|
-
|
|
257
|
-
export class LifeCycleRenderer extends qreact.Component<{
|
|
258
|
-
lifeCycle: LifeCycle;
|
|
259
|
-
defaultEditMode?: boolean;
|
|
260
|
-
onSearch?: (lifeCycleId: string) => void;
|
|
261
|
-
}> {
|
|
262
|
-
state = t.state({
|
|
263
|
-
expanded: t.atomic<boolean>(false),
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
controller = LifeCyclesController(SocketFunction.browserNodeId());
|
|
267
|
-
|
|
268
|
-
render() {
|
|
269
|
-
let lifeCycle = this.props.lifeCycle;
|
|
270
|
-
let isSelected = lifecycleIdURL.value === lifeCycle.id;
|
|
271
|
-
let bgClass = isSelected && css.hsl(280, 50, 90) || css.hsl(200, 30, 95);
|
|
272
|
-
let borderHue = isSelected && 280 || 200;
|
|
273
|
-
|
|
274
|
-
return <div className={css.pad2(12).bord2(borderHue, 30, 70).vbox(8) + bgClass}>
|
|
275
|
-
<div className={css.hbox(12)}>
|
|
276
|
-
<Button
|
|
277
|
-
hue={borderHue}
|
|
278
|
-
onClick={() => {
|
|
279
|
-
this.state.expanded = !this.state.expanded;
|
|
280
|
-
}}
|
|
281
|
-
>
|
|
282
|
-
{this.state.expanded && "▼" || "▶"} {lifeCycle.title}
|
|
283
|
-
</Button>
|
|
284
|
-
<span>{lifeCycle.entries.length} entries</span>
|
|
285
|
-
{this.props.onSearch && (
|
|
286
|
-
<Button
|
|
287
|
-
hue={280}
|
|
288
|
-
onClick={() => {
|
|
289
|
-
if (this.props.onSearch) {
|
|
290
|
-
this.props.onSearch(lifeCycle.id);
|
|
291
|
-
}
|
|
292
|
-
}}
|
|
293
|
-
>
|
|
294
|
-
Search
|
|
295
|
-
</Button>
|
|
296
|
-
)}
|
|
297
|
-
{isSelected && (
|
|
298
|
-
<Button
|
|
299
|
-
hue={200}
|
|
300
|
-
onClick={() => {
|
|
301
|
-
lifecycleIdURL.value = "";
|
|
302
|
-
}}
|
|
303
|
-
>
|
|
304
|
-
Clear Selection
|
|
305
|
-
</Button>
|
|
306
|
-
) || (
|
|
307
|
-
<Button
|
|
308
|
-
hue={280}
|
|
309
|
-
onClick={() => {
|
|
310
|
-
lifecycleIdURL.value = lifeCycle.id;
|
|
311
|
-
}}
|
|
312
|
-
>
|
|
313
|
-
Pin
|
|
314
|
-
</Button>
|
|
315
|
-
)}
|
|
316
|
-
<Button
|
|
317
|
-
hue={0}
|
|
318
|
-
onClick={() => {
|
|
319
|
-
if (!confirm(`Delete life cycle "${lifeCycle.title}"?`)) return;
|
|
320
|
-
|
|
321
|
-
Querysub.onCommitFinished(async () => {
|
|
322
|
-
await this.controller.deleteLifeCycle.promise(lifeCycle.id);
|
|
323
|
-
});
|
|
324
|
-
}}
|
|
325
|
-
>
|
|
326
|
-
Delete
|
|
327
|
-
</Button>
|
|
328
|
-
</div>
|
|
329
|
-
|
|
330
|
-
{this.state.expanded && (
|
|
331
|
-
<div className={css.vbox(8)}>
|
|
332
|
-
<InputLabel
|
|
333
|
-
label="Title"
|
|
334
|
-
value={lifeCycle.title}
|
|
335
|
-
onChangeValue={(value) => {
|
|
336
|
-
let title = value.trim();
|
|
337
|
-
if (!title) return;
|
|
338
|
-
let updatedLifeCycle = deepCloneJSON(lifeCycle);
|
|
339
|
-
updatedLifeCycle.title = title;
|
|
340
|
-
Querysub.onCommitFinished(async () => {
|
|
341
|
-
await this.controller.setLifeCycle.promise(updatedLifeCycle);
|
|
342
|
-
});
|
|
343
|
-
}}
|
|
344
|
-
className={css.width(500)}
|
|
345
|
-
/>
|
|
346
|
-
|
|
347
|
-
<div className={css.vbox(4)}>
|
|
348
|
-
{lifeCycle.entries.map((entry, idx) => (
|
|
349
|
-
<LifeCycleEntryEditor
|
|
350
|
-
key={entry.matchPattern + idx}
|
|
351
|
-
lifeCycle={lifeCycle}
|
|
352
|
-
entry={entry}
|
|
353
|
-
entryIndex={idx}
|
|
354
|
-
defaultEditMode={this.props.defaultEditMode}
|
|
355
|
-
/>
|
|
356
|
-
))}
|
|
357
|
-
|
|
358
|
-
<InputLabel
|
|
359
|
-
placeholder="Add new entry (match pattern)"
|
|
360
|
-
onChangeValue={(value) => {
|
|
361
|
-
let matchPattern = value.trim();
|
|
362
|
-
if (matchPattern) {
|
|
363
|
-
let defaultGroupByKeys = lifeCycle.entries.length > 0 && lifeCycle.entries[0].groupByKeys && deepCloneJSON(lifeCycle.entries[0].groupByKeys) || [];
|
|
364
|
-
let newEntry: LifeCycleEntry = {
|
|
365
|
-
matchPattern,
|
|
366
|
-
sourceType: "info",
|
|
367
|
-
groupByKeys: defaultGroupByKeys,
|
|
368
|
-
variables: {},
|
|
369
|
-
};
|
|
370
|
-
let updatedLifeCycle = deepCloneJSON(lifeCycle);
|
|
371
|
-
let firstEndIndex = updatedLifeCycle.entries.findIndex(e => e.isEnd);
|
|
372
|
-
if (firstEndIndex !== -1) {
|
|
373
|
-
updatedLifeCycle.entries.splice(firstEndIndex, 0, newEntry);
|
|
374
|
-
} else {
|
|
375
|
-
updatedLifeCycle.entries.push(newEntry);
|
|
376
|
-
}
|
|
377
|
-
Querysub.onCommitFinished(async () => {
|
|
378
|
-
await this.controller.setLifeCycle.promise(updatedLifeCycle);
|
|
379
|
-
});
|
|
380
|
-
}
|
|
381
|
-
}}
|
|
382
|
-
className={css.width(500)}
|
|
383
|
-
/>
|
|
384
|
-
</div>
|
|
385
|
-
</div>
|
|
386
|
-
)}
|
|
387
|
-
</div>;
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
class LifeCycleInstanceRenderer extends qreact.Component<{
|
|
393
|
-
lifeCycle: LifeCycle;
|
|
394
|
-
instance: LifecycleInstance;
|
|
395
|
-
}> {
|
|
396
|
-
state = t.state({
|
|
397
|
-
expanded: t.atomic<boolean>(false),
|
|
398
|
-
});
|
|
399
|
-
|
|
400
|
-
render() {
|
|
401
|
-
let { lifeCycle, instance } = this.props;
|
|
402
|
-
|
|
403
|
-
let bgClass = instance.isWarning && css.hsl(30, 60, 90) ||
|
|
404
|
-
instance.isTruncated && css.hsl(40, 50, 92) ||
|
|
405
|
-
instance.isComplete && css.hsl(120, 30, 95) ||
|
|
406
|
-
css.hsl(200, 30, 95);
|
|
407
|
-
|
|
408
|
-
let borderHue = instance.isWarning && 30 || instance.isTruncated && 40 || instance.isComplete && 120 || 200;
|
|
409
|
-
|
|
410
|
-
let firstDatum = instance.entries[0]?.datum;
|
|
411
|
-
|
|
412
|
-
let statusTitle = instance.isWarning && `⚠ ${instance.warningMessage}` ||
|
|
413
|
-
instance.isTruncated && "⚠ Truncated (ended by new start)" ||
|
|
414
|
-
instance.isComplete && "✓ Complete" ||
|
|
415
|
-
"Incomplete";
|
|
416
|
-
|
|
417
|
-
return <div
|
|
418
|
-
className={
|
|
419
|
-
css.pad2(4).bord2(borderHue, 40, 70)
|
|
420
|
-
.vbox(8)
|
|
421
|
-
.button.userSelect("auto", "important")
|
|
422
|
-
+ " LifeCycleInstanceRenderer"
|
|
423
|
-
+ bgClass
|
|
424
|
-
}
|
|
425
|
-
title={statusTitle}
|
|
426
|
-
onClick={(e) => {
|
|
427
|
-
if ((e.target as HTMLElement).closest(".LifeCycleEntryEditor")) {
|
|
428
|
-
return;
|
|
429
|
-
}
|
|
430
|
-
this.state.expanded = !this.state.expanded;
|
|
431
|
-
}}
|
|
432
|
-
>
|
|
433
|
-
<div className={css.hbox(12, 4)}>
|
|
434
|
-
<Button
|
|
435
|
-
hue={borderHue}
|
|
436
|
-
>
|
|
437
|
-
{this.state.expanded && "▼" || "▶"}
|
|
438
|
-
</Button>
|
|
439
|
-
<span className={css.minWidth(200).hbox(12)}>
|
|
440
|
-
<span className={css.colorhsl(220, 60, 50)}>
|
|
441
|
-
{formatDateTime(instance.startTime)}
|
|
442
|
-
</span>
|
|
443
|
-
{instance.endTime !== undefined && (
|
|
444
|
-
<span className={css.colorhsl(220, 60, 50)}>
|
|
445
|
-
({formatTime(instance.endTime - instance.startTime)})
|
|
446
|
-
</span>
|
|
447
|
-
)}
|
|
448
|
-
</span>
|
|
449
|
-
<span>{instance.entries.length} entries</span>
|
|
450
|
-
{firstDatum && firstDatum.__machineId && (
|
|
451
|
-
<MachineThreadInfo
|
|
452
|
-
machineId={firstDatum.__machineId}
|
|
453
|
-
threadId={firstDatum.__threadId}
|
|
454
|
-
/>
|
|
455
|
-
)}
|
|
456
|
-
<div className={css.hbox(8, 4).wrap.flexFillWidth}>
|
|
457
|
-
{instance.keys.filter(k => !["__threadId", "__machineId"].includes(k.key)).map((kv, idx, list) => (
|
|
458
|
-
<div key={idx} className={css.hbox(4)}>
|
|
459
|
-
<span className={css.colorhsl(200, 50, 60).boldStyle}>{kv.key}</span>
|
|
460
|
-
<span className={css.colorhsl(0, 0, 50)} title={String(kv.value)}>
|
|
461
|
-
<StartEllipsis maxWidth={Math.ceil(600 / list.length)}>
|
|
462
|
-
{niceStringify(kv.value)}
|
|
463
|
-
</StartEllipsis>
|
|
464
|
-
</span>
|
|
465
|
-
</div>
|
|
466
|
-
))}
|
|
467
|
-
{(() => {
|
|
468
|
-
let uniqueVars = new Map<string, { key: string; title: string | undefined; value: unknown }>();
|
|
469
|
-
for (let entryData of instance.entries) {
|
|
470
|
-
let entry = lifeCycle.entries.find(e => e.matchPattern === entryData.matchPattern);
|
|
471
|
-
if (!entry) continue;
|
|
472
|
-
for (let [key, config] of Object.entries(entry.variables)) {
|
|
473
|
-
let value = entryData.datum[key];
|
|
474
|
-
if (value === undefined) continue;
|
|
475
|
-
let uniqueId = getPathStr([key, JSON.stringify(value)]);
|
|
476
|
-
if (!uniqueVars.has(uniqueId)) {
|
|
477
|
-
uniqueVars.set(uniqueId, {
|
|
478
|
-
key: key,
|
|
479
|
-
title: config?.title || undefined,
|
|
480
|
-
value: value,
|
|
481
|
-
});
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
return Array.from(uniqueVars.values()).map((varData, idx) => (
|
|
486
|
-
<div key={idx} className={css.hbox(4)}>
|
|
487
|
-
<span className={css.colorhsl(0, 0, 0).boldStyle}>{varData.title || varData.key}</span>
|
|
488
|
-
<span className={css.colorhsl(0, 0, 50)} title={niceStringify(varData.value)}>{formatValue(varData.value)}</span>
|
|
489
|
-
</div>
|
|
490
|
-
));
|
|
491
|
-
})()}
|
|
492
|
-
</div>
|
|
493
|
-
</div>
|
|
494
|
-
|
|
495
|
-
{this.state.expanded && (
|
|
496
|
-
<div className={css.vbox(8)}>
|
|
497
|
-
{instance.entries.map((entryData, idx) => {
|
|
498
|
-
let entryIndex = lifeCycle.entries.findIndex(e => e.matchPattern === entryData.matchPattern);
|
|
499
|
-
let entry = lifeCycle.entries[entryIndex];
|
|
500
|
-
return <LifeCycleEntryEditor
|
|
501
|
-
key={idx}
|
|
502
|
-
lifeCycle={lifeCycle}
|
|
503
|
-
entry={entry}
|
|
504
|
-
entryIndex={entryIndex}
|
|
505
|
-
defaultEditMode={false}
|
|
506
|
-
datum={entryData.datum}
|
|
507
|
-
/>;
|
|
508
|
-
})}
|
|
509
|
-
</div>
|
|
510
|
-
)}
|
|
511
|
-
</div>;
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
class LifeCycleEntryEditor extends qreact.Component<{
|
|
517
|
-
lifeCycle: LifeCycle;
|
|
518
|
-
entry: LifeCycleEntry;
|
|
519
|
-
entryIndex: number;
|
|
520
|
-
defaultEditMode?: boolean;
|
|
521
|
-
datum?: LogDatum;
|
|
522
|
-
}> {
|
|
523
|
-
state = t.state({
|
|
524
|
-
editMode: t.atomic<boolean>(false),
|
|
525
|
-
});
|
|
526
|
-
|
|
527
|
-
controller = LifeCyclesController(SocketFunction.browserNodeId());
|
|
528
|
-
|
|
529
|
-
componentDidMount() {
|
|
530
|
-
this.state.editMode = this.props.defaultEditMode || false;
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
render() {
|
|
534
|
-
let { lifeCycle, entry, entryIndex } = this.props;
|
|
535
|
-
let isConfigured = entry.groupByKeys && entry.groupByKeys.length > 0 && entry.groupByKeys.every(k => k.ourKey.trim() !== "");
|
|
536
|
-
let bgClass = isConfigured && css.hsl(150, 30, 95) || css.hsl(30, 50, 90);
|
|
537
|
-
|
|
538
|
-
return <div className={
|
|
539
|
-
css.pad2(4).bord2(150, 30, 70)
|
|
540
|
-
.vbox(4)
|
|
541
|
-
.cursor("auto")
|
|
542
|
-
+ bgClass
|
|
543
|
-
+ " LifeCycleEntryEditor"
|
|
544
|
-
}>
|
|
545
|
-
<div className={css.hbox(12)}>
|
|
546
|
-
<span className={css.boldStyle}>{entry.description || entry.matchPattern}</span>
|
|
547
|
-
{entry.description && <span className={css.colorhsl(0, 0, 50)}>({entry.matchPattern})</span>}
|
|
548
|
-
<span className={css.colorhsl(220, 70, 50)}>[{entry.sourceType}]</span>
|
|
549
|
-
{entry.isStart && <span className={css.colorhsl(120, 80, 30).boldStyle}>START</span>}
|
|
550
|
-
{entry.isEnd && <span className={css.colorhsl(0, 80, 30).boldStyle}>END</span>}
|
|
551
|
-
{!isConfigured && !this.state.editMode && <span className={css.colorhsl(30, 80, 40).boldStyle}>(not fully configured)</span>}
|
|
552
|
-
<Button
|
|
553
|
-
hue={150}
|
|
554
|
-
onClick={() => {
|
|
555
|
-
this.state.editMode = !this.state.editMode;
|
|
556
|
-
}}
|
|
557
|
-
>
|
|
558
|
-
{this.state.editMode && "Read Mode" || "Edit Mode"}
|
|
559
|
-
</Button>
|
|
560
|
-
<Button
|
|
561
|
-
hue={0}
|
|
562
|
-
onClick={() => {
|
|
563
|
-
if (!confirm("Delete this entry?")) return;
|
|
564
|
-
|
|
565
|
-
let updatedLifeCycle = deepCloneJSON(lifeCycle);
|
|
566
|
-
updatedLifeCycle.entries.splice(entryIndex, 1);
|
|
567
|
-
Querysub.onCommitFinished(async () => {
|
|
568
|
-
await this.controller.setLifeCycle.promise(updatedLifeCycle);
|
|
569
|
-
});
|
|
570
|
-
}}
|
|
571
|
-
>
|
|
572
|
-
Delete
|
|
573
|
-
</Button>
|
|
574
|
-
</div>
|
|
575
|
-
|
|
576
|
-
{this.state.editMode && (
|
|
577
|
-
<div className={css.vbox(12)}>
|
|
578
|
-
<div className={css.hbox(10)}>
|
|
579
|
-
<InputLabel
|
|
580
|
-
label="Match Pattern"
|
|
581
|
-
value={entry.matchPattern}
|
|
582
|
-
onChangeValue={(value) => {
|
|
583
|
-
let matchPattern = value.trim();
|
|
584
|
-
let updatedLifeCycle = deepCloneJSON(lifeCycle);
|
|
585
|
-
updatedLifeCycle.entries[entryIndex].matchPattern = matchPattern;
|
|
586
|
-
Querysub.onCommitFinished(async () => {
|
|
587
|
-
await this.controller.setLifeCycle.promise(updatedLifeCycle);
|
|
588
|
-
});
|
|
589
|
-
}}
|
|
590
|
-
className={css.width(500)}
|
|
591
|
-
/>
|
|
592
|
-
<InputLabel
|
|
593
|
-
label="Description"
|
|
594
|
-
value={entry.description || ""}
|
|
595
|
-
onChangeValue={(value) => {
|
|
596
|
-
let description = value.trim() || undefined;
|
|
597
|
-
let updatedLifeCycle = deepCloneJSON(lifeCycle);
|
|
598
|
-
updatedLifeCycle.entries[entryIndex].description = description;
|
|
599
|
-
Querysub.onCommitFinished(async () => {
|
|
600
|
-
await this.controller.setLifeCycle.promise(updatedLifeCycle);
|
|
601
|
-
});
|
|
602
|
-
}}
|
|
603
|
-
className={css.width(500)}
|
|
604
|
-
/>
|
|
605
|
-
</div>
|
|
606
|
-
|
|
607
|
-
<div className={css.hbox(12)}>
|
|
608
|
-
<div className={css.vbox(4)}>
|
|
609
|
-
<span>Source Type</span>
|
|
610
|
-
<select
|
|
611
|
-
value={entry.sourceType}
|
|
612
|
-
onChange={(e) => {
|
|
613
|
-
let sourceType = e.currentTarget.value as "log" | "error" | "info" | "warning";
|
|
614
|
-
let updatedLifeCycle = deepCloneJSON(lifeCycle);
|
|
615
|
-
updatedLifeCycle.entries[entryIndex].sourceType = sourceType;
|
|
616
|
-
Querysub.onCommitFinished(async () => {
|
|
617
|
-
await this.controller.setLifeCycle.promise(updatedLifeCycle);
|
|
618
|
-
});
|
|
619
|
-
}}
|
|
620
|
-
className={css.pad2(4, 2)}
|
|
621
|
-
>
|
|
622
|
-
<option value="log">log</option>
|
|
623
|
-
<option value="info">info</option>
|
|
624
|
-
<option value="warning">warning</option>
|
|
625
|
-
<option value="error">error</option>
|
|
626
|
-
</select>
|
|
627
|
-
</div>
|
|
628
|
-
</div>
|
|
629
|
-
|
|
630
|
-
<div className={css.hbox(12)}>
|
|
631
|
-
<InputLabel
|
|
632
|
-
label="Is Start"
|
|
633
|
-
checkbox
|
|
634
|
-
checked={entry.isStart}
|
|
635
|
-
onChange={(e) => {
|
|
636
|
-
let isStart = e.currentTarget.checked || undefined;
|
|
637
|
-
let updatedLifeCycle = deepCloneJSON(lifeCycle);
|
|
638
|
-
updatedLifeCycle.entries[entryIndex].isStart = isStart;
|
|
639
|
-
Querysub.onCommitFinished(async () => {
|
|
640
|
-
await this.controller.setLifeCycle.promise(updatedLifeCycle);
|
|
641
|
-
});
|
|
642
|
-
}}
|
|
643
|
-
/>
|
|
644
|
-
<InputLabel
|
|
645
|
-
label="Is End"
|
|
646
|
-
checkbox
|
|
647
|
-
checked={entry.isEnd}
|
|
648
|
-
onChange={(e) => {
|
|
649
|
-
let isEnd = e.currentTarget.checked || undefined;
|
|
650
|
-
let updatedLifeCycle = deepCloneJSON(lifeCycle);
|
|
651
|
-
updatedLifeCycle.entries[entryIndex].isEnd = isEnd;
|
|
652
|
-
Querysub.onCommitFinished(async () => {
|
|
653
|
-
await this.controller.setLifeCycle.promise(updatedLifeCycle);
|
|
654
|
-
});
|
|
655
|
-
}}
|
|
656
|
-
/>
|
|
657
|
-
</div>
|
|
658
|
-
|
|
659
|
-
<div className={css.vbox(8)}>
|
|
660
|
-
<div className={!isConfigured && css.colorhsl(30, 80, 40).boldStyle || ""}>
|
|
661
|
-
Group By Keys{!isConfigured && " (at least one required)" || ""}
|
|
662
|
-
</div>
|
|
663
|
-
{Array.isArray(entry.groupByKeys) && entry.groupByKeys.map((keyConfig, keyIdx) => {
|
|
664
|
-
let keyExistsInAllOtherEntries = lifeCycle.entries.every((e, idx) => {
|
|
665
|
-
if (idx === entryIndex) return true;
|
|
666
|
-
return e.groupByKeys && e.groupByKeys.some(k => k.ourKey === keyConfig.ourKey);
|
|
667
|
-
});
|
|
668
|
-
|
|
669
|
-
return <div key={keyIdx} className={css.hbox(8).pad2(4).bord2(200, 30, 80).hsl(200, 30, 97)}>
|
|
670
|
-
<Button
|
|
671
|
-
hue={160}
|
|
672
|
-
disabled={keyExistsInAllOtherEntries}
|
|
673
|
-
onClick={() => {
|
|
674
|
-
let updatedLifeCycle = deepCloneJSON(lifeCycle);
|
|
675
|
-
updatedLifeCycle.entries.forEach((e, idx) => {
|
|
676
|
-
if (idx === entryIndex) return;
|
|
677
|
-
if (!e.groupByKeys.some(k => k.ourKey === keyConfig.ourKey)) {
|
|
678
|
-
e.groupByKeys.push({ ourKey: keyConfig.ourKey });
|
|
679
|
-
}
|
|
680
|
-
});
|
|
681
|
-
Querysub.onCommitFinished(async () => {
|
|
682
|
-
await this.controller.setLifeCycle.promise(updatedLifeCycle);
|
|
683
|
-
});
|
|
684
|
-
}}
|
|
685
|
-
>
|
|
686
|
-
Copy to All
|
|
687
|
-
</Button>
|
|
688
|
-
<InputLabel
|
|
689
|
-
label="Key"
|
|
690
|
-
value={keyConfig.ourKey}
|
|
691
|
-
onChangeValue={(value) => {
|
|
692
|
-
let ourKey = value.trim();
|
|
693
|
-
let updatedLifeCycle = deepCloneJSON(lifeCycle);
|
|
694
|
-
updatedLifeCycle.entries[entryIndex].groupByKeys[keyIdx].ourKey = ourKey;
|
|
695
|
-
Querysub.onCommitFinished(async () => {
|
|
696
|
-
await this.controller.setLifeCycle.promise(updatedLifeCycle);
|
|
697
|
-
});
|
|
698
|
-
}}
|
|
699
|
-
className={css.width(250)}
|
|
700
|
-
/>
|
|
701
|
-
<InputLabel
|
|
702
|
-
label="Different key in start entry (not usually needed)"
|
|
703
|
-
value={keyConfig.startKey || ""}
|
|
704
|
-
onChangeValue={(value) => {
|
|
705
|
-
let startKey = value.trim() || undefined;
|
|
706
|
-
let updatedLifeCycle = deepCloneJSON(lifeCycle);
|
|
707
|
-
updatedLifeCycle.entries[entryIndex].groupByKeys[keyIdx].startKey = startKey;
|
|
708
|
-
Querysub.onCommitFinished(async () => {
|
|
709
|
-
await this.controller.setLifeCycle.promise(updatedLifeCycle);
|
|
710
|
-
});
|
|
711
|
-
}}
|
|
712
|
-
className={css.width(250)}
|
|
713
|
-
/>
|
|
714
|
-
<Button
|
|
715
|
-
hue={0}
|
|
716
|
-
onClick={() => {
|
|
717
|
-
let updatedLifeCycle = deepCloneJSON(lifeCycle);
|
|
718
|
-
updatedLifeCycle.entries[entryIndex].groupByKeys.splice(keyIdx, 1);
|
|
719
|
-
Querysub.onCommitFinished(async () => {
|
|
720
|
-
await this.controller.setLifeCycle.promise(updatedLifeCycle);
|
|
721
|
-
});
|
|
722
|
-
}}
|
|
723
|
-
>
|
|
724
|
-
Delete
|
|
725
|
-
</Button>
|
|
726
|
-
</div>;
|
|
727
|
-
})}
|
|
728
|
-
<InputLabel
|
|
729
|
-
label="Add new key"
|
|
730
|
-
onChangeValue={(value) => {
|
|
731
|
-
let ourKey = value.trim();
|
|
732
|
-
if (ourKey) {
|
|
733
|
-
let updatedLifeCycle = deepCloneJSON(lifeCycle);
|
|
734
|
-
updatedLifeCycle.entries[entryIndex].groupByKeys.push({ ourKey });
|
|
735
|
-
Querysub.onCommitFinished(async () => {
|
|
736
|
-
await this.controller.setLifeCycle.promise(updatedLifeCycle);
|
|
737
|
-
});
|
|
738
|
-
}
|
|
739
|
-
}}
|
|
740
|
-
className={css.width(250)}
|
|
741
|
-
/>
|
|
742
|
-
</div>
|
|
743
|
-
|
|
744
|
-
<div className={css.vbox(4)}>
|
|
745
|
-
{Object.entries(entry.variables).map(([key, varData]) => {
|
|
746
|
-
let keyExistsInAllOtherEntries = lifeCycle.entries.every((e, idx) => {
|
|
747
|
-
if (idx === entryIndex) return true;
|
|
748
|
-
return key in e.variables;
|
|
749
|
-
});
|
|
750
|
-
|
|
751
|
-
return <div key={key} className={css.hbox(8).pad2(4).bord2(0, 0, 80).hsl(0, 0, 97)}>
|
|
752
|
-
<Button
|
|
753
|
-
hue={160}
|
|
754
|
-
disabled={keyExistsInAllOtherEntries}
|
|
755
|
-
onClick={() => {
|
|
756
|
-
let updatedLifeCycle = deepCloneJSON(lifeCycle);
|
|
757
|
-
updatedLifeCycle.entries.forEach((e, idx) => {
|
|
758
|
-
if (idx === entryIndex) return;
|
|
759
|
-
if (!(key in e.variables)) {
|
|
760
|
-
e.variables[key] = {};
|
|
761
|
-
}
|
|
762
|
-
});
|
|
763
|
-
Querysub.onCommitFinished(async () => {
|
|
764
|
-
await this.controller.setLifeCycle.promise(updatedLifeCycle);
|
|
765
|
-
});
|
|
766
|
-
}}
|
|
767
|
-
>
|
|
768
|
-
Copy to All
|
|
769
|
-
</Button>
|
|
770
|
-
<span className={css.minWidth(150).boldStyle}>{key}</span>
|
|
771
|
-
<InputLabel
|
|
772
|
-
placeholder="Variable title"
|
|
773
|
-
value={varData.title || ""}
|
|
774
|
-
onChangeValue={(value) => {
|
|
775
|
-
let title = value.trim() || undefined;
|
|
776
|
-
let updatedLifeCycle = deepCloneJSON(lifeCycle);
|
|
777
|
-
updatedLifeCycle.entries[entryIndex].variables[key].title = title;
|
|
778
|
-
Querysub.onCommitFinished(async () => {
|
|
779
|
-
await this.controller.setLifeCycle.promise(updatedLifeCycle);
|
|
780
|
-
});
|
|
781
|
-
}}
|
|
782
|
-
className={css.width(300)}
|
|
783
|
-
/>
|
|
784
|
-
<Button
|
|
785
|
-
hue={0}
|
|
786
|
-
onClick={() => {
|
|
787
|
-
let updatedLifeCycle = deepCloneJSON(lifeCycle);
|
|
788
|
-
delete updatedLifeCycle.entries[entryIndex].variables[key];
|
|
789
|
-
Querysub.onCommitFinished(async () => {
|
|
790
|
-
await this.controller.setLifeCycle.promise(updatedLifeCycle);
|
|
791
|
-
});
|
|
792
|
-
}}
|
|
793
|
-
>
|
|
794
|
-
Delete
|
|
795
|
-
</Button>
|
|
796
|
-
</div>;
|
|
797
|
-
})}
|
|
798
|
-
|
|
799
|
-
<InputLabel
|
|
800
|
-
label="Add new variable key"
|
|
801
|
-
onChangeValue={(value) => {
|
|
802
|
-
let key = value.trim();
|
|
803
|
-
if (key) {
|
|
804
|
-
let updatedLifeCycle = deepCloneJSON(lifeCycle);
|
|
805
|
-
updatedLifeCycle.entries[entryIndex].variables[key] = {};
|
|
806
|
-
Querysub.onCommitFinished(async () => {
|
|
807
|
-
await this.controller.setLifeCycle.promise(updatedLifeCycle);
|
|
808
|
-
});
|
|
809
|
-
}
|
|
810
|
-
}}
|
|
811
|
-
className={css.width(300)}
|
|
812
|
-
/>
|
|
813
|
-
</div>
|
|
814
|
-
</div>
|
|
815
|
-
)}
|
|
816
|
-
|
|
817
|
-
{!this.state.editMode && <LifeCycleEntryReadMode
|
|
818
|
-
lifeCycle={lifeCycle}
|
|
819
|
-
entry={entry}
|
|
820
|
-
entryIndex={entryIndex}
|
|
821
|
-
datum={this.props.datum}
|
|
822
|
-
/>}
|
|
823
|
-
</div>;
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
class LifeCycleEntryReadMode extends qreact.Component<{
|
|
828
|
-
lifeCycle: LifeCycle;
|
|
829
|
-
entry: LifeCycleEntry;
|
|
830
|
-
entryIndex: number;
|
|
831
|
-
datum: LogDatum | undefined;
|
|
832
|
-
}> {
|
|
833
|
-
state = t.state({
|
|
834
|
-
showAllVariables: t.atomic<boolean>(false),
|
|
835
|
-
});
|
|
836
|
-
|
|
837
|
-
controller = LifeCyclesController(SocketFunction.browserNodeId());
|
|
838
|
-
|
|
839
|
-
render() {
|
|
840
|
-
let { lifeCycle, entry, entryIndex } = this.props;
|
|
841
|
-
const datum = this.props.datum;
|
|
842
|
-
|
|
843
|
-
let variables = getVariables(entry);
|
|
844
|
-
let renderEditUI = (key: string) => {
|
|
845
|
-
let updateLifeCycle = (fnc: (updatedLifeCycle: LifeCycle) => void) => {
|
|
846
|
-
let updatedLifeCycle = deepCloneJSON(lifeCycle);
|
|
847
|
-
fnc(updatedLifeCycle);
|
|
848
|
-
Querysub.onCommitFinished(async () => {
|
|
849
|
-
await this.controller.setLifeCycle.promise(updatedLifeCycle);
|
|
850
|
-
});
|
|
851
|
-
};
|
|
852
|
-
|
|
853
|
-
let isVariable = key in entry.variables;
|
|
854
|
-
let isGroupByKey = Array.isArray(entry.groupByKeys) && entry.groupByKeys.some(k => k.ourKey === key);
|
|
855
|
-
|
|
856
|
-
return <>
|
|
857
|
-
<span
|
|
858
|
-
className={css.button.pad2(4).bord2(0, 0, 60).hsl(220, 50, 60) + (!isGroupByKey && css.opacity(0.4))}
|
|
859
|
-
onClick={() => {
|
|
860
|
-
updateLifeCycle((updated) => {
|
|
861
|
-
if (isGroupByKey) {
|
|
862
|
-
updated.entries.forEach((e) => {
|
|
863
|
-
e.groupByKeys = e.groupByKeys.filter(k => k.ourKey !== key);
|
|
864
|
-
});
|
|
865
|
-
} else {
|
|
866
|
-
updated.entries.forEach((e) => {
|
|
867
|
-
if (!e.groupByKeys.some(k => k.ourKey === key)) {
|
|
868
|
-
e.groupByKeys.push({ ourKey: key });
|
|
869
|
-
}
|
|
870
|
-
});
|
|
871
|
-
}
|
|
872
|
-
});
|
|
873
|
-
}}
|
|
874
|
-
title={isGroupByKey && "Remove group by key from all entries" || "Add group by key to all entries"}
|
|
875
|
-
>
|
|
876
|
-
🔑
|
|
877
|
-
</span>
|
|
878
|
-
<span
|
|
879
|
-
className={css.button.pad2(4).bord2(0, 0, 60).hsl(120, 50, 60) + (!isVariable && css.opacity(0.4))}
|
|
880
|
-
onClick={() => {
|
|
881
|
-
updateLifeCycle((updated) => {
|
|
882
|
-
if (isVariable) {
|
|
883
|
-
updated.entries.forEach((e) => {
|
|
884
|
-
delete e.variables[key];
|
|
885
|
-
});
|
|
886
|
-
} else {
|
|
887
|
-
updated.entries.forEach((e) => {
|
|
888
|
-
if (!(key in e.variables)) {
|
|
889
|
-
e.variables[key] = {};
|
|
890
|
-
}
|
|
891
|
-
});
|
|
892
|
-
}
|
|
893
|
-
});
|
|
894
|
-
}}
|
|
895
|
-
title={isVariable && "Remove variable from all entries" || "Add variable to all entries"}
|
|
896
|
-
>
|
|
897
|
-
📌
|
|
898
|
-
</span>
|
|
899
|
-
</>;
|
|
900
|
-
};
|
|
901
|
-
|
|
902
|
-
return <div className={css.vbox(8)}>
|
|
903
|
-
{Array.isArray(entry.groupByKeys) && entry.groupByKeys.some(k => k.startKey) && (
|
|
904
|
-
<div className={css.vbox(2)}>
|
|
905
|
-
{entry.groupByKeys.filter(k => k.startKey).map((k, idx) => (
|
|
906
|
-
<div key={idx}>{k.ourKey} start override: {k.startKey}</div>
|
|
907
|
-
))}
|
|
908
|
-
</div>
|
|
909
|
-
)}
|
|
910
|
-
{datum && <div className={css.vbox(4)}>
|
|
911
|
-
{variables.map(({ key, title }) => {
|
|
912
|
-
let value = datum[key];
|
|
913
|
-
return <div key={key} className={css.hbox(8)}>
|
|
914
|
-
{renderEditUI(key)}
|
|
915
|
-
<span className={css.minWidth(150).colorhsl(0, 0, 50)}>
|
|
916
|
-
{title || key}{title && ` (${key})` || ""}:
|
|
917
|
-
</span>
|
|
918
|
-
<span className={css.whiteSpace("pre-wrap")}>{value !== undefined && niceStringify(value) || "(not found)"}</span>
|
|
919
|
-
</div>;
|
|
920
|
-
})}
|
|
921
|
-
{this.state.showAllVariables && (() => {
|
|
922
|
-
let configuredKeys = new Set(getVariables(entry).map(v => v.key));
|
|
923
|
-
let allKeys = Object.keys(datum).filter(key => !configuredKeys.has(key));
|
|
924
|
-
return allKeys.map((key) => {
|
|
925
|
-
let value = datum[key];
|
|
926
|
-
return <div key={key} className={css.hbox(8)}>
|
|
927
|
-
{renderEditUI(key)}
|
|
928
|
-
<span className={css.minWidth(150).colorhsl(0, 0, 50)}>
|
|
929
|
-
{key}
|
|
930
|
-
</span>
|
|
931
|
-
<span className={css.whiteSpace("pre-wrap")}>{value !== undefined && niceStringify(value) || "(not found)"}</span>
|
|
932
|
-
</div>;
|
|
933
|
-
});
|
|
934
|
-
})()}
|
|
935
|
-
<Button
|
|
936
|
-
hue={220}
|
|
937
|
-
onClick={() => {
|
|
938
|
-
this.state.showAllVariables = !this.state.showAllVariables;
|
|
939
|
-
}}
|
|
940
|
-
>
|
|
941
|
-
{this.state.showAllVariables && "Hide Additional Variables" || "Show All Variables"}
|
|
942
|
-
</Button>
|
|
943
|
-
</div>}
|
|
944
|
-
</div>;
|
|
945
|
-
}
|
|
946
|
-
}
|