mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-12-13 13:11:37 +01:00
vmui: fix auto-completion triggers (#6566)
### Describe Your Changes
- Fixes auto-complete triggers according to [these
comments](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5866#issuecomment-2065273421).
- Fixes loading and displaying suggestions when there is no metric in
the expression.
Related issue: #6153
- Adds quotes when inserting label values.
Related issue: #6260
- [x] My change adheres [VictoriaMetrics contributing
guidelines](https://docs.victoriametrics.com/contributing/).
(cherry picked from commit 53919327b2
)
This commit is contained in:
parent
9b543a1394
commit
7d37ca3159
@ -34,14 +34,25 @@ const QueryEditorAutocomplete: FC<QueryEditorAutocompleteProps> = ({
|
|||||||
}, [value, caretPosition]);
|
}, [value, caretPosition]);
|
||||||
|
|
||||||
const exprLastPart = useMemo(() => {
|
const exprLastPart = useMemo(() => {
|
||||||
const parts = values.beforeCursor.split("}");
|
const regexpSplit = /\s(or|and|unless|default|ifnot|if|group_left|group_right)\s|}|\+|\|-|\*|\/|\^/i;
|
||||||
|
const parts = values.beforeCursor.split(regexpSplit);
|
||||||
return parts[parts.length - 1];
|
return parts[parts.length - 1];
|
||||||
}, [values]);
|
}, [values]);
|
||||||
|
|
||||||
const metric = useMemo(() => {
|
const metric = useMemo(() => {
|
||||||
const regexp = /\b[^{}(),\s]+(?={|$)/g;
|
const regex1 = /\w+\((?<metricName>[^)]+)\)\s+(by|without|on|ignoring)\s*\(\w*/gi;
|
||||||
const match = exprLastPart.match(regexp);
|
const matchAlt = [...exprLastPart.matchAll(regex1)];
|
||||||
return match ? match[0] : "";
|
if (matchAlt.length > 0 && matchAlt[0].groups && matchAlt[0].groups.metricName) {
|
||||||
|
return matchAlt[0].groups.metricName;
|
||||||
|
}
|
||||||
|
|
||||||
|
const regex2 = /^\s*\b(?<metricName>[^{}(),\s]+)(?={|$)/g;
|
||||||
|
const match = [...exprLastPart.matchAll(regex2)];
|
||||||
|
if (match.length > 0 && match[0].groups && match[0].groups.metricName) {
|
||||||
|
return match[0].groups.metricName;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
}, [exprLastPart]);
|
}, [exprLastPart]);
|
||||||
|
|
||||||
const label = useMemo(() => {
|
const label = useMemo(() => {
|
||||||
@ -51,7 +62,7 @@ const QueryEditorAutocomplete: FC<QueryEditorAutocompleteProps> = ({
|
|||||||
}, [exprLastPart]);
|
}, [exprLastPart]);
|
||||||
|
|
||||||
const shouldSuppressAutoSuggestion = (value: string) => {
|
const shouldSuppressAutoSuggestion = (value: string) => {
|
||||||
const pattern = /([{(),+\-*/^]|\b(?:or|and|unless|default|ifnot|if|group_left|group_right)\b)/;
|
const pattern = /([{(),+\-*/^]|\b(?:or|and|unless|default|ifnot|if|group_left|group_right|by|without|on|ignoring)\b)/i;
|
||||||
const parts = value.split(/\s+/);
|
const parts = value.split(/\s+/);
|
||||||
const partsCount = parts.length;
|
const partsCount = parts.length;
|
||||||
const lastPart = parts[partsCount - 1];
|
const lastPart = parts[partsCount - 1];
|
||||||
@ -63,12 +74,16 @@ const QueryEditorAutocomplete: FC<QueryEditorAutocompleteProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const context = useMemo(() => {
|
const context = useMemo(() => {
|
||||||
if (!values.beforeCursor || values.beforeCursor.endsWith("}") || shouldSuppressAutoSuggestion(values.beforeCursor)) {
|
const valueBeforeCursor = values.beforeCursor.trim();
|
||||||
|
const endOfClosedBrackets = ["}", ")"].some(char => valueBeforeCursor.endsWith(char));
|
||||||
|
const endOfClosedQuotes = !hasUnclosedQuotes(valueBeforeCursor) && ["`", "'", "\""].some(char => valueBeforeCursor.endsWith(char));
|
||||||
|
if (!values.beforeCursor || endOfClosedBrackets || endOfClosedQuotes || shouldSuppressAutoSuggestion(values.beforeCursor)) {
|
||||||
return QueryContextType.empty;
|
return QueryContextType.empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
const labelRegexp = /\{[^}]*$/;
|
const labelRegexp = /(?:by|without|on|ignoring)\s*\(\s*[^)]*$|\{[^}]*$/i;
|
||||||
const labelValueRegexp = new RegExp(`(${escapeRegexp(metric)})?{?.+${escapeRegexp(label)}(=|!=|=~|!~)"?([^"]*)$`, "g");
|
const patternLabelValue = `(${escapeRegexp(metric)})?{?.+${escapeRegexp(label)}(=|!=|=~|!~)"?([^"]*)$`;
|
||||||
|
const labelValueRegexp = new RegExp(patternLabelValue, "g");
|
||||||
|
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case labelValueRegexp.test(values.beforeCursor):
|
case labelValueRegexp.test(values.beforeCursor):
|
||||||
@ -81,7 +96,7 @@ const QueryEditorAutocomplete: FC<QueryEditorAutocompleteProps> = ({
|
|||||||
}, [values, metric, label]);
|
}, [values, metric, label]);
|
||||||
|
|
||||||
const valueByContext = useMemo(() => {
|
const valueByContext = useMemo(() => {
|
||||||
const wordMatch = values.beforeCursor.match(/([\w_\-.:/]+(?![},]))$/);
|
const wordMatch = values.beforeCursor.match(/([\w_.:]+(?![},]))$/);
|
||||||
return wordMatch ? wordMatch[0] : "";
|
return wordMatch ? wordMatch[0] : "";
|
||||||
}, [values.beforeCursor]);
|
}, [values.beforeCursor]);
|
||||||
|
|
||||||
@ -119,9 +134,10 @@ const QueryEditorAutocomplete: FC<QueryEditorAutocompleteProps> = ({
|
|||||||
// Add quotes around the value if the context is labelValue
|
// Add quotes around the value if the context is labelValue
|
||||||
if (context === QueryContextType.labelValue) {
|
if (context === QueryContextType.labelValue) {
|
||||||
const quote = "\"";
|
const quote = "\"";
|
||||||
const needsQuote = /(?:=|!=|=~|!~)$/.test(beforeValueByContext);
|
|
||||||
valueAfterCursor = valueAfterCursor.replace(/^[^\s"|},]*/, "");
|
valueAfterCursor = valueAfterCursor.replace(/^[^\s"|},]*/, "");
|
||||||
insert = `${needsQuote ? quote : ""}${insert}`;
|
const needsOpenQuote = /(?:=|!=|=~|!~)$/.test(beforeValueByContext);
|
||||||
|
const needsCloseQuote = valueAfterCursor.trim()[0] !== "\"";
|
||||||
|
insert = `${needsOpenQuote ? quote : ""}${insert}${needsCloseQuote ? quote : ""}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context === QueryContextType.label) {
|
if (context === QueryContextType.label) {
|
||||||
|
@ -137,7 +137,7 @@ export const useFetchQueryOptions = ({ valueByContext, metric, label, context }:
|
|||||||
|
|
||||||
// fetch labels
|
// fetch labels
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!serverUrl || !metric || context !== QueryContextType.label) {
|
if (!serverUrl || context !== QueryContextType.label) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setLabels([]);
|
setLabels([]);
|
||||||
@ -149,7 +149,7 @@ export const useFetchQueryOptions = ({ valueByContext, metric, label, context }:
|
|||||||
urlSuffix: "labels",
|
urlSuffix: "labels",
|
||||||
setter: setLabels,
|
setter: setLabels,
|
||||||
type: TypeData.label,
|
type: TypeData.label,
|
||||||
params: getQueryParams({ "match[]": `{__name__="${metricEscaped}"}` })
|
params: getQueryParams(metric ? { "match[]": `{__name__="${metricEscaped}"}` } : undefined)
|
||||||
});
|
});
|
||||||
|
|
||||||
return () => abortControllerRef.current?.abort();
|
return () => abortControllerRef.current?.abort();
|
||||||
@ -157,20 +157,23 @@ export const useFetchQueryOptions = ({ valueByContext, metric, label, context }:
|
|||||||
|
|
||||||
// fetch labelValues
|
// fetch labelValues
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!serverUrl || !metric || !label || context !== QueryContextType.labelValue) {
|
if (!serverUrl || !label || context !== QueryContextType.labelValue) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setLabelValues([]);
|
setLabelValues([]);
|
||||||
|
|
||||||
const metricEscaped = escapeDoubleQuotes(metric);
|
const metricEscaped = escapeDoubleQuotes(metric);
|
||||||
const valueReEscaped = escapeDoubleQuotes(escapeRegexp(value));
|
const valueReEscaped = escapeDoubleQuotes(escapeRegexp(value));
|
||||||
|
const matchMetric = metric ? `__name__="${metricEscaped}"` : "";
|
||||||
|
const matchLabel = `${label}=~".*${valueReEscaped}.*"`;
|
||||||
|
const matchValue = [matchMetric, matchLabel].filter(Boolean).join(",");
|
||||||
|
|
||||||
fetchData({
|
fetchData({
|
||||||
value,
|
value,
|
||||||
urlSuffix: `label/${label}/values`,
|
urlSuffix: `label/${label}/values`,
|
||||||
setter: setLabelValues,
|
setter: setLabelValues,
|
||||||
type: TypeData.labelValue,
|
type: TypeData.labelValue,
|
||||||
params: getQueryParams({ "match[]": `{__name__="${metricEscaped}", ${label}=~".*${valueReEscaped}.*"}` })
|
params: getQueryParams({ "match[]": `{${matchValue}}` })
|
||||||
});
|
});
|
||||||
|
|
||||||
return () => abortControllerRef.current?.abort();
|
return () => abortControllerRef.current?.abort();
|
||||||
|
@ -8,6 +8,6 @@ export const escapeDoubleQuotes = (s: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const hasUnclosedQuotes = (str: string) => {
|
export const hasUnclosedQuotes = (str: string) => {
|
||||||
const matches = str.match(/"/g);
|
const matches = str.match(/["`']/g);
|
||||||
return matches ? matches.length % 2 !== 0 : false;
|
return matches ? matches.length % 2 !== 0 : false;
|
||||||
};
|
};
|
||||||
|
@ -40,6 +40,8 @@ See also [LTS releases](https://docs.victoriametrics.com/lts-releases/).
|
|||||||
* BUGFIX: all VictoriaMetrics components: validate files specified via `-tlsKeyFile` and `-tlsCertFile` cmd-line flags on the process start-up. Previously, validation happened on the first connection accepted by HTTP server. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/6608) for the details. Thanks to @yincongcyincong for the [pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/6621).
|
* BUGFIX: all VictoriaMetrics components: validate files specified via `-tlsKeyFile` and `-tlsCertFile` cmd-line flags on the process start-up. Previously, validation happened on the first connection accepted by HTTP server. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/6608) for the details. Thanks to @yincongcyincong for the [pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/6621).
|
||||||
* BUGFIX: [vmauth](https://docs.victoriametrics.com/vmauth/): properly proxy requests to backend urls ending with `/` if the original request path equals to `/`. Previously the trailing `/` at the backend path was incorrectly removed. For example, if the request to `http://vmauth/` is configured to be proxied to `url_prefix=http://backend/foo/`, then it was proxied to `http://backend/foo`, while it should go to `http://backend/foo/`.
|
* BUGFIX: [vmauth](https://docs.victoriametrics.com/vmauth/): properly proxy requests to backend urls ending with `/` if the original request path equals to `/`. Previously the trailing `/` at the backend path was incorrectly removed. For example, if the request to `http://vmauth/` is configured to be proxied to `url_prefix=http://backend/foo/`, then it was proxied to `http://backend/foo`, while it should go to `http://backend/foo/`.
|
||||||
* BUGFIX: [vmauth](https://docs.victoriametrics.com/vmauth/): fix `cannot read data after closing the reader` error when proxying HTTP requests without body (aka `GET` requests). The issue has been introduced in [v1.102.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.102.0) in [this commit](https://github.com/VictoriaMetrics/VictoriaMetrics/commit/7ee57974935a662896f2de40fdf613156630617d).
|
* BUGFIX: [vmauth](https://docs.victoriametrics.com/vmauth/): fix `cannot read data after closing the reader` error when proxying HTTP requests without body (aka `GET` requests). The issue has been introduced in [v1.102.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.102.0) in [this commit](https://github.com/VictoriaMetrics/VictoriaMetrics/commit/7ee57974935a662896f2de40fdf613156630617d).
|
||||||
|
* BUGFIX: [vmui](https://docs.victoriametrics.com/#vmui): fix auto-completion triggers for metric and label names, and disable it after specific characters. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/6153) and [these comments](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5866#issuecomment-2065273421).
|
||||||
|
* BUGFIX: [vmui](https://docs.victoriametrics.com/#vmui): fix automatic addition of quotes when inserting label values from autocomplete suggestions. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/6260).
|
||||||
|
|
||||||
## [v1.102.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.102.0)
|
## [v1.102.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.102.0)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user