Skip to content

Commit 666d1a9

Browse files
committed
Merge branch 'small-fix'
# Conflicts: # cmd/logql-to-logsql/web/ui/package-lock.json
2 parents b1aa7b6 + 6f256c1 commit 666d1a9

6 files changed

Lines changed: 129 additions & 101 deletions

File tree

cmd/logql-to-logsql/web/ui/package-lock.json

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/logql-to-logsql/web/ui/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"date-fns": "^4.1.0",
3535
"dayjs": "^1.11.19",
3636
"lucide-react": "^0.562.0",
37+
"monaco-editor": "^0.32.1",
3738
"next-themes": "^0.4.6",
3839
"prettier": "^3.7.4",
3940
"react": "^19.2.3",

cmd/logql-to-logsql/web/ui/src/components/logql-editor/LogQLEditor.tsx

Lines changed: 74 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import Editor from "@monaco-editor/react";
1+
import Editor, {type OnMount} from "@monaco-editor/react";
22
import {
33
Card,
44
CardAction,
@@ -36,6 +36,7 @@ import {
3636
} from "@/components/date-time-range";
3737
import { parseDate } from "chrono-node";
3838
import { languageConfiguration, monarchlanguage } from '@grafana/monaco-logql';
39+
import {editor} from "monaco-editor";
3940

4041
export interface LogQLEditorProps {
4142
readonly onRun?: (logql: string, start?: number, end?: number) => void;
@@ -57,26 +58,26 @@ export function LogQLEditor({
5758
className,
5859
}: LogQLEditorProps) {
5960
const [value, setValue] = useState<string>(DEFAULT_EXAMPLE_ID);
60-
const [logql, setLogql] = useState("");
61+
const [logql, setLogql] = useState<undefined | string>("");
6162
const [timeRange, setTimeRange] = useState<DateTimeRangeValue>({
6263
from: "1h ago",
6364
to: "now",
6465
});
6566
const dateStart = useMemo(
6667
() => (timeRange?.from ? parseDate(timeRange?.from)?.valueOf() : undefined),
67-
[timeRange.from],
68+
[timeRange?.from],
6869
);
6970
const dateEnd = useMemo(
7071
() => (timeRange?.to ? parseDate(timeRange?.to)?.valueOf() : undefined),
71-
[timeRange.to],
72+
[timeRange?.to],
7273
);
7374

7475
const runQuery = useCallback(
75-
(text?: string) => {
76+
(text?: any) => {
7677
if (!onRun || isLoading) {
7778
return;
7879
}
79-
const current = typeof text === "string" ? text : logql;
80+
const current = typeof text === "string" ? text : (logql ?? '');
8081
onRun(current, dateStart, dateEnd);
8182
},
8283
[onRun, isLoading, logql, dateStart, dateEnd],
@@ -89,6 +90,67 @@ export function LogQLEditor({
8990
}
9091
}, [value]);
9192

93+
const initEditorHandler: OnMount = useCallback((editorInstance, monaco) => {
94+
monaco.languages.register({ id: "logql" });
95+
monaco.languages.setMonarchTokensProvider("logql", monarchlanguage);
96+
monaco.languages.setLanguageConfiguration("logql", languageConfiguration);
97+
monaco.languages.registerCompletionItemProvider("logql", {
98+
provideCompletionItems: () => {
99+
const suggestions = COMPLETIONS.map((label) => ({
100+
label,
101+
kind: monaco.languages.CompletionItemKind.Keyword,
102+
insertText: label,
103+
range: {
104+
startLineNumber: 1,
105+
endLineNumber: 1,
106+
startColumn: 1,
107+
endColumn: 1,
108+
},
109+
detail: "LogQL snippet",
110+
documentation: "LogQL snippet",
111+
sortText: label,
112+
filterText: label,
113+
}));
114+
return { suggestions };
115+
},
116+
});
117+
118+
const executeFromEditor = () =>
119+
runQuery(editorInstance.getValue() ?? "");
120+
const keybindings = [
121+
monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter,
122+
monaco.KeyMod.Shift | monaco.KeyCode.Enter,
123+
monaco.KeyMod.WinCtrl | monaco.KeyCode.Enter,
124+
monaco.KeyMod.CtrlCmd |
125+
monaco.KeyMod.Shift |
126+
monaco.KeyCode.Enter,
127+
];
128+
keybindings.forEach((binding) => {
129+
editorInstance.addCommand(binding, executeFromEditor);
130+
});
131+
}, [])
132+
133+
const editorOptions = useMemo(() => ({
134+
readOnly: isLoading,
135+
minimap: { enabled: false },
136+
fontSize: 12,
137+
lineNumbers: "off",
138+
scrollBeyondLastLine: false,
139+
selectionHighlight: !isLoading,
140+
} as editor.IStandaloneEditorConstructionOptions), [isLoading])
141+
142+
const examples = useMemo(() => (
143+
EXAMPLES.map((example) => (
144+
<SelectItem
145+
value={example.id}
146+
key={example.id}
147+
className={"cursor-pointer"}
148+
>
149+
Example: {example.title}
150+
</SelectItem>
151+
))
152+
), [])
153+
92154
return (
93155
<Card className={cn("w-full h-full py-4 border-none shadow-none drop-shadow-none", className)}>
94156
<CardHeader
@@ -101,15 +163,7 @@ export function LogQLEditor({
101163
<SelectValue placeholder="Select example" />
102164
</SelectTrigger>
103165
<SelectContent>
104-
{EXAMPLES.map((example) => (
105-
<SelectItem
106-
value={example.id}
107-
key={example.id}
108-
className={"cursor-pointer"}
109-
>
110-
Example: {example.title}
111-
</SelectItem>
112-
))}
166+
{examples}
113167
</SelectContent>
114168
</Select>
115169
{execMode === 'query' &&
@@ -118,7 +172,7 @@ export function LogQLEditor({
118172
<Button
119173
disabled={isLoading}
120174
className={"cursor-pointer max-sm:w-full"}
121-
onClick={() => runQuery()}
175+
onClick={runQuery}
122176
>
123177
{isLoading ? <Spinner /> : <PlayIcon />}
124178
Execute
@@ -137,55 +191,10 @@ export function LogQLEditor({
137191
defaultLanguage="logql"
138192
language="logql"
139193
theme="vs-light"
140-
value={logql}
141-
options={{
142-
readOnly: isLoading,
143-
minimap: { enabled: false },
144-
fontSize: 12,
145-
lineNumbers: "off",
146-
scrollBeyondLastLine: false,
147-
selectionHighlight: !isLoading,
148-
}}
149-
onChange={(next) => setLogql(next ?? "")}
150-
onMount={(editorInstance, monaco) => {
151-
monaco.languages.register({ id: "logql" });
152-
monaco.languages.setMonarchTokensProvider("logql", monarchlanguage);
153-
monaco.languages.setLanguageConfiguration("logql", languageConfiguration);
154-
monaco.languages.registerCompletionItemProvider("logql", {
155-
provideCompletionItems: () => {
156-
const suggestions = COMPLETIONS.map((label) => ({
157-
label,
158-
kind: monaco.languages.CompletionItemKind.Keyword,
159-
insertText: label,
160-
range: {
161-
startLineNumber: 1,
162-
endLineNumber: 1,
163-
startColumn: 1,
164-
endColumn: 1,
165-
},
166-
detail: "LogQL snippet",
167-
documentation: "LogQL snippet",
168-
sortText: label,
169-
filterText: label,
170-
}));
171-
return { suggestions };
172-
},
173-
});
174-
175-
const executeFromEditor = () =>
176-
runQuery(editorInstance.getValue() ?? "");
177-
const keybindings = [
178-
monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter,
179-
monaco.KeyMod.Shift | monaco.KeyCode.Enter,
180-
monaco.KeyMod.WinCtrl | monaco.KeyCode.Enter,
181-
monaco.KeyMod.CtrlCmd |
182-
monaco.KeyMod.Shift |
183-
monaco.KeyCode.Enter,
184-
];
185-
keybindings.forEach((binding) => {
186-
editorInstance.addCommand(binding, executeFromEditor);
187-
});
188-
}}
194+
value={logql ?? ''}
195+
options={editorOptions}
196+
onChange={setLogql}
197+
onMount={initEditorHandler}
189198
/>
190199
</CardContent>
191200
{error && (

cmd/logql-to-logsql/web/ui/src/components/logs-endpoint/LogsEndpoint.tsx

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
import { Input } from "@/components/ui/input.tsx";
1010
import { Label } from "@/components/ui/label.tsx";
1111
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group.tsx";
12+
import {type ChangeEvent, useCallback} from "react";
1213

1314
export interface LogsEndpointProps {
1415
readonly endpointUrl?: string;
@@ -19,7 +20,6 @@ export interface LogsEndpointProps {
1920
readonly onExecModeChange?: (mode: "translate" | "query") => void;
2021
readonly isLoading?: boolean;
2122
readonly endpointEnabled?: boolean;
22-
readonly onEndpointEnabledChange?: (enabled: boolean) => void;
2323
}
2424

2525
export function LogsEndpoint({
@@ -32,6 +32,27 @@ export function LogsEndpoint({
3232
isLoading,
3333
endpointEnabled,
3434
}: LogsEndpointProps) {
35+
const changeExecModeHandler = useCallback((value: 'translate' | 'query') => {
36+
if (
37+
onExecModeChange &&
38+
(value === "translate" || value === "query")
39+
) {
40+
onExecModeChange(value);
41+
}
42+
}, [onExecModeChange]);
43+
const endpointChangeHandler = useCallback((e: ChangeEvent<HTMLInputElement>) => {
44+
if (!onUrlChange) {
45+
return
46+
}
47+
onUrlChange(e.target.value)
48+
}, [onUrlChange])
49+
const tokenChangeHandler = useCallback((e: ChangeEvent<HTMLInputElement>) => {
50+
if (!onTokenChange) {
51+
return
52+
}
53+
onTokenChange(e.target.value)
54+
}, [onTokenChange])
55+
3556
return (
3657
<Card className={"w-full py-4 border-none shadow-none drop-shadow-none"}>
3758
<CardHeader>
@@ -44,14 +65,7 @@ export function LogsEndpoint({
4465
<ToggleGroup
4566
type="single"
4667
value={execMode}
47-
onValueChange={(value) => {
48-
if (
49-
onExecModeChange &&
50-
(value === "translate" || value === "query")
51-
) {
52-
onExecModeChange(value);
53-
}
54-
}}
68+
onValueChange={changeExecModeHandler}
5569
variant="outline"
5670
>
5771
<ToggleGroupItem value="translate" className={"cursor-pointer"}>
@@ -73,7 +87,7 @@ export function LogsEndpoint({
7387
value={endpointUrl}
7488
type={"url"}
7589
placeholder={"https://play-vmlogs.victoriametrics.com"}
76-
onChange={(e) => onUrlChange && onUrlChange(e.target.value)}
90+
onChange={endpointChangeHandler}
7791
/>
7892
</div>
7993
<div className={"flex flex-col gap-1 sm:w-1/4"}>
@@ -83,7 +97,7 @@ export function LogsEndpoint({
8397
id={"bearerToken"}
8498
value={bearerToken}
8599
type={"password"}
86-
onChange={(e) => onTokenChange && onTokenChange(e.target.value)}
100+
onChange={tokenChangeHandler}
87101
/>
88102
</div>
89103
</CardContent>

cmd/logql-to-logsql/web/ui/src/pages/main/Main.tsx

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@ const formatExecutionTime = (ms: number): string => {
2323
};
2424

2525
export function Main() {
26-
const [endpointEnabled, setEndpointEnabled] = useState<boolean>(true);
26+
const [endpointEnabled, setEndpointEnabled] = useState<boolean>(false);
2727
const [endpointUrl, setEndpointUrl] = useState<string>(
2828
"https://play-vmlogs.victoriametrics.com",
2929
);
3030
const [bearerToken, setBearerToken] = useState<string>("");
3131
const [results, setResults] = useState<unknown>();
3232
const [query, setQuery] = useState<string>("");
33-
const [loading, setLoading] = useState<boolean>(false);
33+
const [loading, setLoading] = useState<boolean>(true);
3434
const [error, setError] = useState<string>("");
3535
const [success, setSuccess] = useState<string>("");
3636
const [limit, setLimit] = useState<number>(0);
@@ -44,9 +44,18 @@ export function Main() {
4444
if (data.endpoint) {
4545
setEndpointUrl(data.endpoint);
4646
setEndpointEnabled(false);
47+
} else {
48+
setEndpointUrl('');
49+
setEndpointEnabled(true);
4750
}
4851
setLimit(data.limit || 0);
4952
setLoading(false);
53+
})
54+
.catch(error => {
55+
toast.error("config loading error:", {
56+
description: error?.error ?? error?.message ?? error,
57+
duration: 10000,
58+
});
5059
});
5160
}, []);
5261

@@ -55,42 +64,30 @@ export function Main() {
5564
setLoading(true);
5665
setError("");
5766
setSuccess("");
58-
59-
const reqBody: {
60-
logql: string;
61-
endpoint?: string;
62-
bearerToken?: string;
63-
start?: string;
64-
end?: string;
65-
execMode?: "translate" | "query";
66-
} = {
67-
logql,
68-
start: start ? `${start}` : undefined,
69-
end: end ? `${end}` : undefined,
70-
execMode: execMode,
71-
};
72-
if (endpointEnabled) {
73-
reqBody.endpoint = endpointUrl;
74-
reqBody.bearerToken = bearerToken;
75-
}
76-
7767
const execStart = performance.now();
7868
const resp = await fetch(`/api/v1/logql-to-logsql`, {
7969
method: "POST",
8070
headers: {
8171
"Content-Type": "application/json",
8272
Authorization: `Bearer ${bearerToken}`,
8373
},
84-
body: JSON.stringify(reqBody),
74+
body: JSON.stringify({
75+
logql,
76+
start: start ? `${start}` : undefined,
77+
end: end ? `${end}` : undefined,
78+
execMode: execMode,
79+
endpoint: endpointEnabled ? endpointUrl : undefined,
80+
bearerToken: endpointEnabled ? bearerToken : undefined,
81+
}),
8582
});
8683
const body = await resp.json();
8784
if (resp.status !== 200) {
88-
setError(body.error);
85+
setError(body.error ?? body.message ?? body);
8986
setResults(undefined);
9087
setQuery("");
9188
setLoading(false);
9289
toast.error("execute error:", {
93-
description: body.error,
90+
description: body.error ?? body.message ?? body,
9491
duration: 10000,
9592
});
9693
return;

0 commit comments

Comments
 (0)