From da93b67dd900c060ca9701c9780519f3114f682c Mon Sep 17 00:00:00 2001 From: Brian Harrington Date: Fri, 7 Oct 2022 09:57:14 -0500 Subject: [PATCH] add support for contains, starts, and ends Update to be consistent with recent changes to Atlas backend to support `:contains`, `:starts`, and `:ends` query operations. These get mapped to regex so it only impacts the query parsing. See: Netflix/atlas#1470 and Netflix/atlas#1471. --- .../netflix/spectator/atlas/impl/Parser.java | 17 +++++ .../spectator/atlas/impl/QueryTest.java | 62 ++++++++++++++++++- 2 files changed, 78 insertions(+), 1 deletion(-) diff --git a/spectator-reg-atlas/src/main/java/com/netflix/spectator/atlas/impl/Parser.java b/spectator-reg-atlas/src/main/java/com/netflix/spectator/atlas/impl/Parser.java index c2723d107..5713c3248 100644 --- a/spectator-reg-atlas/src/main/java/com/netflix/spectator/atlas/impl/Parser.java +++ b/spectator-reg-atlas/src/main/java/com/netflix/spectator/atlas/impl/Parser.java @@ -15,6 +15,8 @@ */ package com.netflix.spectator.atlas.impl; +import com.netflix.spectator.impl.matcher.PatternUtils; + import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; @@ -156,6 +158,21 @@ private static Object parse(String expr) { k = (String) stack.pop(); pushRegex(stack, new Query.Regex(k, v, true, ":reic")); break; + case ":contains": + v = (String) stack.pop(); + k = (String) stack.pop(); + pushRegex(stack, new Query.Regex(k, ".*" + PatternUtils.escape(v))); + break; + case ":starts": + v = (String) stack.pop(); + k = (String) stack.pop(); + pushRegex(stack, new Query.Regex(k, PatternUtils.escape(v))); + break; + case ":ends": + v = (String) stack.pop(); + k = (String) stack.pop(); + pushRegex(stack, new Query.Regex(k, ".*" + PatternUtils.escape(v) + "$")); + break; case ":all": q = (Query) stack.pop(); stack.push(new DataExpr.All(q)); diff --git a/spectator-reg-atlas/src/test/java/com/netflix/spectator/atlas/impl/QueryTest.java b/spectator-reg-atlas/src/test/java/com/netflix/spectator/atlas/impl/QueryTest.java index c87a806ca..a91345453 100644 --- a/spectator-reg-atlas/src/test/java/com/netflix/spectator/atlas/impl/QueryTest.java +++ b/spectator-reg-atlas/src/test/java/com/netflix/spectator/atlas/impl/QueryTest.java @@ -42,7 +42,10 @@ private Query parse(String expr) { Query q1 = Parser.parseQuery(expr); Query q2 = Parser.parseQuery(expr); Assertions.assertEquals(q1, q2); - Assertions.assertEquals(expr, q1.toString()); + + Query q3 = Parser.parseQuery(q1.toString()); + Assertions.assertEquals(q1, q3); + Assertions.assertEquals(q1.toString(), q3.toString()); return q1; } @@ -210,6 +213,63 @@ public void reEqualsContract() { .verify(); } + @Test + public void containsQuery() { + Query q = parse("name,foo,:contains"); + Assertions.assertTrue(q.matches(registry.createId("foo"))); + Assertions.assertTrue(q.matches(registry.createId("foo_"))); + Assertions.assertTrue(q.matches(registry.createId("_foo_"))); + Assertions.assertTrue(q.matches(registry.createId("_foo"))); + Assertions.assertFalse(q.matches(registry.createId("_Foo_"))); + } + + @Test + public void containsQueryEscape() { + Query q = parse("name,^$.?*+[](){}\\#&!%,:contains"); + Assertions.assertEquals( + "name,.*\\^\\$\\.\\?\\*\\+\\[\\]\\(\\)\\{\\}\\\\#&!%,:re", + q.toString()); + Assertions.assertTrue(q.matches(registry.createId("^$.?*+[](){}\\#&!%"))); + } + + @Test + public void startsQuery() { + Query q = parse("name,foo,:starts"); + Assertions.assertTrue(q.matches(registry.createId("foo"))); + Assertions.assertTrue(q.matches(registry.createId("foo_"))); + Assertions.assertFalse(q.matches(registry.createId("_foo_"))); + Assertions.assertFalse(q.matches(registry.createId("_foo"))); + Assertions.assertFalse(q.matches(registry.createId("Foo_"))); + } + + @Test + public void startsQueryEscape() { + Query q = parse("name,^$.?*+[](){}\\#&!%,:starts"); + Assertions.assertEquals( + "name,\\^\\$\\.\\?\\*\\+\\[\\]\\(\\)\\{\\}\\\\#&!%,:re", + q.toString()); + Assertions.assertTrue(q.matches(registry.createId("^$.?*+[](){}\\#&!%"))); + } + + @Test + public void endsQuery() { + Query q = parse("name,foo,:ends"); + Assertions.assertTrue(q.matches(registry.createId("foo"))); + Assertions.assertFalse(q.matches(registry.createId("foo_"))); + Assertions.assertFalse(q.matches(registry.createId("_foo_"))); + Assertions.assertTrue(q.matches(registry.createId("_foo"))); + Assertions.assertFalse(q.matches(registry.createId("_Foo"))); + } + + @Test + public void endsQueryEscape() { + Query q = parse("name,^$.?*+[](){}\\#&!%,:ends"); + Assertions.assertEquals( + "name,.*\\^\\$\\.\\?\\*\\+\\[\\]\\(\\)\\{\\}\\\\#&!%$,:re", + q.toString()); + Assertions.assertTrue(q.matches(registry.createId("^$.?*+[](){}\\#&!%"))); + } + @Test public void andQuery() { Query q = parse("name,foo,:eq,bar,baz,:eq,:and");