@@ -275,7 +277,7 @@ export const ApplicationsList = (props: RouteComponentProps<{}>) => {
health: newPref.healthFilter.join(','),
namespace: newPref.namespacesFilter.join(','),
cluster: newPref.clustersFilter.join(','),
- labels: newPref.labelsFilter.join(',')
+ labels: newPref.labelsFilter.map(encodeURIComponent).join(',')
});
}}
/>
diff --git a/ui/src/app/applications/components/applications-sync-panel/applications-sync-panel.tsx b/ui/src/app/applications/components/applications-sync-panel/applications-sync-panel.tsx
index af8b184e9f..97a13d14ec 100644
--- a/ui/src/app/applications/components/applications-sync-panel/applications-sync-panel.tsx
+++ b/ui/src/app/applications/components/applications-sync-panel/applications-sync-panel.tsx
@@ -20,7 +20,7 @@ export const ApplicationsSyncPanel = ({show, apps, hide}: {show: boolean; apps:
+ {' '}
diff --git a/ui/src/app/applications/components/label-selector.test.ts b/ui/src/app/applications/components/label-selector.test.ts
new file mode 100644
index 0000000000..3b436ed258
--- /dev/null
+++ b/ui/src/app/applications/components/label-selector.test.ts
@@ -0,0 +1,44 @@
+import * as LabelSelector from './label-selector';
+
+test('exists', () => {
+ expect(LabelSelector.match('test', {test: 'hello'})).toBeTruthy();
+ expect(LabelSelector.match('test1', {test: 'hello'})).toBeFalsy();
+});
+
+test('not exists', () => {
+ expect(LabelSelector.match('!test', {test: 'hello'})).toBeFalsy();
+ expect(LabelSelector.match('!test1', {test: 'hello'})).toBeTruthy();
+});
+
+test('in', () => {
+ expect(LabelSelector.match('test in 1, 2, 3', {test: '1'})).toBeTruthy();
+ expect(LabelSelector.match('test in 1, 2, 3', {test: '4'})).toBeFalsy();
+ expect(LabelSelector.match('test in 1, 2, 3', {test1: '1'})).toBeFalsy();
+});
+
+test('notIn', () => {
+ expect(LabelSelector.match('test notin 1, 2, 3', {test: '1'})).toBeFalsy();
+ expect(LabelSelector.match('test notin 1, 2, 3', {test: '4'})).toBeTruthy();
+ expect(LabelSelector.match('test notin 1, 2, 3', {test1: '1'})).toBeTruthy();
+});
+
+test('equal', () => {
+ expect(LabelSelector.match('test=hello', {test: 'hello'})).toBeTruthy();
+ expect(LabelSelector.match('test=world', {test: 'hello'})).toBeFalsy();
+ expect(LabelSelector.match('test==hello', {test: 'hello'})).toBeTruthy();
+});
+
+test('notEqual', () => {
+ expect(LabelSelector.match('test!=hello', {test: 'hello'})).toBeFalsy();
+ expect(LabelSelector.match('test!=world', {test: 'hello'})).toBeTruthy();
+});
+
+test('greaterThen', () => {
+ expect(LabelSelector.match('test gt 1', {test: '2'})).toBeTruthy();
+ expect(LabelSelector.match('test gt 3', {test: '2'})).toBeFalsy();
+});
+
+test('lessThen', () => {
+ expect(LabelSelector.match('test lt 1', {test: '2'})).toBeFalsy();
+ expect(LabelSelector.match('test lt 3', {test: '2'})).toBeTruthy();
+});
diff --git a/ui/src/app/applications/components/label-selector.ts b/ui/src/app/applications/components/label-selector.ts
new file mode 100644
index 0000000000..751ae6e418
--- /dev/null
+++ b/ui/src/app/applications/components/label-selector.ts
@@ -0,0 +1,39 @@
+type operatorFn = (labels: {[name: string]: string}, key: string, values: string[]) => boolean;
+
+const operators: {[type: string]: operatorFn} = {
+ '!=': (labels, key, values) => labels[key] !== values[0],
+ '==': (labels, key, values) => labels[key] === values[0],
+ '=': (labels, key, values) => labels[key] === values[0],
+ 'notin': (labels, key, values) => !values.includes(labels[key]),
+ 'in': (labels, key, values) => values.includes(labels[key]),
+ 'gt': (labels, key, values) => parseFloat(labels[key]) > parseFloat(values[0]),
+ 'lt': (labels, key, values) => parseFloat(labels[key]) < parseFloat(values[0])
+};
+
+function split(input: string, delimiter: string): string[] {
+ return input
+ .split(delimiter)
+ .map(part => part.trim())
+ .filter(part => part !== '');
+}
+
+export type LabelSelector = (labels: {[name: string]: string}) => boolean;
+
+export function parse(selector: string): LabelSelector {
+ for (const type of Object.keys(operators)) {
+ const operator = operators[type];
+ const parts = split(selector, type);
+ if (parts.length > 1) {
+ const values = split(parts[1], ',');
+ return labels => operator(labels, parts[0], values);
+ }
+ }
+ if (selector.startsWith('!')) {
+ return labels => !labels.hasOwnProperty(selector.slice(1));
+ }
+ return labels => labels.hasOwnProperty(selector);
+}
+
+export function match(selector: string, labels: {[name: string]: string}): boolean {
+ return parse(selector)(labels || {});
+}