Adds option to prepared queries to remove empty tags. (#3330)
This commit is contained in:
parent
f4daf8a84c
commit
6b51744ddf
|
@ -29,6 +29,10 @@ type CompiledTemplate struct {
|
|||
|
||||
// re is the compiled regexp, if they supplied one (this can be nil).
|
||||
re *regexp.Regexp
|
||||
|
||||
// removeEmptyTags will cause the service tags to be stripped of any
|
||||
// empty strings after interpolation.
|
||||
removeEmptyTags bool
|
||||
}
|
||||
|
||||
// Compile validates a prepared query template and returns an opaque compiled
|
||||
|
@ -41,7 +45,8 @@ func Compile(query *structs.PreparedQuery) (*CompiledTemplate, error) {
|
|||
|
||||
// Start compile.
|
||||
ct := &CompiledTemplate{
|
||||
trees: make(map[string]ast.Node),
|
||||
trees: make(map[string]ast.Node),
|
||||
removeEmptyTags: query.Template.RemoveEmptyTags,
|
||||
}
|
||||
|
||||
// Make a copy of the query to use as the basis for rendering later.
|
||||
|
@ -181,5 +186,15 @@ func (ct *CompiledTemplate) Render(name string) (*structs.PreparedQuery, error)
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if ct.removeEmptyTags {
|
||||
tags := make([]string, 0, len(query.Service.Tags))
|
||||
for _, tag := range query.Service.Tags {
|
||||
if tag != "" {
|
||||
tags = append(tags, tag)
|
||||
}
|
||||
}
|
||||
query.Service.Tags = tags
|
||||
}
|
||||
|
||||
return query, nil
|
||||
}
|
||||
|
|
|
@ -15,8 +15,9 @@ var (
|
|||
bigBench = &structs.PreparedQuery{
|
||||
Name: "hello",
|
||||
Template: structs.QueryTemplateOptions{
|
||||
Type: structs.QueryTemplateTypeNamePrefixMatch,
|
||||
Regexp: "^hello-(.*)-(.*)$",
|
||||
Type: structs.QueryTemplateTypeNamePrefixMatch,
|
||||
Regexp: "^hello-(.*)-(.*)$",
|
||||
RemoveEmptyTags: true,
|
||||
},
|
||||
Service: structs.ServiceQuery{
|
||||
Service: "${name.full}",
|
||||
|
@ -296,4 +297,84 @@ func TestTemplate_Render(t *testing.T) {
|
|||
t.Fatalf("bad: %#v", actual)
|
||||
}
|
||||
}
|
||||
|
||||
// Try all the variables and functions, removing empty tags.
|
||||
query = &structs.PreparedQuery{
|
||||
Name: "hello-",
|
||||
Template: structs.QueryTemplateOptions{
|
||||
Type: structs.QueryTemplateTypeNamePrefixMatch,
|
||||
Regexp: "^(.*?)-(.*?)-(.*)$",
|
||||
RemoveEmptyTags: true,
|
||||
},
|
||||
Service: structs.ServiceQuery{
|
||||
Service: "${name.prefix} xxx ${name.full} xxx ${name.suffix}",
|
||||
Tags: []string{
|
||||
"${match(-1)}",
|
||||
"${match(0)}",
|
||||
"${match(1)}",
|
||||
"${match(2)}",
|
||||
"${match(3)}",
|
||||
"${match(4)}",
|
||||
"${40 + 2}",
|
||||
},
|
||||
},
|
||||
}
|
||||
ct, err = Compile(query)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Run a case that matches the regexp, removing empty tags.
|
||||
{
|
||||
actual, err := ct.Render("hello-foo-bar-none")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
expected := &structs.PreparedQuery{
|
||||
Name: "hello-",
|
||||
Template: structs.QueryTemplateOptions{
|
||||
Type: structs.QueryTemplateTypeNamePrefixMatch,
|
||||
Regexp: "^(.*?)-(.*?)-(.*)$",
|
||||
RemoveEmptyTags: true,
|
||||
},
|
||||
Service: structs.ServiceQuery{
|
||||
Service: "hello- xxx hello-foo-bar-none xxx foo-bar-none",
|
||||
Tags: []string{
|
||||
"hello-foo-bar-none",
|
||||
"hello",
|
||||
"foo",
|
||||
"bar-none",
|
||||
"42",
|
||||
},
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Fatalf("bad:\n%#v\nexpected:\n%#v\n", actual, expected)
|
||||
}
|
||||
}
|
||||
|
||||
// Run a case that doesn't match the regexp, removing empty tags.
|
||||
{
|
||||
actual, err := ct.Render("hello-nope")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
expected := &structs.PreparedQuery{
|
||||
Name: "hello-",
|
||||
Template: structs.QueryTemplateOptions{
|
||||
Type: structs.QueryTemplateTypeNamePrefixMatch,
|
||||
Regexp: "^(.*?)-(.*?)-(.*)$",
|
||||
RemoveEmptyTags: true,
|
||||
},
|
||||
Service: structs.ServiceQuery{
|
||||
Service: "hello- xxx hello-nope xxx nope",
|
||||
Tags: []string{
|
||||
"42",
|
||||
},
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Fatalf("bad: %#v", actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,6 +68,9 @@ type QueryTemplateOptions struct {
|
|||
// used to extract parts of the name and choose a service name, set
|
||||
// tags, etc.
|
||||
Regexp string
|
||||
|
||||
// RemoveEmptyTags, if true, removes empty tags from matched tag list
|
||||
RemoveEmptyTags bool
|
||||
}
|
||||
|
||||
// PreparedQuery defines a complete prepared query, and is the structure we
|
||||
|
|
|
@ -32,7 +32,8 @@ Here is an example prepared query template:
|
|||
{
|
||||
"Template": {
|
||||
"Type": "name_prefix_match",
|
||||
"Regexp": "^geo-db-(.*?)-([^\\-]+?)$"
|
||||
"Regexp": "^geo-db-(.*?)-([^\\-]+?)$",
|
||||
"RemoveEmptyTags": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -55,6 +56,12 @@ static query. It has two fields:
|
|||
[RE2](https://github.com/google/re2/wiki/Syntax) reference for syntax of this
|
||||
regular expression.
|
||||
|
||||
- `RemoveEmptyTags` is optional, and if set to true, will cause the `Tags` list
|
||||
inside the `Service` structure to be stripped of any empty strings. This defaults
|
||||
to false, meaning that empty strings will remain in the list. This is useful
|
||||
when interpolating into tags in a way where the tag is optional, and where
|
||||
searching for an empty tag would yield no results from the query.
|
||||
|
||||
All other fields of the query have the same meanings as for a static query,
|
||||
except that several interpolation variables are available to dynamically
|
||||
populate the query before it is executed. All of the string fields inside the
|
||||
|
|
Loading…
Reference in New Issue