From f7212f8fc494dec6cb7702fdd18f313c894d92eb Mon Sep 17 00:00:00 2001 From: victoria jeffrey Date: Tue, 21 May 2019 07:28:10 -0700 Subject: [PATCH] nodes: filtering by multiple tags (#377) * nodes: add a test for filtering by multiple tags Signed-off-by: Victoria Jeffrey * nodes: make the test pass! (filter by mult tags) Signed-off-by: Victoria Jeffrey * expect two nodes, not one Signed-off-by: Victoria Jeffrey * clean up the filtering logic a bit Signed-off-by: Victoria Jeffrey --- .../api/tests/30_jobs_docker_spec.rb | 2 +- .../nodemanager-service/pgdb/filtering.go | 29 +++++++++++++++++-- .../pgdb/nodes_integration_test.go | 27 +++++++++++++++++ 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/components/compliance-service/api/tests/30_jobs_docker_spec.rb b/components/compliance-service/api/tests/30_jobs_docker_spec.rb index bd31716db32..85170a4bf8e 100644 --- a/components/compliance-service/api/tests/30_jobs_docker_spec.rb +++ b/components/compliance-service/api/tests/30_jobs_docker_spec.rb @@ -664,7 +664,7 @@ def cleanup Common::Filter.new(key: "environment", values: ["trou"]), Common::Filter.new(key: "group", values: ["mak", "do"]) ]) - assert_equal(1, filtered_nodes_by_tags.total) + assert_equal(2, filtered_nodes_by_tags.total) end it "does not include deleted jobs in job list" do diff --git a/components/nodemanager-service/pgdb/filtering.go b/components/nodemanager-service/pgdb/filtering.go index 560f382ed60..459bc96da5e 100644 --- a/components/nodemanager-service/pgdb/filtering.go +++ b/components/nodemanager-service/pgdb/filtering.go @@ -38,6 +38,22 @@ func mergeFilters(mergeableFilters []*common.Filter) ([]common.Filter, error) { return filters, nil } +func handleTagFilters(tagFilters []common.Filter) (string, error) { + var tagConditions []string + for _, filter := range tagFilters { + tagKeyFilter := strings.TrimPrefix(filter.Key, "tags:") + newTagCondition, err := wherePatternMatchTags(tagKeyFilter, filter.Values, "t") + if err != nil { + return "", errors.Wrap(err, "buildWhereFilter error") + } + if filter.Exclude { + newTagCondition = fmt.Sprintf("NOT (%s)", newTagCondition) + } + tagConditions = append(tagConditions, newTagCondition) + } + return strings.Join(tagConditions, " OR "), nil +} + // Takes a filter map (should be validated for content) and table abbreviation and returns a wherefilter func buildWhereFilter(mergeableFilters []*common.Filter, tableAbbrev string, filterField map[string]string) (whereFilter string, err error) { if len(mergeableFilters) == 0 { @@ -50,12 +66,13 @@ func buildWhereFilter(mergeableFilters []*common.Filter, tableAbbrev string, fil } var conditions []string + var tagFilters []common.Filter for _, filter := range filters { var newCondition string var err error if strings.HasPrefix(filter.Key, "tags:") { - tagKeyFilter := strings.TrimPrefix(filter.Key, "tags:") - newCondition, err = wherePatternMatchTags(tagKeyFilter, filter.Values, "t") + tagFilters = append(tagFilters, filter) + continue } else { switch filterField[filter.Key] { case "": @@ -84,6 +101,14 @@ func buildWhereFilter(mergeableFilters []*common.Filter, tableAbbrev string, fil conditions = append(conditions, newCondition) } + if len(tagFilters) > 0 { + tagCondition, err := handleTagFilters(tagFilters) + if err != nil { + return "", errors.Wrap(err, "buildWhereFilter error building tags") + } + conditions = append(conditions, tagCondition) + } + whereFilter = fmt.Sprintf("WHERE (%s)", strings.Join(conditions, " AND ")) logrus.Debugf("buildWhereFilter, whereFilter=%s", whereFilter) return whereFilter, nil diff --git a/components/nodemanager-service/pgdb/nodes_integration_test.go b/components/nodemanager-service/pgdb/nodes_integration_test.go index fdb22802a5f..aef13de14f1 100644 --- a/components/nodemanager-service/pgdb/nodes_integration_test.go +++ b/components/nodemanager-service/pgdb/nodes_integration_test.go @@ -199,6 +199,33 @@ func (suite *NodesIntegrationSuite) TestGetNodesCanFilterByTags() { suite.Equal("Taco Node", fetchedNodes[0].Name) } +func (suite *NodesIntegrationSuite) TestGetNodesCanFilterByMultipleTags() { + _, err := suite.Database.AddNode(&nodes.Node{Name: "Taco Node", Manager: "automate", Tags: []*common.Kv{{Key: "tacos", Value: "yes"}}, TargetConfig: &nodes.TargetConfig{}}) + suite.Require().NoError(err) + + _, err = suite.Database.AddNode(&nodes.Node{Name: "Nacho Node", Manager: "automate", Tags: []*common.Kv{{Key: "nachos", Value: "yes"}}, TargetConfig: &nodes.TargetConfig{}}) + suite.Require().NoError(err) + + _, err = suite.Database.AddNode(&nodes.Node{Name: "No Nacho Node", Manager: "automate", Tags: []*common.Kv{{Key: "nachos", Value: "no"}}, TargetConfig: &nodes.TargetConfig{}}) + suite.Require().NoError(err) + + filter1 := &common.Filter{ + Key: "tacos", + Values: []string{"yes"}, + } + filter2 := &common.Filter{ + Key: "nachos", + Values: []string{"yes"}, + } + fetchedNodes, count, err := suite.Database.GetNodes("name", nodes.Query_ASC, 1, 100, []*common.Filter{filter1, filter2}) + suite.Require().NoError(err) + + suite.Equal(2, len(fetchedNodes)) + suite.Equal(&pgdb.TotalCount{Total: 2, Unreachable: 0, Reachable: 0, Unknown: 3}, count) + suite.Equal("Nacho Node", fetchedNodes[0].GetName()) + suite.Equal("Taco Node", fetchedNodes[1].GetName()) +} + func (suite *NodesIntegrationSuite) TestGetNodesCanFilterByProjects() { node1Id, err := suite.Database.AddNode(&nodes.Node{Name: "Taco Node", Projects: []string{"Favorite Food", "Taco Bell Menu"}}) suite.Require().NoError(err)