@oneuptime/common 10.0.86 → 10.0.89
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/Models/DatabaseModels/EnterpriseLicense.ts +54 -0
- package/Models/DatabaseModels/GlobalConfig.ts +51 -0
- package/Server/API/EnterpriseLicenseAPI.ts +83 -0
- package/Server/API/GlobalConfigAPI.ts +59 -0
- package/Server/API/MetricAPI.ts +149 -0
- package/Server/API/TelemetryAPI.ts +24 -0
- package/Server/EnvironmentConfig.ts +10 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1777629313843-MigrationName.ts +59 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
- package/Server/Infrastructure/Queue.ts +4 -4
- package/Server/Services/AnalyticsDatabaseService.ts +21 -0
- package/Server/Services/MetricService.ts +193 -1
- package/Server/Services/TelemetryAttributeService.ts +37 -3
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +174 -7
- package/Tests/Types/Date.test.ts +46 -0
- package/Types/Dashboard/DashboardComponentType.ts +3 -0
- package/Types/Dashboard/DashboardComponents/DashboardAlertListComponent.ts +13 -0
- package/Types/Dashboard/DashboardComponents/DashboardIncidentListComponent.ts +13 -0
- package/Types/Dashboard/DashboardComponents/DashboardMonitorListComponent.ts +13 -0
- package/Types/Date.ts +9 -4
- package/Types/JSONFunctions.ts +61 -1
- package/UI/Components/AutocompleteTextInput/AutocompleteTextInput.tsx +60 -21
- package/UI/Components/Dictionary/Dictionary.tsx +188 -26
- package/UI/Components/Dictionary/DictionaryFilterOperator.ts +357 -0
- package/UI/Components/Dictionary/DictionaryOfStrings.tsx +12 -7
- package/UI/Components/EditionLabel/EditionLabel.tsx +224 -10
- package/UI/Components/Filters/FilterViewer.tsx +81 -16
- package/UI/Components/Filters/FiltersForm.tsx +18 -3
- package/UI/Components/Filters/JSONFilter.tsx +11 -2
- package/UI/Components/Filters/Types/Filter.ts +3 -0
- package/UI/Components/Forms/Fields/FormField.tsx +6 -1
- package/UI/Components/Forms/Types/Field.ts +5 -0
- package/UI/Components/LogsViewer/LogsViewer.tsx +73 -4
- package/UI/Components/LogsViewer/components/LogSearchBar.tsx +77 -31
- package/UI/Components/LogsViewer/components/LogSearchSuggestions.tsx +44 -1
- package/UI/Components/LogsViewer/components/LogsFilterCard.tsx +7 -5
- package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +6 -0
- package/UI/Components/TelemetryViewer/components/TelemetrySearchBar.tsx +84 -25
- package/UI/Components/TelemetryViewer/components/TelemetrySearchSuggestions.tsx +44 -1
- package/Utils/Dashboard/Components/DashboardAlertListComponent.ts +86 -0
- package/Utils/Dashboard/Components/DashboardIncidentListComponent.ts +86 -0
- package/Utils/Dashboard/Components/DashboardMonitorListComponent.ts +85 -0
- package/Utils/Dashboard/Components/Index.ts +21 -0
- package/build/dist/Models/DatabaseModels/EnterpriseLicense.js +57 -0
- package/build/dist/Models/DatabaseModels/EnterpriseLicense.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalConfig.js +54 -0
- package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
- package/build/dist/Server/API/EnterpriseLicenseAPI.js +64 -1
- package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +1 -1
- package/build/dist/Server/API/GlobalConfigAPI.js +47 -0
- package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
- package/build/dist/Server/API/MetricAPI.js +123 -0
- package/build/dist/Server/API/MetricAPI.js.map +1 -0
- package/build/dist/Server/API/TelemetryAPI.js +9 -0
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +3 -0
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1777629313843-MigrationName.js +26 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1777629313843-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Infrastructure/Queue.js +3 -3
- package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js +18 -0
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/MetricService.js +151 -1
- package/build/dist/Server/Services/MetricService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryAttributeService.js +36 -7
- package/build/dist/Server/Services/TelemetryAttributeService.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +135 -5
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
- package/build/dist/Tests/Types/Date.test.js +40 -0
- package/build/dist/Tests/Types/Date.test.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponentType.js +3 -0
- package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardAlertListComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardAlertListComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardIncidentListComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardIncidentListComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardMonitorListComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardMonitorListComponent.js.map +1 -0
- package/build/dist/Types/Date.js +7 -2
- package/build/dist/Types/Date.js.map +1 -1
- package/build/dist/Types/JSONFunctions.js +47 -1
- package/build/dist/Types/JSONFunctions.js.map +1 -1
- package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js +21 -10
- package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js.map +1 -1
- package/build/dist/UI/Components/Dictionary/Dictionary.js +109 -16
- package/build/dist/UI/Components/Dictionary/Dictionary.js.map +1 -1
- package/build/dist/UI/Components/Dictionary/DictionaryFilterOperator.js +263 -0
- package/build/dist/UI/Components/Dictionary/DictionaryFilterOperator.js.map +1 -0
- package/build/dist/UI/Components/Dictionary/DictionaryOfStrings.js +10 -6
- package/build/dist/UI/Components/Dictionary/DictionaryOfStrings.js.map +1 -1
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js +124 -6
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
- package/build/dist/UI/Components/Filters/FilterViewer.js +50 -12
- package/build/dist/UI/Components/Filters/FilterViewer.js.map +1 -1
- package/build/dist/UI/Components/Filters/FiltersForm.js +5 -4
- package/build/dist/UI/Components/Filters/FiltersForm.js.map +1 -1
- package/build/dist/UI/Components/Filters/JSONFilter.js +1 -1
- package/build/dist/UI/Components/Filters/JSONFilter.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +54 -5
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogSearchBar.js +59 -29
- package/build/dist/UI/Components/LogsViewer/components/LogSearchBar.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogSearchSuggestions.js +10 -2
- package/build/dist/UI/Components/LogsViewer/components/LogSearchSuggestions.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsFilterCard.js +2 -5
- package/build/dist/UI/Components/LogsViewer/components/LogsFilterCard.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +1 -1
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchBar.js +59 -22
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchBar.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchSuggestions.js +10 -2
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchSuggestions.js.map +1 -1
- package/build/dist/Utils/Dashboard/Components/DashboardAlertListComponent.js +70 -0
- package/build/dist/Utils/Dashboard/Components/DashboardAlertListComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardIncidentListComponent.js +70 -0
- package/build/dist/Utils/Dashboard/Components/DashboardIncidentListComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardMonitorListComponent.js +69 -0
- package/build/dist/Utils/Dashboard/Components/DashboardMonitorListComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/Index.js +12 -0
- package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
- package/package.json +1 -1
|
@@ -9,6 +9,8 @@ export interface LogsFilterCardProps {
|
|
|
9
9
|
onSearchSubmit: () => void;
|
|
10
10
|
valueSuggestions?: Record<string, Array<string>> | undefined;
|
|
11
11
|
onFieldValueSelect?: ((fieldKey: string, value: string) => void) | undefined;
|
|
12
|
+
isAttributesLoading?: boolean | undefined;
|
|
13
|
+
isValuesLoading?: boolean | undefined;
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
const LogsFilterCard: React.ForwardRefExoticComponent<
|
|
@@ -18,14 +20,11 @@ const LogsFilterCard: React.ForwardRefExoticComponent<
|
|
|
18
20
|
props: LogsFilterCardProps,
|
|
19
21
|
ref: React.Ref<LogSearchBarRef>,
|
|
20
22
|
): ReactElement => {
|
|
21
|
-
const
|
|
23
|
+
const fieldSuggestions: Array<string> = [
|
|
22
24
|
"severity",
|
|
23
25
|
"service",
|
|
24
26
|
"trace",
|
|
25
27
|
"span",
|
|
26
|
-
...props.logAttributes.map((attr: string) => {
|
|
27
|
-
return `@${attr}`;
|
|
28
|
-
}),
|
|
29
28
|
];
|
|
30
29
|
|
|
31
30
|
return (
|
|
@@ -36,9 +35,12 @@ const LogsFilterCard: React.ForwardRefExoticComponent<
|
|
|
36
35
|
value={props.searchQuery}
|
|
37
36
|
onChange={props.onSearchQueryChange}
|
|
38
37
|
onSubmit={props.onSearchSubmit}
|
|
39
|
-
suggestions={
|
|
38
|
+
suggestions={fieldSuggestions}
|
|
39
|
+
attributeSuggestions={props.logAttributes}
|
|
40
40
|
valueSuggestions={props.valueSuggestions}
|
|
41
41
|
onFieldValueSelect={props.onFieldValueSelect}
|
|
42
|
+
isAttributesLoading={props.isAttributesLoading}
|
|
43
|
+
isValuesLoading={props.isValuesLoading}
|
|
42
44
|
/>
|
|
43
45
|
</div>
|
|
44
46
|
<div>{props.toolbar}</div>
|
|
@@ -42,6 +42,7 @@ export interface TelemetryViewerProps<T> {
|
|
|
42
42
|
onSearchSubmit: () => void;
|
|
43
43
|
searchPlaceholder?: string | undefined;
|
|
44
44
|
searchSuggestions?: Array<string> | undefined;
|
|
45
|
+
searchAttributeSuggestions?: Array<string> | undefined;
|
|
45
46
|
searchValueSuggestions?: Record<string, Array<string>> | undefined;
|
|
46
47
|
searchFieldAliasMap?: Record<string, string> | undefined;
|
|
47
48
|
onSearchFieldValueSelect?:
|
|
@@ -50,6 +51,8 @@ export interface TelemetryViewerProps<T> {
|
|
|
50
51
|
searchHelpRows?: Array<SearchHelpRow> | undefined;
|
|
51
52
|
searchHelpCombinedExample?: string | undefined;
|
|
52
53
|
searchBarRef?: React.Ref<TelemetrySearchBarRef> | undefined;
|
|
54
|
+
searchAttributesLoading?: boolean | undefined;
|
|
55
|
+
searchValuesLoading?: boolean | undefined;
|
|
53
56
|
|
|
54
57
|
// -- Toolbar: time --
|
|
55
58
|
timeRange: RangeStartAndEndDateTime;
|
|
@@ -118,11 +121,14 @@ function TelemetryViewerInner<T>(props: TelemetryViewerProps<T>): ReactElement {
|
|
|
118
121
|
onSubmit={props.onSearchSubmit}
|
|
119
122
|
placeholder={props.searchPlaceholder}
|
|
120
123
|
suggestions={props.searchSuggestions}
|
|
124
|
+
attributeSuggestions={props.searchAttributeSuggestions}
|
|
121
125
|
valueSuggestions={props.searchValueSuggestions}
|
|
122
126
|
fieldAliasMap={props.searchFieldAliasMap}
|
|
123
127
|
onFieldValueSelect={props.onSearchFieldValueSelect}
|
|
124
128
|
helpRows={props.searchHelpRows}
|
|
125
129
|
helpCombinedExample={props.searchHelpCombinedExample}
|
|
130
|
+
isAttributesLoading={props.searchAttributesLoading}
|
|
131
|
+
isValuesLoading={props.searchValuesLoading}
|
|
126
132
|
/>
|
|
127
133
|
</div>
|
|
128
134
|
|
|
@@ -18,8 +18,17 @@ export interface TelemetrySearchBarProps {
|
|
|
18
18
|
value: string;
|
|
19
19
|
onChange: (value: string) => void;
|
|
20
20
|
onSubmit: () => void;
|
|
21
|
-
|
|
21
|
+
/*
|
|
22
|
+
* Top-level field names like "service", "name" — used as `field:value`
|
|
23
|
+
* (no @). Shown when the user types regular text.
|
|
24
|
+
*/
|
|
22
25
|
suggestions?: Array<string> | undefined;
|
|
26
|
+
/*
|
|
27
|
+
* Telemetry attribute keys like "host.name", "container.id" — used as
|
|
28
|
+
* `@attr:value`. Shown when the user types `@`. Pass plain keys, not
|
|
29
|
+
* pre-prefixed with `@` — the bar adds it on submit.
|
|
30
|
+
*/
|
|
31
|
+
attributeSuggestions?: Array<string> | undefined;
|
|
23
32
|
// field → allowed value completions (resolved field keys).
|
|
24
33
|
valueSuggestions?: Record<string, Array<string>> | undefined;
|
|
25
34
|
// Called when the user picks a concrete field:value chip from the dropdown.
|
|
@@ -30,6 +39,10 @@ export interface TelemetrySearchBarProps {
|
|
|
30
39
|
// Rows rendered in the help popover when the bar is empty + focused.
|
|
31
40
|
helpRows?: Array<SearchHelpRow> | undefined;
|
|
32
41
|
helpCombinedExample?: string | undefined;
|
|
42
|
+
// Loading state for `@attribute` autocomplete (initial fetch of keys).
|
|
43
|
+
isAttributesLoading?: boolean | undefined;
|
|
44
|
+
// Loading state for `@attribute:value` autocomplete (per-key value fetch).
|
|
45
|
+
isValuesLoading?: boolean | undefined;
|
|
33
46
|
}
|
|
34
47
|
|
|
35
48
|
export interface TelemetrySearchBarRef {
|
|
@@ -79,6 +92,17 @@ const TelemetrySearchBar: React.ForwardRefExoticComponent<
|
|
|
79
92
|
? normalizedWord.substring(colonIndex + 1)
|
|
80
93
|
: "";
|
|
81
94
|
|
|
95
|
+
/*
|
|
96
|
+
* Pick the suggestion list based on whether the user typed `@`.
|
|
97
|
+
* `@` is the explicit trigger for attribute mode — only show attribute
|
|
98
|
+
* keys there. Without `@`, only show top-level field names. Mixing the
|
|
99
|
+
* two led to confusing dropdowns that rendered field names like "name"
|
|
100
|
+
* as if they were attributes.
|
|
101
|
+
*/
|
|
102
|
+
const activeSuggestions: Array<string> = hasAtPrefix
|
|
103
|
+
? props.attributeSuggestions || []
|
|
104
|
+
: props.suggestions || [];
|
|
105
|
+
|
|
82
106
|
const filteredSuggestions: Array<string> = isValueMode
|
|
83
107
|
? getValueSuggestions(
|
|
84
108
|
fieldPrefix,
|
|
@@ -86,23 +110,32 @@ const TelemetrySearchBar: React.ForwardRefExoticComponent<
|
|
|
86
110
|
props.valueSuggestions || {},
|
|
87
111
|
fieldAliasMap,
|
|
88
112
|
)
|
|
89
|
-
:
|
|
113
|
+
: activeSuggestions.filter((s: string): boolean => {
|
|
90
114
|
if (!normalizedWord && !hasAtPrefix) {
|
|
91
115
|
return false;
|
|
92
116
|
}
|
|
93
117
|
if (hasAtPrefix && normalizedWord.length === 0) {
|
|
94
118
|
return true;
|
|
95
119
|
}
|
|
96
|
-
|
|
97
|
-
? s.substring(1).toLowerCase()
|
|
98
|
-
: s.toLowerCase();
|
|
99
|
-
return normalizedSuggestion.startsWith(normalizedWord.toLowerCase());
|
|
120
|
+
return s.toLowerCase().startsWith(normalizedWord.toLowerCase());
|
|
100
121
|
});
|
|
101
122
|
|
|
123
|
+
/*
|
|
124
|
+
* Show a loader inside the dropdown while the parent is fetching:
|
|
125
|
+
* - attribute keys: `@` was just typed but the keys haven't arrived
|
|
126
|
+
* - attribute values: `@key:` was typed but values for that key
|
|
127
|
+
* haven't arrived yet
|
|
128
|
+
*/
|
|
129
|
+
const isLoadingForCurrentMode: boolean = isValueMode
|
|
130
|
+
? Boolean(props.isValuesLoading)
|
|
131
|
+
: hasAtPrefix
|
|
132
|
+
? Boolean(props.isAttributesLoading)
|
|
133
|
+
: false;
|
|
134
|
+
|
|
102
135
|
const shouldShowSuggestions: boolean =
|
|
103
136
|
showSuggestions &&
|
|
104
137
|
isFocused &&
|
|
105
|
-
filteredSuggestions.length > 0 &&
|
|
138
|
+
(filteredSuggestions.length > 0 || isLoadingForCurrentMode) &&
|
|
106
139
|
(isValueMode ? true : currentWord.length > 0);
|
|
107
140
|
|
|
108
141
|
const shouldShowHelp: boolean =
|
|
@@ -123,6 +156,7 @@ const TelemetrySearchBar: React.ForwardRefExoticComponent<
|
|
|
123
156
|
if (e.key === "Enter") {
|
|
124
157
|
if (
|
|
125
158
|
shouldShowSuggestions &&
|
|
159
|
+
!isLoadingForCurrentMode &&
|
|
126
160
|
selectedSuggestionIndex >= 0 &&
|
|
127
161
|
selectedSuggestionIndex < filteredSuggestions.length
|
|
128
162
|
) {
|
|
@@ -136,6 +170,11 @@ const TelemetrySearchBar: React.ForwardRefExoticComponent<
|
|
|
136
170
|
partialValue.length > 0 &&
|
|
137
171
|
props.onFieldValueSelect
|
|
138
172
|
) {
|
|
173
|
+
/*
|
|
174
|
+
* Prefer a match from the suggestion list (so casing matches
|
|
175
|
+
* what's actually in the data); otherwise accept the typed
|
|
176
|
+
* value as-is so users aren't blocked when no suggestion exists.
|
|
177
|
+
*/
|
|
139
178
|
const resolvedField: string =
|
|
140
179
|
fieldAliasMap[fieldPrefix] || fieldPrefix;
|
|
141
180
|
const availableValues: Array<string> =
|
|
@@ -147,23 +186,21 @@ const TelemetrySearchBar: React.ForwardRefExoticComponent<
|
|
|
147
186
|
},
|
|
148
187
|
);
|
|
149
188
|
|
|
150
|
-
const resolvedMatch: string
|
|
189
|
+
const resolvedMatch: string =
|
|
151
190
|
exactMatch ||
|
|
152
191
|
(filteredSuggestions.length === 1
|
|
153
|
-
? filteredSuggestions[0]
|
|
154
|
-
:
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
192
|
+
? filteredSuggestions[0]!
|
|
193
|
+
: partialValue);
|
|
194
|
+
|
|
195
|
+
props.onFieldValueSelect(fieldPrefix, resolvedMatch);
|
|
196
|
+
const parts: Array<string> = props.value.split(/\s+/);
|
|
197
|
+
parts.pop();
|
|
198
|
+
const remaining: string = parts.join(" ");
|
|
199
|
+
props.onChange(remaining ? remaining + " " : "");
|
|
200
|
+
setShowSuggestions(false);
|
|
201
|
+
setShowHelp(false);
|
|
202
|
+
e.preventDefault();
|
|
203
|
+
return;
|
|
167
204
|
}
|
|
168
205
|
|
|
169
206
|
props.onSubmit();
|
|
@@ -178,7 +215,7 @@ const TelemetrySearchBar: React.ForwardRefExoticComponent<
|
|
|
178
215
|
return;
|
|
179
216
|
}
|
|
180
217
|
|
|
181
|
-
if (!shouldShowSuggestions) {
|
|
218
|
+
if (!shouldShowSuggestions || isLoadingForCurrentMode) {
|
|
182
219
|
return;
|
|
183
220
|
}
|
|
184
221
|
|
|
@@ -206,6 +243,7 @@ const TelemetrySearchBar: React.ForwardRefExoticComponent<
|
|
|
206
243
|
partialValue,
|
|
207
244
|
props,
|
|
208
245
|
fieldAliasMap,
|
|
246
|
+
isLoadingForCurrentMode,
|
|
209
247
|
],
|
|
210
248
|
);
|
|
211
249
|
|
|
@@ -229,7 +267,13 @@ const TelemetrySearchBar: React.ForwardRefExoticComponent<
|
|
|
229
267
|
const parts: Array<string> = props.value.split(/\s+/);
|
|
230
268
|
|
|
231
269
|
if (parts.length > 0) {
|
|
232
|
-
|
|
270
|
+
/*
|
|
271
|
+
* Attribute suggestions are stored without `@`; add it back
|
|
272
|
+
* when filling the bar so the parser recognizes it as an attribute.
|
|
273
|
+
*/
|
|
274
|
+
parts[parts.length - 1] = hasAtPrefix
|
|
275
|
+
? "@" + suggestion + ":"
|
|
276
|
+
: suggestion + ":";
|
|
233
277
|
}
|
|
234
278
|
|
|
235
279
|
props.onChange(parts.join(" "));
|
|
@@ -237,7 +281,7 @@ const TelemetrySearchBar: React.ForwardRefExoticComponent<
|
|
|
237
281
|
setShowHelp(false);
|
|
238
282
|
inputRef.current?.focus();
|
|
239
283
|
},
|
|
240
|
-
[props, isValueMode, fieldPrefix],
|
|
284
|
+
[props, isValueMode, fieldPrefix, hasAtPrefix],
|
|
241
285
|
);
|
|
242
286
|
|
|
243
287
|
const handleExampleClick: (example: string) => void = useCallback(
|
|
@@ -268,6 +312,17 @@ const TelemetrySearchBar: React.ForwardRefExoticComponent<
|
|
|
268
312
|
};
|
|
269
313
|
}, []);
|
|
270
314
|
|
|
315
|
+
const loadingMessage: string = isValueMode
|
|
316
|
+
? `Loading values for ${fieldPrefix}...`
|
|
317
|
+
: "Loading attributes...";
|
|
318
|
+
|
|
319
|
+
const emptyMessage: string | undefined =
|
|
320
|
+
isValueMode &&
|
|
321
|
+
!isLoadingForCurrentMode &&
|
|
322
|
+
filteredSuggestions.length === 0
|
|
323
|
+
? `No matching values — press Enter to filter by "${partialValue}"`
|
|
324
|
+
: undefined;
|
|
325
|
+
|
|
271
326
|
return (
|
|
272
327
|
<div ref={containerRef} className="relative">
|
|
273
328
|
<div
|
|
@@ -329,6 +384,10 @@ const TelemetrySearchBar: React.ForwardRefExoticComponent<
|
|
|
329
384
|
selectedIndex={selectedSuggestionIndex}
|
|
330
385
|
onSelect={applySuggestion}
|
|
331
386
|
fieldContext={isValueMode ? fieldPrefix : undefined}
|
|
387
|
+
isAttributeMode={hasAtPrefix}
|
|
388
|
+
isLoading={isLoadingForCurrentMode}
|
|
389
|
+
loadingMessage={loadingMessage}
|
|
390
|
+
emptyMessage={emptyMessage}
|
|
332
391
|
/>
|
|
333
392
|
)}
|
|
334
393
|
|
|
@@ -5,6 +5,14 @@ export interface TelemetrySearchSuggestionsProps {
|
|
|
5
5
|
selectedIndex: number;
|
|
6
6
|
onSelect: (suggestion: string) => void;
|
|
7
7
|
fieldContext?: string | undefined;
|
|
8
|
+
/*
|
|
9
|
+
* When true, items are attribute keys (rendered with the @ prefix).
|
|
10
|
+
* When false, items are top-level field names (no prefix).
|
|
11
|
+
*/
|
|
12
|
+
isAttributeMode?: boolean | undefined;
|
|
13
|
+
isLoading?: boolean | undefined;
|
|
14
|
+
loadingMessage?: string | undefined;
|
|
15
|
+
emptyMessage?: string | undefined;
|
|
8
16
|
}
|
|
9
17
|
|
|
10
18
|
const MAX_VISIBLE_SUGGESTIONS: number = 8;
|
|
@@ -19,6 +27,39 @@ const TelemetrySearchSuggestions: FunctionComponent<
|
|
|
19
27
|
|
|
20
28
|
return (
|
|
21
29
|
<div className="absolute left-0 right-0 top-full z-50 mt-1 max-h-56 overflow-y-auto rounded-lg border border-gray-200 bg-white py-1 shadow-lg">
|
|
30
|
+
{props.isLoading && (
|
|
31
|
+
<div className="flex w-full items-center px-3 py-2 text-left text-sm text-gray-500">
|
|
32
|
+
<svg
|
|
33
|
+
className="animate-spin -ml-0.5 mr-2 h-4 w-4 text-indigo-500"
|
|
34
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
35
|
+
fill="none"
|
|
36
|
+
viewBox="0 0 24 24"
|
|
37
|
+
aria-hidden="true"
|
|
38
|
+
>
|
|
39
|
+
<circle
|
|
40
|
+
className="opacity-25"
|
|
41
|
+
cx="12"
|
|
42
|
+
cy="12"
|
|
43
|
+
r="10"
|
|
44
|
+
stroke="currentColor"
|
|
45
|
+
strokeWidth="4"
|
|
46
|
+
></circle>
|
|
47
|
+
<path
|
|
48
|
+
className="opacity-75"
|
|
49
|
+
fill="currentColor"
|
|
50
|
+
d="M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z"
|
|
51
|
+
></path>
|
|
52
|
+
</svg>
|
|
53
|
+
<span>{props.loadingMessage || "Loading..."}</span>
|
|
54
|
+
</div>
|
|
55
|
+
)}
|
|
56
|
+
{!props.isLoading &&
|
|
57
|
+
visible.length === 0 &&
|
|
58
|
+
props.emptyMessage !== undefined && (
|
|
59
|
+
<div className="px-3 py-2 text-sm text-gray-400">
|
|
60
|
+
{props.emptyMessage}
|
|
61
|
+
</div>
|
|
62
|
+
)}
|
|
22
63
|
{visible.map((suggestion: string, index: number) => {
|
|
23
64
|
const isSelected: boolean = index === props.selectedIndex;
|
|
24
65
|
|
|
@@ -43,11 +84,13 @@ const TelemetrySearchSuggestions: FunctionComponent<
|
|
|
43
84
|
</span>
|
|
44
85
|
<span className="font-mono">{suggestion}</span>
|
|
45
86
|
</>
|
|
46
|
-
) : (
|
|
87
|
+
) : props.isAttributeMode ? (
|
|
47
88
|
<>
|
|
48
89
|
<span className="font-mono text-xs text-indigo-400">@</span>
|
|
49
90
|
<span className="font-mono">{suggestion}</span>
|
|
50
91
|
</>
|
|
92
|
+
) : (
|
|
93
|
+
<span className="font-mono">{suggestion}</span>
|
|
51
94
|
)}
|
|
52
95
|
</button>
|
|
53
96
|
);
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import DashboardAlertListComponent from "../../../Types/Dashboard/DashboardComponents/DashboardAlertListComponent";
|
|
2
|
+
import { ObjectType } from "../../../Types/JSON";
|
|
3
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
4
|
+
import DashboardBaseComponentUtil from "./DashboardBaseComponent";
|
|
5
|
+
import {
|
|
6
|
+
ComponentArgument,
|
|
7
|
+
ComponentArgumentSection,
|
|
8
|
+
ComponentInputType,
|
|
9
|
+
} from "../../../Types/Dashboard/DashboardComponents/ComponentArgument";
|
|
10
|
+
import DashboardComponentType from "../../../Types/Dashboard/DashboardComponentType";
|
|
11
|
+
|
|
12
|
+
const DisplaySection: ComponentArgumentSection = {
|
|
13
|
+
name: "Display Options",
|
|
14
|
+
description: "Configure the widget title and row limit",
|
|
15
|
+
order: 1,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const FiltersSection: ComponentArgumentSection = {
|
|
19
|
+
name: "Filters",
|
|
20
|
+
description: "Narrow down which alerts are shown",
|
|
21
|
+
order: 2,
|
|
22
|
+
defaultCollapsed: true,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default class DashboardAlertListComponentUtil extends DashboardBaseComponentUtil {
|
|
26
|
+
public static override getDefaultComponent(): DashboardAlertListComponent {
|
|
27
|
+
return {
|
|
28
|
+
_type: ObjectType.DashboardComponent,
|
|
29
|
+
componentType: DashboardComponentType.AlertList,
|
|
30
|
+
widthInDashboardUnits: 6,
|
|
31
|
+
heightInDashboardUnits: 4,
|
|
32
|
+
topInDashboardUnits: 0,
|
|
33
|
+
leftInDashboardUnits: 0,
|
|
34
|
+
componentId: ObjectID.generate(),
|
|
35
|
+
minHeightInDashboardUnits: 3,
|
|
36
|
+
minWidthInDashboardUnits: 6,
|
|
37
|
+
arguments: {
|
|
38
|
+
maxRows: 25,
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public static override getComponentConfigArguments(): Array<
|
|
44
|
+
ComponentArgument<DashboardAlertListComponent>
|
|
45
|
+
> {
|
|
46
|
+
const componentArguments: Array<
|
|
47
|
+
ComponentArgument<DashboardAlertListComponent>
|
|
48
|
+
> = [];
|
|
49
|
+
|
|
50
|
+
componentArguments.push({
|
|
51
|
+
name: "Title",
|
|
52
|
+
description: "Header shown above the alert list",
|
|
53
|
+
required: false,
|
|
54
|
+
type: ComponentInputType.Text,
|
|
55
|
+
id: "title",
|
|
56
|
+
section: DisplaySection,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
componentArguments.push({
|
|
60
|
+
name: "Max Rows",
|
|
61
|
+
description: "Maximum number of alerts to show",
|
|
62
|
+
required: false,
|
|
63
|
+
type: ComponentInputType.Number,
|
|
64
|
+
id: "maxRows",
|
|
65
|
+
placeholder: "25",
|
|
66
|
+
section: DisplaySection,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
componentArguments.push({
|
|
70
|
+
name: "State",
|
|
71
|
+
description: "Filter alerts by lifecycle state",
|
|
72
|
+
required: false,
|
|
73
|
+
type: ComponentInputType.Dropdown,
|
|
74
|
+
id: "stateFilter",
|
|
75
|
+
section: FiltersSection,
|
|
76
|
+
dropdownOptions: [
|
|
77
|
+
{ label: "All", value: "" },
|
|
78
|
+
{ label: "Unresolved (open)", value: "unresolved" },
|
|
79
|
+
{ label: "Acknowledged", value: "acknowledged" },
|
|
80
|
+
{ label: "Resolved", value: "resolved" },
|
|
81
|
+
],
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
return componentArguments;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import DashboardIncidentListComponent from "../../../Types/Dashboard/DashboardComponents/DashboardIncidentListComponent";
|
|
2
|
+
import { ObjectType } from "../../../Types/JSON";
|
|
3
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
4
|
+
import DashboardBaseComponentUtil from "./DashboardBaseComponent";
|
|
5
|
+
import {
|
|
6
|
+
ComponentArgument,
|
|
7
|
+
ComponentArgumentSection,
|
|
8
|
+
ComponentInputType,
|
|
9
|
+
} from "../../../Types/Dashboard/DashboardComponents/ComponentArgument";
|
|
10
|
+
import DashboardComponentType from "../../../Types/Dashboard/DashboardComponentType";
|
|
11
|
+
|
|
12
|
+
const DisplaySection: ComponentArgumentSection = {
|
|
13
|
+
name: "Display Options",
|
|
14
|
+
description: "Configure the widget title and row limit",
|
|
15
|
+
order: 1,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const FiltersSection: ComponentArgumentSection = {
|
|
19
|
+
name: "Filters",
|
|
20
|
+
description: "Narrow down which incidents are shown",
|
|
21
|
+
order: 2,
|
|
22
|
+
defaultCollapsed: true,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default class DashboardIncidentListComponentUtil extends DashboardBaseComponentUtil {
|
|
26
|
+
public static override getDefaultComponent(): DashboardIncidentListComponent {
|
|
27
|
+
return {
|
|
28
|
+
_type: ObjectType.DashboardComponent,
|
|
29
|
+
componentType: DashboardComponentType.IncidentList,
|
|
30
|
+
widthInDashboardUnits: 6,
|
|
31
|
+
heightInDashboardUnits: 4,
|
|
32
|
+
topInDashboardUnits: 0,
|
|
33
|
+
leftInDashboardUnits: 0,
|
|
34
|
+
componentId: ObjectID.generate(),
|
|
35
|
+
minHeightInDashboardUnits: 3,
|
|
36
|
+
minWidthInDashboardUnits: 6,
|
|
37
|
+
arguments: {
|
|
38
|
+
maxRows: 25,
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public static override getComponentConfigArguments(): Array<
|
|
44
|
+
ComponentArgument<DashboardIncidentListComponent>
|
|
45
|
+
> {
|
|
46
|
+
const componentArguments: Array<
|
|
47
|
+
ComponentArgument<DashboardIncidentListComponent>
|
|
48
|
+
> = [];
|
|
49
|
+
|
|
50
|
+
componentArguments.push({
|
|
51
|
+
name: "Title",
|
|
52
|
+
description: "Header shown above the incident list",
|
|
53
|
+
required: false,
|
|
54
|
+
type: ComponentInputType.Text,
|
|
55
|
+
id: "title",
|
|
56
|
+
section: DisplaySection,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
componentArguments.push({
|
|
60
|
+
name: "Max Rows",
|
|
61
|
+
description: "Maximum number of incidents to show",
|
|
62
|
+
required: false,
|
|
63
|
+
type: ComponentInputType.Number,
|
|
64
|
+
id: "maxRows",
|
|
65
|
+
placeholder: "25",
|
|
66
|
+
section: DisplaySection,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
componentArguments.push({
|
|
70
|
+
name: "State",
|
|
71
|
+
description: "Filter incidents by lifecycle state",
|
|
72
|
+
required: false,
|
|
73
|
+
type: ComponentInputType.Dropdown,
|
|
74
|
+
id: "stateFilter",
|
|
75
|
+
section: FiltersSection,
|
|
76
|
+
dropdownOptions: [
|
|
77
|
+
{ label: "All", value: "" },
|
|
78
|
+
{ label: "Unresolved (open)", value: "unresolved" },
|
|
79
|
+
{ label: "Acknowledged", value: "acknowledged" },
|
|
80
|
+
{ label: "Resolved", value: "resolved" },
|
|
81
|
+
],
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
return componentArguments;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import DashboardMonitorListComponent from "../../../Types/Dashboard/DashboardComponents/DashboardMonitorListComponent";
|
|
2
|
+
import { ObjectType } from "../../../Types/JSON";
|
|
3
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
4
|
+
import DashboardBaseComponentUtil from "./DashboardBaseComponent";
|
|
5
|
+
import {
|
|
6
|
+
ComponentArgument,
|
|
7
|
+
ComponentArgumentSection,
|
|
8
|
+
ComponentInputType,
|
|
9
|
+
} from "../../../Types/Dashboard/DashboardComponents/ComponentArgument";
|
|
10
|
+
import DashboardComponentType from "../../../Types/Dashboard/DashboardComponentType";
|
|
11
|
+
|
|
12
|
+
const DisplaySection: ComponentArgumentSection = {
|
|
13
|
+
name: "Display Options",
|
|
14
|
+
description: "Configure the widget title and row limit",
|
|
15
|
+
order: 1,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const FiltersSection: ComponentArgumentSection = {
|
|
19
|
+
name: "Filters",
|
|
20
|
+
description: "Narrow down which monitors are shown",
|
|
21
|
+
order: 2,
|
|
22
|
+
defaultCollapsed: true,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default class DashboardMonitorListComponentUtil extends DashboardBaseComponentUtil {
|
|
26
|
+
public static override getDefaultComponent(): DashboardMonitorListComponent {
|
|
27
|
+
return {
|
|
28
|
+
_type: ObjectType.DashboardComponent,
|
|
29
|
+
componentType: DashboardComponentType.MonitorList,
|
|
30
|
+
widthInDashboardUnits: 6,
|
|
31
|
+
heightInDashboardUnits: 4,
|
|
32
|
+
topInDashboardUnits: 0,
|
|
33
|
+
leftInDashboardUnits: 0,
|
|
34
|
+
componentId: ObjectID.generate(),
|
|
35
|
+
minHeightInDashboardUnits: 3,
|
|
36
|
+
minWidthInDashboardUnits: 6,
|
|
37
|
+
arguments: {
|
|
38
|
+
maxRows: 25,
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public static override getComponentConfigArguments(): Array<
|
|
44
|
+
ComponentArgument<DashboardMonitorListComponent>
|
|
45
|
+
> {
|
|
46
|
+
const componentArguments: Array<
|
|
47
|
+
ComponentArgument<DashboardMonitorListComponent>
|
|
48
|
+
> = [];
|
|
49
|
+
|
|
50
|
+
componentArguments.push({
|
|
51
|
+
name: "Title",
|
|
52
|
+
description: "Header shown above the monitor list",
|
|
53
|
+
required: false,
|
|
54
|
+
type: ComponentInputType.Text,
|
|
55
|
+
id: "title",
|
|
56
|
+
section: DisplaySection,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
componentArguments.push({
|
|
60
|
+
name: "Max Rows",
|
|
61
|
+
description: "Maximum number of monitors to show",
|
|
62
|
+
required: false,
|
|
63
|
+
type: ComponentInputType.Number,
|
|
64
|
+
id: "maxRows",
|
|
65
|
+
placeholder: "25",
|
|
66
|
+
section: DisplaySection,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
componentArguments.push({
|
|
70
|
+
name: "Status",
|
|
71
|
+
description: "Filter monitors by current status",
|
|
72
|
+
required: false,
|
|
73
|
+
type: ComponentInputType.Dropdown,
|
|
74
|
+
id: "statusFilter",
|
|
75
|
+
section: FiltersSection,
|
|
76
|
+
dropdownOptions: [
|
|
77
|
+
{ label: "All", value: "" },
|
|
78
|
+
{ label: "Operational only", value: "operational" },
|
|
79
|
+
{ label: "Not operational only", value: "non-operational" },
|
|
80
|
+
],
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
return componentArguments;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -2,9 +2,12 @@ import { ComponentArgument } from "../../../Types/Dashboard/DashboardComponents/
|
|
|
2
2
|
import DashboardBaseComponent from "../../../Types/Dashboard/DashboardComponents/DashboardBaseComponent";
|
|
3
3
|
import DashboardComponentType from "../../../Types/Dashboard/DashboardComponentType";
|
|
4
4
|
import BadDataException from "../../../Types/Exception/BadDataException";
|
|
5
|
+
import DashboardAlertListComponentUtil from "./DashboardAlertListComponent";
|
|
5
6
|
import DashboardChartComponentUtil from "./DashboardChartComponent";
|
|
6
7
|
import DashboardGaugeComponentUtil from "./DashboardGaugeComponent";
|
|
8
|
+
import DashboardIncidentListComponentUtil from "./DashboardIncidentListComponent";
|
|
7
9
|
import DashboardLogStreamComponentUtil from "./DashboardLogStreamComponent";
|
|
10
|
+
import DashboardMonitorListComponentUtil from "./DashboardMonitorListComponent";
|
|
8
11
|
import DashboardTableComponentUtil from "./DashboardTableComponent";
|
|
9
12
|
import DashboardTextComponentUtil from "./DashboardTextComponent";
|
|
10
13
|
import DashboardTraceListComponentUtil from "./DashboardTraceListComponent";
|
|
@@ -56,6 +59,24 @@ export default class DashboardComponentsUtil {
|
|
|
56
59
|
>;
|
|
57
60
|
}
|
|
58
61
|
|
|
62
|
+
if (dashboardComponentType === DashboardComponentType.IncidentList) {
|
|
63
|
+
return DashboardIncidentListComponentUtil.getComponentConfigArguments() as Array<
|
|
64
|
+
ComponentArgument<DashboardBaseComponent>
|
|
65
|
+
>;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (dashboardComponentType === DashboardComponentType.AlertList) {
|
|
69
|
+
return DashboardAlertListComponentUtil.getComponentConfigArguments() as Array<
|
|
70
|
+
ComponentArgument<DashboardBaseComponent>
|
|
71
|
+
>;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (dashboardComponentType === DashboardComponentType.MonitorList) {
|
|
75
|
+
return DashboardMonitorListComponentUtil.getComponentConfigArguments() as Array<
|
|
76
|
+
ComponentArgument<DashboardBaseComponent>
|
|
77
|
+
>;
|
|
78
|
+
}
|
|
79
|
+
|
|
59
80
|
throw new BadDataException(
|
|
60
81
|
`Unknown dashboard component type: ${dashboardComponentType}`,
|
|
61
82
|
);
|