@checkstack/anomaly-frontend 0.5.5 → 0.5.7
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/CHANGELOG.md +37 -0
- package/package.json +4 -4
- package/src/components/AnomalySignalsFiller.tsx +28 -41
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,42 @@
|
|
|
1
1
|
# @checkstack/anomaly-frontend
|
|
2
2
|
|
|
3
|
+
## 0.5.7
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 0b6f01b: feat(anomaly): contribute anomaly signals to the backend system.issues aggregator
|
|
8
|
+
|
|
9
|
+
The anomaly plugin now registers a `system.issues` contributor (sourceId
|
|
10
|
+
`anomaly`) from its backend `init`, so the AI assistant surfaces confirmed
|
|
11
|
+
anomalies and suspicious states alongside incidents, SLOs, health checks, and
|
|
12
|
+
dependency problems.
|
|
13
|
+
|
|
14
|
+
The contributor enforces its own `anomaly_feed.read` access gate (returning an
|
|
15
|
+
empty map - never throwing - when the principal lacks access; service users are
|
|
16
|
+
trusted), then reads the current problem rows for every system from the shared,
|
|
17
|
+
durable `anomalies` table via a new global `getActiveSignalAnomalies` service
|
|
18
|
+
method (state = anomaly | suspicious, suppressed rows excluded). The answer is
|
|
19
|
+
therefore identical on every pod, and only systems with a current problem appear
|
|
20
|
+
in the result.
|
|
21
|
+
|
|
22
|
+
The row->signal mapping (source/tone/label/detail/href/accessRule/iconName) is
|
|
23
|
+
extracted into a new pure `deriveAnomalySignals` deriver in
|
|
24
|
+
`@checkstack/anomaly-common`, shared by both the backend contributor and the
|
|
25
|
+
frontend `AnomalySignalsFiller` so the two surfaces stay in lockstep. The
|
|
26
|
+
frontend filler now delegates to that deriver with unchanged behavior.
|
|
27
|
+
|
|
28
|
+
- Updated dependencies [0b6f01b]
|
|
29
|
+
- Updated dependencies [0b6f01b]
|
|
30
|
+
- @checkstack/anomaly-common@1.4.0
|
|
31
|
+
- @checkstack/healthcheck-common@1.6.0
|
|
32
|
+
- @checkstack/healthcheck-frontend@0.23.7
|
|
33
|
+
|
|
34
|
+
## 0.5.6
|
|
35
|
+
|
|
36
|
+
### Patch Changes
|
|
37
|
+
|
|
38
|
+
- @checkstack/healthcheck-frontend@0.23.6
|
|
39
|
+
|
|
3
40
|
## 0.5.5
|
|
4
41
|
|
|
5
42
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@checkstack/anomaly-frontend",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.7",
|
|
4
4
|
"license": "Elastic-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.tsx",
|
|
@@ -13,12 +13,12 @@
|
|
|
13
13
|
"lint:code": "eslint . --max-warnings 0"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@checkstack/anomaly-common": "1.
|
|
16
|
+
"@checkstack/anomaly-common": "1.4.0",
|
|
17
17
|
"@checkstack/catalog-common": "2.3.4",
|
|
18
18
|
"@checkstack/common": "0.15.0",
|
|
19
19
|
"@checkstack/frontend-api": "0.9.0",
|
|
20
|
-
"@checkstack/healthcheck-common": "1.
|
|
21
|
-
"@checkstack/healthcheck-frontend": "0.23.
|
|
20
|
+
"@checkstack/healthcheck-common": "1.6.0",
|
|
21
|
+
"@checkstack/healthcheck-frontend": "0.23.7",
|
|
22
22
|
"@checkstack/notification-common": "1.3.3",
|
|
23
23
|
"@checkstack/notification-frontend": "0.5.5",
|
|
24
24
|
"@checkstack/signal-frontend": "0.2.4",
|
|
@@ -3,10 +3,14 @@ import { usePluginClient, type SlotContext } from "@checkstack/frontend-api";
|
|
|
3
3
|
import { resolveRoute } from "@checkstack/common";
|
|
4
4
|
import {
|
|
5
5
|
SystemSignalsSlot,
|
|
6
|
-
type SystemSignal,
|
|
7
6
|
type SystemSignalsMap,
|
|
8
7
|
} from "@checkstack/catalog-common";
|
|
9
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
AnomalyApi,
|
|
10
|
+
deriveAnomalySignals,
|
|
11
|
+
ANOMALY_SIGNAL_SOURCE_ID,
|
|
12
|
+
type AnomalySignalRow,
|
|
13
|
+
} from "@checkstack/anomaly-common";
|
|
10
14
|
import {
|
|
11
15
|
healthcheckRoutes,
|
|
12
16
|
healthCheckAccess,
|
|
@@ -14,14 +18,12 @@ import {
|
|
|
14
18
|
|
|
15
19
|
type Props = SlotContext<typeof SystemSignalsSlot>;
|
|
16
20
|
|
|
17
|
-
const SOURCE_ID = "anomaly";
|
|
18
|
-
|
|
19
21
|
/**
|
|
20
22
|
* Reports confirmed anomalies and suspicious states as dashboard signals.
|
|
21
23
|
* Reuses the two globally-deduped anomaly queries (the same ones the badge
|
|
22
|
-
* uses) and
|
|
23
|
-
*
|
|
24
|
-
* {@link SystemSignalsSlot}.
|
|
24
|
+
* uses) and the shared {@link deriveAnomalySignals} mapper - the SAME mapper the
|
|
25
|
+
* backend `system.issues` contributor runs - so frontend and backend signals
|
|
26
|
+
* stay identical. Headless filler for {@link SystemSignalsSlot}.
|
|
25
27
|
*/
|
|
26
28
|
export const AnomalySignalsFiller: React.FC<Props> = ({
|
|
27
29
|
systemIds,
|
|
@@ -39,46 +41,31 @@ export const AnomalySignalsFiller: React.FC<Props> = ({
|
|
|
39
41
|
);
|
|
40
42
|
|
|
41
43
|
const signals = useMemo<SystemSignalsMap>(() => {
|
|
42
|
-
const result: SystemSignalsMap = {};
|
|
43
44
|
const inOverview = new Set(systemIds);
|
|
45
|
+
const rows: AnomalySignalRow[] = [...confirmed, ...suspicious]
|
|
46
|
+
.filter((a) => inOverview.has(a.systemId))
|
|
47
|
+
.map((a) => ({
|
|
48
|
+
systemId: a.systemId,
|
|
49
|
+
configurationId: a.configurationId,
|
|
50
|
+
fieldPath: a.fieldPath,
|
|
51
|
+
startedAt: a.startedAt,
|
|
52
|
+
state: a.state,
|
|
53
|
+
}));
|
|
44
54
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
label: string,
|
|
52
|
-
) => {
|
|
53
|
-
if (!inOverview.has(systemId)) return;
|
|
54
|
-
const signal: SystemSignal = {
|
|
55
|
-
source: SOURCE_ID,
|
|
56
|
-
tone,
|
|
57
|
-
label,
|
|
58
|
-
detail: fieldPath,
|
|
59
|
-
href: resolveRoute(healthcheckRoutes.routes.historyDetail, {
|
|
60
|
-
systemId,
|
|
61
|
-
configurationId,
|
|
55
|
+
return deriveAnomalySignals({
|
|
56
|
+
rows,
|
|
57
|
+
buildHref: (row) =>
|
|
58
|
+
resolveRoute(healthcheckRoutes.routes.historyDetail, {
|
|
59
|
+
systemId: row.systemId,
|
|
60
|
+
configurationId: row.configurationId,
|
|
62
61
|
}),
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
iconName: "ChartSpline",
|
|
67
|
-
};
|
|
68
|
-
(result[systemId] ??= []).push(signal);
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
for (const a of confirmed) {
|
|
72
|
-
add(a.systemId, a.configurationId, a.fieldPath, a.startedAt, "warn", "Anomaly detected");
|
|
73
|
-
}
|
|
74
|
-
for (const a of suspicious) {
|
|
75
|
-
add(a.systemId, a.configurationId, a.fieldPath, a.startedAt, "info", "Suspicious behaviour");
|
|
76
|
-
}
|
|
77
|
-
return result;
|
|
62
|
+
// The history detail page is gated; render as text for users without it.
|
|
63
|
+
accessRule: healthCheckAccess.details,
|
|
64
|
+
});
|
|
78
65
|
}, [confirmed, suspicious, systemIds]);
|
|
79
66
|
|
|
80
67
|
useEffect(() => {
|
|
81
|
-
onSignals(
|
|
68
|
+
onSignals(ANOMALY_SIGNAL_SOURCE_ID, signals);
|
|
82
69
|
}, [signals, onSignals]);
|
|
83
70
|
|
|
84
71
|
return null;
|