react-dashstream 0.3.1 → 0.3.2

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.
@@ -0,0 +1,212 @@
1
+ ---
2
+ name: dashstream-event-view
3
+ description: Set up the EventView operations console and event-to-dashboard bridge
4
+ license: MIT
5
+ compatibility: opencode
6
+ metadata:
7
+ audience: developers
8
+ workflow: react
9
+ ---
10
+
11
+ ## What I do
12
+
13
+ - Document `EventView` — the operations event console table component
14
+ - Explain `EventApiConfig`, `EventFieldMapping`, and `AIOpsEvent` types
15
+ - Cover credential resolution, severity mapping, and API polling
16
+ - Document the event-to-dashboard bridge (`eventApiConfig` on `AIOPsDashboard`)
17
+
18
+ ## When to use me
19
+
20
+ Use this when setting up an event console, configuring event API integration, or enabling automatic node alerts from events on the 3D dashboard.
21
+
22
+ ---
23
+
24
+ ## EventView — Operations Event Console
25
+
26
+ Table component for displaying infrastructure events from a live API. Uses the same `access-key` / `access-secret-key` authentication and holographic theme as the 3D dashboard. No built-in default data — requires either `apiConfig` (to fetch from an API) or `events` (pre-fetched array).
27
+
28
+ ### API mode (primary usage)
29
+
30
+ ```tsx
31
+ import "react-dashstream/dist/index.css";
32
+ import { EventView } from "react-dashstream";
33
+ import type { EventApiConfig } from "react-dashstream";
34
+
35
+ const apiConfig: EventApiConfig = {
36
+ baseUrl: "https://your-monitoring-server.example.com",
37
+ payload: { filter: {}, sortBy: "date_reception", sortOrder: "DESC" },
38
+ fieldMapping: {
39
+ id: "mc_ueid",
40
+ occurrence: "date_reception",
41
+ severityLastModified: "severity_last_modified",
42
+ severity: "severity",
43
+ owner: "owner",
44
+ class: "class",
45
+ host: "mc_host",
46
+ message: "msg",
47
+ remedySupportGroup: "ara_remedy_support_group",
48
+ incidentId: "ara_incident_id",
49
+ smsStatus: "ara_sms_status",
50
+ onCallNumber: "ara_on_call_number",
51
+ hostedApplication: "ara_hosted_application",
52
+ monitoringCategory: "ara_hosted_app_monitoring",
53
+ applicationSupportUnit: "ara_application_support_unit",
54
+ },
55
+ };
56
+
57
+ <EventView apiConfig={apiConfig} />
58
+ ```
59
+
60
+ ### How it works
61
+
62
+ 1. Shows credentials modal (or reuses DataProvider credentials if inside `AIOPsDashboard`)
63
+ 2. Sends `POST {baseUrl}/tsws/monitoring/api/v1.0/events/search` with the payload as JSON body
64
+ 3. Auth via headers: `access-key` and `access-secret-key`
65
+ 4. Expects response: `{ eventSeverityCount: {...}, totalCount: N, eventList: [{...}] }`
66
+ 5. Maps each object in `eventList` to `AIOpsEvent` using `fieldMapping`
67
+ 6. Maps severity strings via `severityMap` (default: `CRITICAL→Critical`, `MAJOR→Major`, `MINOR→Minor`)
68
+ 7. Polls on `refreshInterval` (default 60s)
69
+
70
+ ### Props
71
+
72
+ | Prop | Type | Default | Notes |
73
+ | ---- | ---- | ------- | ----- |
74
+ | `apiConfig` | `EventApiConfig` | — | Required for API mode |
75
+ | `events` | `AIOpsEvent[]` | — | Pre-fetched events (skips API calls) |
76
+ | `credentials` | `Credentials` | — | Explicit creds; falls back to DataProvider → modal |
77
+ | `columnWidthsCookie` | `string` | `"ev_col_widths"` | Cookie name for persisting column widths |
78
+ | `title` | `string` | `"Event Console"` | Header title |
79
+ | `theme` | `DashboardTheme` | — | Optional override; else `ThemeProvider` / default |
80
+
81
+ ### Features
82
+
83
+ - Live API polling with configurable interval and manual refresh button
84
+ - Configurable field mapping — any API response shape can be mapped
85
+ - Shared auth — reuses DataProvider credentials when available
86
+ - Severity filter chips with live counts
87
+ - Free-text search across message, host, owner, incident ID
88
+ - Sortable columns (click header: asc → desc → none)
89
+ - Infinite scroll — all matching events in a single scrollable table
90
+ - Resizable columns — drag column edges; widths persist in cookies
91
+ - Loading spinner, error badge, last-refresh timestamp
92
+ - Light / dark table chrome (via `ThemeProvider` or `theme` prop)
93
+
94
+ ---
95
+
96
+ ## EventApiConfig
97
+
98
+ ```ts
99
+ interface EventApiConfig {
100
+ baseUrl: string; // "https://monitoring.example.com"
101
+ endpoint?: string; // default: "/tsws/monitoring/api/v1.0/events/search"
102
+ payload: Record<string, unknown>; // POST body
103
+ fieldMapping: EventFieldMapping; // maps API keys → AIOpsEvent keys
104
+ severityMap?: Record<string, EventSeverity>; // default: { CRITICAL, MAJOR, MINOR }
105
+ refreshInterval?: number; // ms, default 60000
106
+ }
107
+ ```
108
+
109
+ ### EventFieldMapping
110
+
111
+ Maps every `AIOpsEvent` property to the API response field name. All 15 keys required:
112
+
113
+ ```ts
114
+ type EventFieldMapping = Record<keyof AIOpsEvent, string>;
115
+ ```
116
+
117
+ ### AIOpsEvent interface
118
+
119
+ ```ts
120
+ interface AIOpsEvent {
121
+ id: string;
122
+ occurrence: string;
123
+ severityLastModified: string;
124
+ severity: "Minor" | "Major" | "Critical";
125
+ owner: string;
126
+ class: "Self-monitoring" | "SCOM" | "Situation" | "Alarm" | "Event";
127
+ host: string;
128
+ message: string;
129
+ remedySupportGroup: string;
130
+ incidentId: string;
131
+ smsStatus: "" | "SUCCESS" | "FAILED";
132
+ onCallNumber: string;
133
+ hostedApplication: string;
134
+ monitoringCategory: "24/7" | "Office Hours";
135
+ applicationSupportUnit: string;
136
+ }
137
+ ```
138
+
139
+ ---
140
+
141
+ ## Credential resolution order
142
+
143
+ 1. `credentials` prop (if provided)
144
+ 2. `DataProvider` context credentials (when inside `AIOPsDashboard` with `liveData`)
145
+ 3. Built-in credentials modal (same UI as the 3D dashboard)
146
+
147
+ ---
148
+
149
+ ## Severity colors
150
+
151
+ - **Minor** → `#ffb800` (amber)
152
+ - **Major** → `#ff6600` (orange)
153
+ - **Critical** → `#ff2255` (red)
154
+
155
+ ---
156
+
157
+ ## Event-to-dashboard bridge
158
+
159
+ When `eventApiConfig` is passed to `AIOPsDashboard`, events are fetched in the background and nodes whose `name` (case-insensitive) matches an event `host` automatically show alert callouts.
160
+
161
+ ```tsx
162
+ <AIOPsDashboard
163
+ eventApiConfig={eventApiConfig}
164
+ liveData
165
+ dataEndpoint="..."
166
+ dataBindings={dataBindings}
167
+ services={services}
168
+ >
169
+ <MyService name="My Service" />
170
+ </AIOPsDashboard>
171
+ ```
172
+
173
+ ### How it works internally
174
+
175
+ 1. `AIOPsDashboard` wraps children in `EventAlertsProvider` (inside `DataProvider` when `liveData` is set — shares credentials automatically)
176
+ 2. `EventAlertsProvider` fetches events using the same `fetchEvents` + `mapRawEvent` logic as `EventView`
177
+ 3. Events are grouped by **lower-cased hostname** into a `Map<string, EventHostAlert>`
178
+ 4. `ServiceNode` reads from `EventAlertsContext` via `useEventAlertsOptional()`
179
+ 5. If `componentInfo.name.toLowerCase()` matches an entry, event severity is factored into `resolveNodeSeverity` as a **third severity source** (alongside node status and metric breaches)
180
+ 6. Highest severity among all three sources wins
181
+ 7. `NodeCallout` appears automatically with the event message; multi-event hosts show `"N events – message"`
182
+
183
+ ### Severity mapping
184
+
185
+ | Event severity | → ComponentStatus |
186
+ | -------------- | ----------------- |
187
+ | Critical | `"critical"` |
188
+ | Major | `"warning"` |
189
+ | Minor | `"warning"` |
190
+
191
+ **Key requirement:** Node `name` props must match event `host` values from the API (case-insensitive). Example: if the API returns `host: "SAP-PRD-APP01"`, the node must be `<ServerNode name="SAP-PRD-APP01" ... />`.
192
+
193
+ ### Credential resolution order (inside AIOPsDashboard)
194
+
195
+ 1. `DataProvider` context credentials (when `liveData` is enabled — same credentials, one login)
196
+ 2. Built-in `CredentialsModal` (when `liveData` is not enabled)
197
+
198
+ **Opt-in:** Without `eventApiConfig`, no events are fetched — zero overhead.
199
+
200
+ **When generating an `AIOPsDashboard` with event bridge:** Reuse the same `EventApiConfig` object that the user already has for their `EventView`. Just add it as `eventApiConfig` on `AIOPsDashboard`.
201
+
202
+ ---
203
+
204
+ ## Generating an EventApiConfig from the AI
205
+
206
+ When asked to set up EventView for a new API, generate the config like this:
207
+
208
+ 1. Set `baseUrl` to the server's protocol + host + port
209
+ 2. Set `endpoint` only if it differs from the default `/tsws/monitoring/api/v1.0/events/search`
210
+ 3. Build `payload` based on the API's expected POST body schema
211
+ 4. Build `fieldMapping` by asking the user which API field maps to each `AIOpsEvent` property
212
+ 5. Set `severityMap` only if the API uses non-standard severity values
@@ -0,0 +1,250 @@
1
+ ---
2
+ name: dashstream-live-data
3
+ description: Wire live PromQL data into DashStream dashboards via DataProvider and bindings
4
+ license: MIT
5
+ compatibility: opencode
6
+ metadata:
7
+ audience: developers
8
+ workflow: react
9
+ ---
10
+
11
+ ## What I do
12
+
13
+ - Explain the 4 data binding paths: node props, service dialog metrics, component dialog metrics, and hooks
14
+ - Document `DataProvider`, `dataBindings`, `serviceDataBindings`, and data hooks
15
+ - Specify the endpoint contract (method, headers, response format)
16
+ - Show patterns for custom transforms and multi-component services
17
+
18
+ ## When to use me
19
+
20
+ Use this when connecting a DashStream dashboard to a live Prometheus/monitoring endpoint, configuring polling, or accessing data programmatically via hooks.
21
+
22
+ ---
23
+
24
+ ## Endpoint contract
25
+
26
+ | Aspect | Requirement |
27
+ | ------ | ----------- |
28
+ | **Method** | `GET` |
29
+ | **URL** | `<dataEndpoint>?query=<urlEncodedQuery>` |
30
+ | **Headers** | `access-key` and `access-secret-key` |
31
+ | **Response** | Plain text body (trimmed). Default transform parses as number. |
32
+ | **Errors** | Non-2xx → counted as failure per query |
33
+
34
+ For JSON responses, provide a custom `dataTransform`:
35
+
36
+ ```tsx
37
+ dataTransform={(raw) => {
38
+ const parsed = JSON.parse(String(raw));
39
+ return parsed?.data?.result?.[0]?.value?.[1] ?? raw;
40
+ }}
41
+ ```
42
+
43
+ ---
44
+
45
+ ## Data path 1 — dataBindings (live props injected into service components)
46
+
47
+ `dataBindings` maps **service name → prop name → PromQL query**. The dashboard polls each query, applies a transform, and injects the result as a prop on the matched child component.
48
+
49
+ ```ts
50
+ type DataBinding = string | { query: string; transform?: (raw: unknown) => unknown };
51
+ type DataBindings = Record<string, Record<string, DataBinding>>;
52
+ ```
53
+
54
+ - Bare string → uses global `dataTransform` (defaults to numeric parsing)
55
+ - Object `{ query, transform }` → uses per-binding transform
56
+ - Service name key must match child's `name` prop **exactly**
57
+
58
+ ```tsx
59
+ const dataBindings: DataBindings = {
60
+ "My Service": {
61
+ cpuLoad: 'cpu_usage{instance="srv-01"}',
62
+ memLoad: 'memory_usage{instance="srv-01"}',
63
+ status: {
64
+ query: 'cpu_usage{instance="srv-01"}',
65
+ transform: (raw) => {
66
+ const n = Number(raw);
67
+ if (n >= 85) return "critical";
68
+ if (n >= 70) return "warning";
69
+ return "online";
70
+ },
71
+ },
72
+ dbCapacity: 'disk_capacity{instance="db-01"}',
73
+ dbStatus: { query: 'disk_capacity{instance="db-01"}', transform: statusFromValue },
74
+ },
75
+ };
76
+ ```
77
+
78
+ ### Multi-component services
79
+
80
+ When a service has multiple nodes of the same type, use flat prefixed props. Each `DataBinding` maps **one prop → one query** — there is no array or object binding type.
81
+
82
+ ```tsx
83
+ dataBindings={{
84
+ ServiceX: {
85
+ status: { query: 'status{instance="svcx"}', transform: statusFromValue },
86
+ srv1CpuLoad: 'cpu_usage{instance="srvx-01"}',
87
+ srv1MemLoad: 'memory_usage{instance="srvx-01"}',
88
+ srv1Status: { query: 'status{instance="srvx-01"}', transform: statusFromValue },
89
+ srv2CpuLoad: 'cpu_usage{instance="srvx-02"}',
90
+ srv2MemLoad: 'memory_usage{instance="srvx-02"}',
91
+ srv2Status: { query: 'status{instance="srvx-02"}', transform: statusFromValue },
92
+ dbCapacity: 'disk_capacity{instance="dbx-01"}',
93
+ dbStatus: { query: 'status{instance="dbx-01"}', transform: statusFromValue },
94
+ },
95
+ }}
96
+ ```
97
+
98
+ ---
99
+
100
+ ## Data path 2 — serviceDataBindings (live ServiceDialog metrics)
101
+
102
+ Replace static `ServiceMeta.metrics` with live-fetched values:
103
+
104
+ ```tsx
105
+ serviceDataBindings={{
106
+ "My Service": [
107
+ { label: "CPU Load", query: 'cpu_usage{instance="srv-01"}', unit: "%", color: "#00e5ff" },
108
+ { label: "Memory", query: 'memory_usage{instance="srv-01"}', unit: "%", color: "#bb55ff" },
109
+ { label: "Disk", query: 'disk_capacity{instance="db-01"}', unit: "%", color: "#ff8c00" },
110
+ ],
111
+ }}
112
+ ```
113
+
114
+ ```ts
115
+ interface ServiceMetricBinding {
116
+ label: string; // Row label
117
+ query: string; // PromQL query
118
+ unit?: string; // Suffix (e.g. "%", "ms")
119
+ color?: string; // Accent color
120
+ transform?: (raw: unknown) => string; // Custom formatter
121
+ }
122
+ ```
123
+
124
+ When provided, live values **replace** static `ServiceMeta.metrics` for that service.
125
+
126
+ ---
127
+
128
+ ## Data path 3 — live component dialog metrics
129
+
130
+ To feed live query values into custom gauges, expose each metric as a prop on your service component. Use `dataBindings` to inject them. Then build the `dialogMetrics` array from those props:
131
+
132
+ ```tsx
133
+ function MyService({ name, status = "online", cpuLoad = 42, memLoad = 60, iops = 20 }: any) {
134
+ return (
135
+ <Service name={name} status={status} connections={[/* ... */]}>
136
+ <ServerNode
137
+ name="SRV-01"
138
+ status={status}
139
+ cpuLoad={cpuLoad}
140
+ memLoad={memLoad}
141
+ ex={200} ey={380}
142
+ compactOffset={{ x: -30, y: -20 }}
143
+ zIndex={8}
144
+ dialogMetrics={[
145
+ { id: "cpu", label: "CPU", sublabel: "PROCESSOR", value: cpuLoad },
146
+ { id: "mem", label: "MEMORY", sublabel: "HEAP", value: memLoad },
147
+ { id: "iops", label: "IOPS", sublabel: "DISK OPS", value: iops, icon: "disk", warnAt: 50, critAt: 80 },
148
+ ]}
149
+ />
150
+ </Service>
151
+ );
152
+ }
153
+
154
+ <AIOPsDashboard
155
+ liveData
156
+ dataEndpoint="..."
157
+ dataBindings={{
158
+ "My Service": {
159
+ cpuLoad: 'cpu_usage{instance="srv-01"}',
160
+ memLoad: 'memory_usage{instance="srv-01"}',
161
+ iops: 'disk_iops{instance="srv-01"}',
162
+ status: { query: 'cpu_usage{instance="srv-01"}', transform: statusFromValue },
163
+ },
164
+ }}
165
+ >
166
+ <MyService name="My Service" />
167
+ </AIOPsDashboard>
168
+ ```
169
+
170
+ The dashboard injects `cpuLoad`, `memLoad`, `iops` as live props → they flow into `dialogMetrics` → gauges update automatically on every poll.
171
+
172
+ ---
173
+
174
+ ## Data path 4 — data hooks (programmatic access)
175
+
176
+ Access live data context anywhere inside the dashboard:
177
+
178
+ ```tsx
179
+ import { useAIOpsData, useAIOpsDataOptional, useQueryResult } from "react-dashstream";
180
+
181
+ function CustomWidget() {
182
+ const { data, isRefreshing, lastRefreshError, lastRefreshTime, credentialsSet } = useAIOpsData();
183
+ const cpu = useQueryResult('cpu_usage{instance="srv-01"}');
184
+ return <div>CPU: {String(cpu)}</div>;
185
+ }
186
+ ```
187
+
188
+ | Hook | Returns | Throws? |
189
+ | ---- | ------- | ------- |
190
+ | `useAIOpsData()` | `DataContextValue` | Yes — outside `DataProvider` |
191
+ | `useAIOpsDataOptional()` | `DataContextValue \| null` | No |
192
+ | `useQueryResult(query)` | `unknown \| null` | Yes — outside `DataProvider` |
193
+
194
+ ```ts
195
+ interface DataContextValue {
196
+ data: Record<string, unknown>; // Raw responses keyed by query
197
+ isRefreshing: boolean;
198
+ lastRefreshError: string | null;
199
+ lastRefreshTime: Date | null;
200
+ credentialsSet: boolean;
201
+ setCredentials: (creds: Credentials) => void;
202
+ }
203
+ ```
204
+
205
+ ---
206
+
207
+ ## Standalone DataProvider
208
+
209
+ Use the data layer without `AIOPsDashboard`:
210
+
211
+ ```tsx
212
+ import { DataProvider, useAIOpsData } from "react-dashstream";
213
+
214
+ <DataProvider
215
+ config={{
216
+ endpoint: "https://prometheus.example.com/api/v1/query",
217
+ queries: ['cpu_usage{instance="srv-01"}'],
218
+ refreshInterval: 15000,
219
+ }}
220
+ >
221
+ <MyContent />
222
+ </DataProvider>
223
+ ```
224
+
225
+ ---
226
+
227
+ ## Full live-data dashboard example
228
+
229
+ ```tsx
230
+ <AIOPsDashboard
231
+ brandName="LIVE MONITOR"
232
+ services={services}
233
+ liveData={true}
234
+ dataEndpoint="https://prometheus.example.com/api/v1/query"
235
+ dataRefreshInterval={10000}
236
+ dataBindings={{
237
+ "My Service": {
238
+ cpuLoad: 'cpu_usage{instance="srv-01"}',
239
+ status: { query: 'cpu_usage{instance="srv-01"}', transform: statusFromValue },
240
+ },
241
+ }}
242
+ serviceDataBindings={{
243
+ "My Service": [
244
+ { label: "CPU", query: 'cpu_usage{instance="srv-01"}', unit: "%", color: "#00e5ff" },
245
+ ],
246
+ }}
247
+ >
248
+ <MyService name="My Service" />
249
+ </AIOPsDashboard>
250
+ ```