@oneuptime/common 10.0.20 → 10.0.21
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/Server/API/TelemetryAPI.ts +208 -0
- package/Server/API/UserCallAPI.ts +29 -0
- package/Server/API/UserEmailAPI.ts +29 -0
- package/Server/API/UserSmsAPI.ts +29 -0
- package/Server/API/UserWhatsAppAPI.ts +29 -0
- package/Server/Services/LogAggregationService.ts +251 -0
- package/Server/Utils/VM/VMRunner.ts +10 -0
- package/Types/Log/LogQueryParser.ts +252 -0
- package/Types/Log/LogQueryToFilter.ts +131 -0
- package/UI/Components/CopyTextButton/CopyTextButton.tsx +3 -3
- package/UI/Components/LogsViewer/LogsViewer.tsx +166 -93
- package/UI/Components/LogsViewer/components/ActiveFilterChips.tsx +58 -0
- package/UI/Components/LogsViewer/components/FacetSection.tsx +119 -0
- package/UI/Components/LogsViewer/components/FacetValueRow.tsx +102 -0
- package/UI/Components/LogsViewer/components/HistogramTooltip.tsx +122 -0
- package/UI/Components/LogsViewer/components/LiveLogsToggle.tsx +4 -4
- package/UI/Components/LogsViewer/components/LogDetailsPanel.tsx +22 -26
- package/UI/Components/LogsViewer/components/LogSearchBar.tsx +360 -0
- package/UI/Components/LogsViewer/components/LogSearchHelp.tsx +128 -0
- package/UI/Components/LogsViewer/components/LogSearchSuggestions.tsx +64 -0
- package/UI/Components/LogsViewer/components/LogTimeRangePicker.tsx +199 -0
- package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +172 -0
- package/UI/Components/LogsViewer/components/LogsFilterCard.tsx +27 -57
- package/UI/Components/LogsViewer/components/LogsHistogram.tsx +268 -0
- package/UI/Components/LogsViewer/components/LogsPagination.tsx +12 -10
- package/UI/Components/LogsViewer/components/LogsTable.tsx +33 -32
- package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +16 -18
- package/UI/Components/LogsViewer/components/severityColors.ts +31 -0
- package/UI/Components/LogsViewer/components/severityTheme.ts +25 -25
- package/UI/Components/LogsViewer/types.ts +20 -0
- package/build/dist/Server/API/TelemetryAPI.js +136 -0
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/API/UserCallAPI.js +17 -0
- package/build/dist/Server/API/UserCallAPI.js.map +1 -1
- package/build/dist/Server/API/UserEmailAPI.js +17 -0
- package/build/dist/Server/API/UserEmailAPI.js.map +1 -1
- package/build/dist/Server/API/UserSmsAPI.js +17 -0
- package/build/dist/Server/API/UserSmsAPI.js.map +1 -1
- package/build/dist/Server/API/UserWhatsAppAPI.js +17 -0
- package/build/dist/Server/API/UserWhatsAppAPI.js.map +1 -1
- package/build/dist/Server/Services/LogAggregationService.js +163 -0
- package/build/dist/Server/Services/LogAggregationService.js.map +1 -0
- package/build/dist/Server/Utils/VM/VMRunner.js +10 -0
- package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
- package/build/dist/Types/Log/LogQueryParser.js +200 -0
- package/build/dist/Types/Log/LogQueryParser.js.map +1 -0
- package/build/dist/Types/Log/LogQueryToFilter.js +96 -0
- package/build/dist/Types/Log/LogQueryToFilter.js.map +1 -0
- package/build/dist/UI/Components/CopyTextButton/CopyTextButton.js +3 -3
- package/build/dist/UI/Components/CopyTextButton/CopyTextButton.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +64 -42
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js +24 -0
- package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js.map +1 -0
- package/build/dist/UI/Components/LogsViewer/components/FacetSection.js +46 -0
- package/build/dist/UI/Components/LogsViewer/components/FacetSection.js.map +1 -0
- package/build/dist/UI/Components/LogsViewer/components/FacetValueRow.js +35 -0
- package/build/dist/UI/Components/LogsViewer/components/FacetValueRow.js.map +1 -0
- package/build/dist/UI/Components/LogsViewer/components/HistogramTooltip.js +64 -0
- package/build/dist/UI/Components/LogsViewer/components/HistogramTooltip.js.map +1 -0
- package/build/dist/UI/Components/LogsViewer/components/LiveLogsToggle.js +4 -4
- package/build/dist/UI/Components/LogsViewer/components/LiveLogsToggle.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js +19 -21
- package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogSearchBar.js +230 -0
- package/build/dist/UI/Components/LogsViewer/components/LogSearchBar.js.map +1 -0
- package/build/dist/UI/Components/LogsViewer/components/LogSearchHelp.js +84 -0
- package/build/dist/UI/Components/LogsViewer/components/LogSearchHelp.js.map +1 -0
- package/build/dist/UI/Components/LogsViewer/components/LogSearchSuggestions.js +27 -0
- package/build/dist/UI/Components/LogsViewer/components/LogSearchSuggestions.js.map +1 -0
- package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js +100 -0
- package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js.map +1 -0
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +104 -0
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -0
- package/build/dist/UI/Components/LogsViewer/components/LogsFilterCard.js +14 -35
- package/build/dist/UI/Components/LogsViewer/components/LogsFilterCard.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +127 -0
- package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -0
- package/build/dist/UI/Components/LogsViewer/components/LogsPagination.js +9 -9
- package/build/dist/UI/Components/LogsViewer/components/LogsPagination.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsTable.js +31 -30
- package/build/dist/UI/Components/LogsViewer/components/LogsTable.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +7 -8
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/severityColors.js +22 -0
- package/build/dist/UI/Components/LogsViewer/components/severityColors.js.map +1 -0
- package/build/dist/UI/Components/LogsViewer/components/severityTheme.js +25 -25
- package/build/dist/UI/Components/LogsViewer/components/severityTheme.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LogQueryParser
|
|
3
|
+
*
|
|
4
|
+
* Parses a Datadog-style log search query string into structured filter tokens.
|
|
5
|
+
*
|
|
6
|
+
* Supported syntax:
|
|
7
|
+
* - Free text: `connection refused` → body ILIKE '%connection refused%'
|
|
8
|
+
* - Quoted phrase: `"connection refused"` → body ILIKE '%connection refused%'
|
|
9
|
+
* - Field-specific: `severity:error` → severityText = 'error'
|
|
10
|
+
* - Attribute access: `@http.status_code:500` → attributes.http.status_code = '500'
|
|
11
|
+
* - Negation (prefix): `-severity:debug` → severityText != 'debug'
|
|
12
|
+
* - Wildcard: `service:api-*` → serviceId ILIKE 'api-%'
|
|
13
|
+
* - Numeric range: `@duration:>1000` → attributes.duration > 1000
|
|
14
|
+
* - Boolean: `severity:error AND service:api` (AND is default between tokens)
|
|
15
|
+
*
|
|
16
|
+
* Produces an array of ParsedToken objects consumed by the search bar and query builder.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
export enum TokenType {
|
|
20
|
+
FreeText = "FreeText",
|
|
21
|
+
FieldFilter = "FieldFilter",
|
|
22
|
+
AttributeFilter = "AttributeFilter",
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export enum FilterOperator {
|
|
26
|
+
Equals = "Equals",
|
|
27
|
+
NotEquals = "NotEquals",
|
|
28
|
+
Contains = "Contains",
|
|
29
|
+
NotContains = "NotContains",
|
|
30
|
+
GreaterThan = "GreaterThan",
|
|
31
|
+
GreaterThanOrEqual = "GreaterThanOrEqual",
|
|
32
|
+
LessThan = "LessThan",
|
|
33
|
+
LessThanOrEqual = "LessThanOrEqual",
|
|
34
|
+
Wildcard = "Wildcard",
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface ParsedToken {
|
|
38
|
+
type: TokenType;
|
|
39
|
+
field?: string;
|
|
40
|
+
operator: FilterOperator;
|
|
41
|
+
value: string;
|
|
42
|
+
negated: boolean;
|
|
43
|
+
raw: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const FIELD_ALIASES: Record<string, string> = {
|
|
47
|
+
severity: "severityText",
|
|
48
|
+
level: "severityText",
|
|
49
|
+
service: "serviceId",
|
|
50
|
+
trace: "traceId",
|
|
51
|
+
span: "spanId",
|
|
52
|
+
message: "body",
|
|
53
|
+
msg: "body",
|
|
54
|
+
log: "body",
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const BOOLEAN_KEYWORDS: Set<string> = new Set(["AND", "OR", "NOT"]);
|
|
58
|
+
|
|
59
|
+
function resolveFieldName(raw: string): string {
|
|
60
|
+
const lower: string = raw.toLowerCase();
|
|
61
|
+
return FIELD_ALIASES[lower] || raw;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function detectOperator(value: string): {
|
|
65
|
+
operator: FilterOperator;
|
|
66
|
+
cleanValue: string;
|
|
67
|
+
} {
|
|
68
|
+
if (value.startsWith(">=")) {
|
|
69
|
+
return {
|
|
70
|
+
operator: FilterOperator.GreaterThanOrEqual,
|
|
71
|
+
cleanValue: value.slice(2),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (value.startsWith("<=")) {
|
|
76
|
+
return {
|
|
77
|
+
operator: FilterOperator.LessThanOrEqual,
|
|
78
|
+
cleanValue: value.slice(2),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (value.startsWith(">")) {
|
|
83
|
+
return {
|
|
84
|
+
operator: FilterOperator.GreaterThan,
|
|
85
|
+
cleanValue: value.slice(1),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (value.startsWith("<")) {
|
|
90
|
+
return {
|
|
91
|
+
operator: FilterOperator.LessThan,
|
|
92
|
+
cleanValue: value.slice(1),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (value.includes("*")) {
|
|
97
|
+
return {
|
|
98
|
+
operator: FilterOperator.Wildcard,
|
|
99
|
+
cleanValue: value,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
operator: FilterOperator.Equals,
|
|
105
|
+
cleanValue: value,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function tokenizeRawInput(input: string): Array<string> {
|
|
110
|
+
const tokens: Array<string> = [];
|
|
111
|
+
let current: string = "";
|
|
112
|
+
let inQuotes: boolean = false;
|
|
113
|
+
|
|
114
|
+
for (let i: number = 0; i < input.length; i++) {
|
|
115
|
+
const char: string = input[i]!;
|
|
116
|
+
|
|
117
|
+
if (char === '"') {
|
|
118
|
+
inQuotes = !inQuotes;
|
|
119
|
+
current += char;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (char === " " && !inQuotes) {
|
|
124
|
+
if (current.length > 0) {
|
|
125
|
+
tokens.push(current);
|
|
126
|
+
current = "";
|
|
127
|
+
}
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
current += char;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (current.length > 0) {
|
|
135
|
+
tokens.push(current);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return tokens;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function stripQuotes(value: string): string {
|
|
142
|
+
if (value.startsWith('"') && value.endsWith('"') && value.length >= 2) {
|
|
143
|
+
return value.slice(1, -1);
|
|
144
|
+
}
|
|
145
|
+
return value;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function parseFieldToken(raw: string): ParsedToken {
|
|
149
|
+
let workingRaw: string = raw;
|
|
150
|
+
let negated: boolean = false;
|
|
151
|
+
|
|
152
|
+
if (workingRaw.startsWith("-")) {
|
|
153
|
+
negated = true;
|
|
154
|
+
workingRaw = workingRaw.slice(1);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const isAttribute: boolean = workingRaw.startsWith("@");
|
|
158
|
+
|
|
159
|
+
if (isAttribute) {
|
|
160
|
+
workingRaw = workingRaw.slice(1);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const colonIndex: number = workingRaw.indexOf(":");
|
|
164
|
+
|
|
165
|
+
if (colonIndex === -1) {
|
|
166
|
+
return {
|
|
167
|
+
type: TokenType.FreeText,
|
|
168
|
+
operator: FilterOperator.Contains,
|
|
169
|
+
value: stripQuotes(workingRaw),
|
|
170
|
+
negated,
|
|
171
|
+
raw,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const rawField: string = workingRaw.slice(0, colonIndex);
|
|
176
|
+
const rawValue: string = stripQuotes(workingRaw.slice(colonIndex + 1));
|
|
177
|
+
|
|
178
|
+
const { operator, cleanValue } = detectOperator(rawValue);
|
|
179
|
+
|
|
180
|
+
const field: string = isAttribute ? rawField : resolveFieldName(rawField);
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
type: isAttribute ? TokenType.AttributeFilter : TokenType.FieldFilter,
|
|
184
|
+
field,
|
|
185
|
+
operator:
|
|
186
|
+
negated && operator === FilterOperator.Equals
|
|
187
|
+
? FilterOperator.NotEquals
|
|
188
|
+
: operator,
|
|
189
|
+
value: cleanValue,
|
|
190
|
+
negated,
|
|
191
|
+
raw,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export function parseLogQuery(query: string): Array<ParsedToken> {
|
|
196
|
+
const trimmed: string = query.trim();
|
|
197
|
+
|
|
198
|
+
if (trimmed.length === 0) {
|
|
199
|
+
return [];
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const rawTokens: Array<string> = tokenizeRawInput(trimmed);
|
|
203
|
+
const tokens: Array<ParsedToken> = [];
|
|
204
|
+
const freeTextParts: Array<string> = [];
|
|
205
|
+
|
|
206
|
+
const flushFreeText: () => void = (): void => {
|
|
207
|
+
if (freeTextParts.length > 0) {
|
|
208
|
+
const combined: string = freeTextParts.join(" ");
|
|
209
|
+
tokens.push({
|
|
210
|
+
type: TokenType.FreeText,
|
|
211
|
+
operator: FilterOperator.Contains,
|
|
212
|
+
value: combined,
|
|
213
|
+
negated: false,
|
|
214
|
+
raw: combined,
|
|
215
|
+
});
|
|
216
|
+
freeTextParts.length = 0;
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
for (const rawToken of rawTokens) {
|
|
221
|
+
if (BOOLEAN_KEYWORDS.has(rawToken)) {
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const hasColon: boolean =
|
|
226
|
+
rawToken.includes(":") && !rawToken.startsWith('"');
|
|
227
|
+
|
|
228
|
+
const isNegatedField: boolean =
|
|
229
|
+
rawToken.startsWith("-") && rawToken.slice(1).includes(":");
|
|
230
|
+
|
|
231
|
+
if (hasColon || isNegatedField) {
|
|
232
|
+
flushFreeText();
|
|
233
|
+
tokens.push(parseFieldToken(rawToken));
|
|
234
|
+
} else {
|
|
235
|
+
freeTextParts.push(stripQuotes(rawToken));
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
flushFreeText();
|
|
240
|
+
|
|
241
|
+
return tokens;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export function tokensToDisplayString(tokens: Array<ParsedToken>): string {
|
|
245
|
+
return tokens
|
|
246
|
+
.map((t: ParsedToken) => {
|
|
247
|
+
return t.raw;
|
|
248
|
+
})
|
|
249
|
+
.join(" ");
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export default parseLogQuery;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts parsed log query tokens into a Query<Log> object compatible with
|
|
3
|
+
* the AnalyticsDatabaseService query system.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
ParsedToken,
|
|
8
|
+
TokenType,
|
|
9
|
+
FilterOperator,
|
|
10
|
+
parseLogQuery,
|
|
11
|
+
} from "./LogQueryParser";
|
|
12
|
+
import Search from "../BaseDatabase/Search";
|
|
13
|
+
import GreaterThan from "../BaseDatabase/GreaterThan";
|
|
14
|
+
import GreaterThanOrEqual from "../BaseDatabase/GreaterThanOrEqual";
|
|
15
|
+
import LessThan from "../BaseDatabase/LessThan";
|
|
16
|
+
import LessThanOrEqual from "../BaseDatabase/LessThanOrEqual";
|
|
17
|
+
|
|
18
|
+
export interface LogFilter {
|
|
19
|
+
body?: string | Search<string>;
|
|
20
|
+
severityText?: string;
|
|
21
|
+
serviceId?: string;
|
|
22
|
+
traceId?: string;
|
|
23
|
+
spanId?: string;
|
|
24
|
+
attributes?: Record<string, unknown>;
|
|
25
|
+
[key: string]: unknown;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const TOP_LEVEL_FIELDS: Set<string> = new Set([
|
|
29
|
+
"severityText",
|
|
30
|
+
"serviceId",
|
|
31
|
+
"traceId",
|
|
32
|
+
"spanId",
|
|
33
|
+
"body",
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
function applyFieldFilter(filter: LogFilter, token: ParsedToken): void {
|
|
37
|
+
if (!token.field) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const value: string = token.value;
|
|
42
|
+
|
|
43
|
+
if (TOP_LEVEL_FIELDS.has(token.field)) {
|
|
44
|
+
applyTopLevelFilter(filter, token.field, value, token.operator);
|
|
45
|
+
} else {
|
|
46
|
+
applyAttributeFilter(filter, token.field, value, token.operator);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function applyTopLevelFilter(
|
|
51
|
+
filter: LogFilter,
|
|
52
|
+
field: string,
|
|
53
|
+
value: string,
|
|
54
|
+
operator: FilterOperator,
|
|
55
|
+
): void {
|
|
56
|
+
switch (operator) {
|
|
57
|
+
case FilterOperator.Contains:
|
|
58
|
+
case FilterOperator.Wildcard:
|
|
59
|
+
filter[field] = new Search(value.replace(/\*/g, ""));
|
|
60
|
+
break;
|
|
61
|
+
case FilterOperator.GreaterThan:
|
|
62
|
+
filter[field] = new GreaterThan(parseNumericOrString(value));
|
|
63
|
+
break;
|
|
64
|
+
case FilterOperator.GreaterThanOrEqual:
|
|
65
|
+
filter[field] = new GreaterThanOrEqual(parseNumericOrString(value));
|
|
66
|
+
break;
|
|
67
|
+
case FilterOperator.LessThan:
|
|
68
|
+
filter[field] = new LessThan(parseNumericOrString(value));
|
|
69
|
+
break;
|
|
70
|
+
case FilterOperator.LessThanOrEqual:
|
|
71
|
+
filter[field] = new LessThanOrEqual(parseNumericOrString(value));
|
|
72
|
+
break;
|
|
73
|
+
case FilterOperator.Equals:
|
|
74
|
+
case FilterOperator.NotEquals:
|
|
75
|
+
default:
|
|
76
|
+
filter[field] = value;
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function applyAttributeFilter(
|
|
82
|
+
filter: LogFilter,
|
|
83
|
+
field: string,
|
|
84
|
+
value: string,
|
|
85
|
+
_operator: FilterOperator,
|
|
86
|
+
): void {
|
|
87
|
+
if (!filter.attributes) {
|
|
88
|
+
filter.attributes = {};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
filter.attributes[field] = value;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function applyFreeTextFilter(filter: LogFilter, token: ParsedToken): void {
|
|
95
|
+
if (filter.body && filter.body instanceof Search) {
|
|
96
|
+
const existing: string = filter.body.toString();
|
|
97
|
+
filter.body = new Search(`${existing} ${token.value}`);
|
|
98
|
+
} else if (filter.body && typeof filter.body === "string") {
|
|
99
|
+
filter.body = new Search(`${filter.body} ${token.value}`);
|
|
100
|
+
} else {
|
|
101
|
+
filter.body = new Search(token.value);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function parseNumericOrString(value: string): number | string {
|
|
106
|
+
const num: number = Number(value);
|
|
107
|
+
return isNaN(num) ? value : num;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function queryStringToFilter(queryString: string): LogFilter {
|
|
111
|
+
const tokens: Array<ParsedToken> = parseLogQuery(queryString);
|
|
112
|
+
const filter: LogFilter = {};
|
|
113
|
+
|
|
114
|
+
for (const token of tokens) {
|
|
115
|
+
switch (token.type) {
|
|
116
|
+
case TokenType.FreeText:
|
|
117
|
+
applyFreeTextFilter(filter, token);
|
|
118
|
+
break;
|
|
119
|
+
case TokenType.FieldFilter:
|
|
120
|
+
applyFieldFilter(filter, token);
|
|
121
|
+
break;
|
|
122
|
+
case TokenType.AttributeFilter:
|
|
123
|
+
applyFieldFilter(filter, token);
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return filter;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export default queryStringToFilter;
|
|
@@ -54,14 +54,14 @@ const CopyTextButton: FunctionComponent<ComponentProps> = (
|
|
|
54
54
|
|
|
55
55
|
const variantClasses: Record<typeof variant, string> = {
|
|
56
56
|
ghost:
|
|
57
|
-
"bg-transparent border border-
|
|
58
|
-
soft: "bg-
|
|
57
|
+
"bg-transparent border border-gray-200 text-gray-400 hover:bg-gray-50 hover:text-gray-600",
|
|
58
|
+
soft: "bg-gray-100 text-gray-600 border border-gray-200 hover:bg-gray-200",
|
|
59
59
|
solid:
|
|
60
60
|
"bg-indigo-600 text-white border border-indigo-600 hover:bg-indigo-500",
|
|
61
61
|
} as const;
|
|
62
62
|
|
|
63
63
|
const copiedClasses: string =
|
|
64
|
-
"bg-emerald-
|
|
64
|
+
"bg-emerald-50 border border-emerald-200 text-emerald-600";
|
|
65
65
|
|
|
66
66
|
return (
|
|
67
67
|
<button
|