aboutsummaryrefslogtreecommitdiffstats
path: root/webui/src/pages
diff options
context:
space:
mode:
authorMichael Muré <batolettre@gmail.com>2021-05-24 09:47:55 +0200
committerGitHub <noreply@github.com>2021-05-24 09:47:55 +0200
commit9ded45fe65c9a3af37dc05015a78d5782002e249 (patch)
treea2da60f07da4d66e7d434826edaf212c592d7c19 /webui/src/pages
parent0cee27665b410dd22e3d075f7179c138bac166ab (diff)
parent7446a20db8907acedbd14ddcc6a99de577268c1c (diff)
downloadgit-bug-9ded45fe65c9a3af37dc05015a78d5782002e249.tar.gz
Merge pull request #658 from GlancingMind/fix-webui-quoted-filter-parameters
WebUI: Fix quoted filter strings
Diffstat (limited to 'webui/src/pages')
-rw-r--r--webui/src/pages/list/Filter.tsx44
-rw-r--r--webui/src/pages/list/FilterToolbar.tsx13
2 files changed, 32 insertions, 25 deletions
diff --git a/webui/src/pages/list/Filter.tsx b/webui/src/pages/list/Filter.tsx
index 3559b3ce..496fb3ba 100644
--- a/webui/src/pages/list/Filter.tsx
+++ b/webui/src/pages/list/Filter.tsx
@@ -35,52 +35,50 @@ const ITEM_HEIGHT = 48;
export type Query = { [key: string]: string[] };
function parse(query: string): Query {
- // TODO: extract the rest of the query?
const params: Query = {};
-
- // TODO: support escaping without quotes
- const re = /(\w+):([A-Za-z0-9-]+|(["'])(([^\3]|\\.)*)\3)+/g;
+ let re = new RegExp(/([^:\s]+)(:('[^']*'\S*|"[^"]*"\S*|\S*))?/, 'g');
let matches;
while ((matches = re.exec(query)) !== null) {
if (!params[matches[1]]) {
params[matches[1]] = [];
}
-
- let value;
- if (matches[4]) {
- value = matches[4];
+ if (matches[3] !== undefined) {
+ params[matches[1]].push(matches[3]);
} else {
- value = matches[2];
+ params[matches[1]].push('');
}
- value = value.replace(/\\(.)/g, '$1');
- params[matches[1]].push(value);
}
return params;
}
function quote(value: string): string {
- const hasSingle = value.includes("'");
- const hasDouble = value.includes('"');
const hasSpaces = value.includes(' ');
- if (!hasSingle && !hasDouble && !hasSpaces) {
- return value;
- }
+ const isSingleQuotedRegEx = RegExp(/^'.*'$/);
+ const isDoubleQuotedRegEx = RegExp(/^".*"$/);
+ const isQuoted = () =>
+ isDoubleQuotedRegEx.test(value) || isSingleQuotedRegEx.test(value);
- if (!hasDouble) {
- return `"${value}"`;
+ //Test if label name contains whitespace between quotes. If no quoates but
+ //whitespace, then quote string.
+ if (!isQuoted() && hasSpaces) {
+ value = `"${value}"`;
}
- if (!hasSingle) {
- return `'${value}'`;
+ //Convert single quote (tick) to double quote. This way quoting is always
+ //uniform and can be relied upon by the label menu
+ const hasSingle = value.includes(`'`);
+ if (hasSingle) {
+ value = value.replace(/'/g, `"`);
}
- value = value.replace(/"/g, '\\"');
- return `"${value}"`;
+ return value;
}
function stringify(params: Query): string {
const parts: string[][] = Object.entries(params).map(([key, values]) => {
- return values.map((value) => `${key}:${quote(value)}`);
+ return values.map((value) =>
+ value.length > 0 ? `${key}:${quote(value)}` : key
+ );
});
return new Array<string>().concat(...parts).join(' ');
}
diff --git a/webui/src/pages/list/FilterToolbar.tsx b/webui/src/pages/list/FilterToolbar.tsx
index e109578d..4ac579f5 100644
--- a/webui/src/pages/list/FilterToolbar.tsx
+++ b/webui/src/pages/list/FilterToolbar.tsx
@@ -56,6 +56,16 @@ function CountingFilter({ query, children, ...props }: CountingFilterProps) {
);
}
+function quoteLabel(value: string) {
+ const hasUnquotedColon = RegExp(/^[^'"].*:.*[^'"]$/);
+ if (hasUnquotedColon.test(value)) {
+ //quote values which contain a colon but are not quoted.
+ //E.g. abc:abc becomes "abc:abc"
+ return `"${value}"`;
+ }
+ return value;
+}
+
type Props = {
query: string;
queryLocation: (query: string) => LocationDescriptor;
@@ -87,7 +97,7 @@ function FilterToolbar({ query, queryLocation }: Props) {
labelsData.repository.validLabels.nodes
) {
labels = labelsData.repository.validLabels.nodes.map((node) => [
- node.name,
+ quoteLabel(node.name),
node.name,
node.color,
]);
@@ -131,7 +141,6 @@ function FilterToolbar({ query, queryLocation }: Props) {
[key]: [],
});
- // TODO: author/label filters
return (
<Toolbar className={classes.toolbar}>
<CountingFilter