From eda09bf4ff7b302f9af9fcc28f40e742960bb4c6 Mon Sep 17 00:00:00 2001 From: Kanstantsin Buklis Date: Fri, 6 Mar 2026 15:18:12 +0100 Subject: [PATCH] feat(vulnerability): add filter by region --- internal/api/graphql/gqlgen.yml | 2 + .../api/graphql/graph/baseResolver/common.go | 1 + .../api/graphql/graph/baseResolver/issue.go | 1 + .../graph/baseResolver/vulnerability.go | 4 ++ .../vulnerabilityFilter/region.graphql | 12 +++++ .../graph/resolver/vulnerability_filter.go | 20 ++++++++ .../api/graphql/graph/schema/issue.graphqls | 1 + .../graph/schema/vulnerability.graphqls | 2 +- .../schema/vulnerability_filter.graphqls | 1 + internal/database/mariadb/issue.go | 11 ++-- internal/database/mariadb/issue_test.go | 21 ++++++++ .../20260211213443_extend_mv_schedules.up.sql | 2 +- ...55_add_count_issue_ratings_region.down.sql | 8 +++ ...1355_add_count_issue_ratings_region.up.sql | 51 +++++++++++++++++++ .../database/mariadb/mv_vulnerabilities.go | 7 +++ .../mariadb/mv_vulnerabilities_test.go | 17 +++++++ internal/database/mariadb/test/common.go | 18 ++++--- internal/database/mariadb/test/fixture.go | 8 +++ .../issue_counts/issue_counts_per_region.json | 20 ++++++++ .../e2e/vulnerability_filter_query_test.go | 32 ++++++++++++ internal/entity/issue.go | 1 + 21 files changed, 225 insertions(+), 15 deletions(-) create mode 100644 internal/api/graphql/graph/queryCollection/vulnerabilityFilter/region.graphql create mode 100644 internal/database/mariadb/migrations/20260304131355_add_count_issue_ratings_region.down.sql create mode 100644 internal/database/mariadb/migrations/20260304131355_add_count_issue_ratings_region.up.sql create mode 100644 internal/database/mariadb/testdata/issue_counts/issue_counts_per_region.json diff --git a/internal/api/graphql/gqlgen.yml b/internal/api/graphql/gqlgen.yml index 837a5d021..c748fbc19 100644 --- a/internal/api/graphql/gqlgen.yml +++ b/internal/api/graphql/gqlgen.yml @@ -206,6 +206,8 @@ models: resolver: false service: resolver: true + region: + resolver: true IssueMatchFilterValue: fields: status: diff --git a/internal/api/graphql/graph/baseResolver/common.go b/internal/api/graphql/graph/baseResolver/common.go index 6f70e0f57..f085b0287 100644 --- a/internal/api/graphql/graph/baseResolver/common.go +++ b/internal/api/graphql/graph/baseResolver/common.go @@ -67,6 +67,7 @@ var ( VulnerabilityFilterSupportGroup string = "supportGroup" VulnerabilityFilterSeverity string = "severity" VulnerabilityFilterService string = "service" + VulnerabilityFilterRegion string = "region" ) type ResolverError struct { diff --git a/internal/api/graphql/graph/baseResolver/issue.go b/internal/api/graphql/graph/baseResolver/issue.go index 8dbd34da3..1960e5565 100644 --- a/internal/api/graphql/graph/baseResolver/issue.go +++ b/internal/api/graphql/graph/baseResolver/issue.go @@ -271,6 +271,7 @@ func IssueCountsBaseResolver(app app.Heureka, ctx context.Context, filter *model Paginated: entity.Paginated{}, ServiceCCRN: filter.ServiceCcrn, SupportGroupCCRN: filter.SupportGroupCcrn, + Region: filter.Region, PrimaryName: filter.PrimaryName, Type: lo.Map(filter.IssueType, func(item *model.IssueTypes, _ int) *string { return pointer.String(item.String()) }), Search: filter.Search, diff --git a/internal/api/graphql/graph/baseResolver/vulnerability.go b/internal/api/graphql/graph/baseResolver/vulnerability.go index 02f1fabbd..56143feaa 100644 --- a/internal/api/graphql/graph/baseResolver/vulnerability.go +++ b/internal/api/graphql/graph/baseResolver/vulnerability.go @@ -32,6 +32,7 @@ func VulnerabilityBaseResolver(app app.Heureka, ctx context.Context, filter *mod f := &entity.IssueFilter{ Paginated: entity.Paginated{First: first, After: after}, SupportGroupCCRN: filter.SupportGroup, + Region: filter.Region, ServiceCCRN: filter.Service, Type: []*string{lo.ToPtr(entity.IssueTypeVulnerability.String())}, Search: filter.Search, @@ -101,15 +102,18 @@ func VulnerabilityBaseResolver(app app.Heureka, ctx context.Context, filter *mod IssueMatchStatus: []*model.IssueMatchStatusValues{lo.ToPtr(model.IssueMatchStatusValuesNew)}, Search: filter.Search, ServiceCcrn: filter.Service, + Region: filter.Region, ComponentVersionID: util.ConvertIntToStrSlice(f.ComponentVersionId), AllServices: lo.ToPtr(true), } + severityCounts, err := IssueCountsBaseResolver(app, ctx, icFilter, &model.NodeParent{ ParentName: model.VulnerabilityNodeName, }) if err != nil { return nil, NewResolverError("VulnerabilityBaseResolver", err.Error()) } + connection.Counts = severityCounts } diff --git a/internal/api/graphql/graph/queryCollection/vulnerabilityFilter/region.graphql b/internal/api/graphql/graph/queryCollection/vulnerabilityFilter/region.graphql new file mode 100644 index 000000000..792d6c952 --- /dev/null +++ b/internal/api/graphql/graph/queryCollection/vulnerabilityFilter/region.graphql @@ -0,0 +1,12 @@ +# SPDX-FileCopyrightText: 2026 SAP SE or an SAP affiliate company and Greenhouse contributors +# SPDX-License-Identifier: Apache-2.0 + +query{ + VulnerabilityFilterValues{ + region { + displayName + filterName + values + } + } +} diff --git a/internal/api/graphql/graph/resolver/vulnerability_filter.go b/internal/api/graphql/graph/resolver/vulnerability_filter.go index 422f2b4f6..f01e60001 100644 --- a/internal/api/graphql/graph/resolver/vulnerability_filter.go +++ b/internal/api/graphql/graph/resolver/vulnerability_filter.go @@ -11,6 +11,7 @@ import ( "github.com/cloudoperators/heureka/internal/api/graphql/graph" "github.com/cloudoperators/heureka/internal/api/graphql/graph/baseResolver" "github.com/cloudoperators/heureka/internal/api/graphql/graph/model" + "github.com/samber/lo" ) // SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and Greenhouse contributors @@ -34,6 +35,25 @@ func (r *vulnerabilityFilterValueResolver) Service(ctx context.Context, obj *mod return item, err } +func (r *vulnerabilityFilterValueResolver) Region(ctx context.Context, obj *model.VulnerabilityFilterValue) (*model.FilterItem, error) { + item, err := baseResolver.RegionBaseResolver(r.App, ctx, nil) + if err != nil { + return nil, err + } + + item.Values = lo.UniqBy(item.Values, func(s *string) string { + if s != nil { + return *s + } + + return "" + }) + + item.FilterName = &baseResolver.VulnerabilityFilterRegion + + return item, err +} + func (r *Resolver) VulnerabilityFilterValue() graph.VulnerabilityFilterValueResolver { return &vulnerabilityFilterValueResolver{r} } diff --git a/internal/api/graphql/graph/schema/issue.graphqls b/internal/api/graphql/graph/schema/issue.graphqls index f25fad542..99b5a10e4 100644 --- a/internal/api/graphql/graph/schema/issue.graphqls +++ b/internal/api/graphql/graph/schema/issue.graphqls @@ -56,6 +56,7 @@ enum IssueStatusValues { input IssueFilter { serviceCcrn: [String], supportGroupCcrn: [String], + region: [String], primaryName: [String], issueMatchStatus: [IssueMatchStatusValues], issueType: [IssueTypes], diff --git a/internal/api/graphql/graph/schema/vulnerability.graphqls b/internal/api/graphql/graph/schema/vulnerability.graphqls index 9d0646220..2d9269022 100644 --- a/internal/api/graphql/graph/schema/vulnerability.graphqls +++ b/internal/api/graphql/graph/schema/vulnerability.graphqls @@ -30,11 +30,11 @@ enum VulnerabilityStatus { all } - input VulnerabilityFilter { severity: [SeverityValues] service: [String] supportGroup: [String] + region: [String] name: [String] search: [String] status: VulnerabilityStatus diff --git a/internal/api/graphql/graph/schema/vulnerability_filter.graphqls b/internal/api/graphql/graph/schema/vulnerability_filter.graphqls index e1c082d6e..9f335cc1e 100644 --- a/internal/api/graphql/graph/schema/vulnerability_filter.graphqls +++ b/internal/api/graphql/graph/schema/vulnerability_filter.graphqls @@ -5,4 +5,5 @@ type VulnerabilityFilterValue { supportGroup: FilterItem severity: FilterItem service: FilterItem + region: FilterItem } diff --git a/internal/database/mariadb/issue.go b/internal/database/mariadb/issue.go index e21a6d01e..5f3328762 100644 --- a/internal/database/mariadb/issue.go +++ b/internal/database/mariadb/issue.go @@ -25,6 +25,7 @@ var issueObject = DbObject{ FilterProperties: []*FilterProperty{ NewFilterProperty("S.service_ccrn = ?", WrapRetSlice(func(filter *entity.IssueFilter) []*string { return filter.ServiceCCRN })), NewFilterProperty("CI.componentinstance_service_id = ?", WrapRetSlice(func(filter *entity.IssueFilter) []*int64 { return filter.ServiceId })), + NewFilterProperty("CI.componentinstance_region = ?", WrapRetSlice(func(filter *entity.IssueFilter) []*string { return filter.Region })), NewFilterProperty("I.issue_id = ?", WrapRetSlice(func(filter *entity.IssueFilter) []*int64 { return filter.Id })), NewFilterProperty("IM.issuematch_status = ?", WrapRetSlice(func(filter *entity.IssueFilter) []*string { return filter.IssueMatchStatus })), NewFilterProperty("IM.issuematch_rating = ?", WrapRetSlice(func(filter *entity.IssueFilter) []*string { return filter.IssueMatchSeverity })), @@ -75,13 +76,15 @@ func getIssueJoins(filter *entity.IssueFilter, order []entity.Order) string { RIGHT JOIN IssueMatch IM ON I.issue_id = IM.issuematch_issue_id `) } else if len(filter.IssueMatchStatus) > 0 || len(filter.ServiceId) > 0 || len(filter.ServiceCCRN) > 0 || - len(filter.IssueMatchId) > 0 || len(filter.SupportGroupCCRN) > 0 || len(filter.IssueMatchSeverity) > 0 { + len(filter.IssueMatchId) > 0 || len(filter.SupportGroupCCRN) > 0 || len(filter.IssueMatchSeverity) > 0 || + len(filter.Region) > 0 { joins = fmt.Sprintf("%s\n%s", joins, ` LEFT JOIN IssueMatch IM ON I.issue_id = IM.issuematch_issue_id `) } - if len(filter.ServiceId) > 0 || len(filter.ServiceCCRN) > 0 || len(filter.SupportGroupCCRN) > 0 || filter.AllServices { + if len(filter.ServiceId) > 0 || len(filter.ServiceCCRN) > 0 || len(filter.SupportGroupCCRN) > 0 || + len(filter.Region) > 0 || filter.AllServices { joins = fmt.Sprintf("%s\n%s", joins, ` LEFT JOIN ComponentInstance CI ON CI.componentinstance_id = IM.issuematch_component_instance_id @@ -255,10 +258,6 @@ func (s *SqlDatabase) GetIssuesWithAggregations(filter *entity.IssueFilter, orde GROUP BY I.issue_id %s ORDER BY %s LIMIT ? ` - // count(distinct activity_id) as agg_activities, - // LEFT JOIN ActivityHasIssue AHI on I.issue_id = AHI.activityhasissue_issue_id - // LEFT JOIN Activity A on AHI.activityhasissue_activity_id = A.activity_id~ - baseAggQuery := ` SELECT I.*, count(distinct issuematch_id) as agg_issue_matches, diff --git a/internal/database/mariadb/issue_test.go b/internal/database/mariadb/issue_test.go index 1d0b2ebcc..79df733e0 100644 --- a/internal/database/mariadb/issue_test.go +++ b/internal/database/mariadb/issue_test.go @@ -128,6 +128,27 @@ var _ = Describe("Issue", Label("database", "Issue"), func() { Expect(entries).To(BeEmpty()) }) }) + It("can filter by a region", func() { + region := "test-de-1" + + filter := &entity.IssueFilter{ + Region: []*string{ + ®ion, + }, + } + + entries, err := db.GetIssues(filter, nil) + + By("throwing no error", func() { + Expect(err).To(BeNil()) + }) + + for _, entry := range entries { + for _, im := range entry.Issue.IssueMatches { + Expect(im.ComponentInstance.Region).To(Equal(region)) + } + } + }) It("can filter by multiple existing service names", func() { serviceCcrns := make([]*string, len(seedCollection.ServiceRows)) var expectedIssues []mariadb.IssueRow diff --git a/internal/database/mariadb/migrations/20260211213443_extend_mv_schedules.up.sql b/internal/database/mariadb/migrations/20260211213443_extend_mv_schedules.up.sql index 1c11fb0eb..d64a6a393 100644 --- a/internal/database/mariadb/migrations/20260211213443_extend_mv_schedules.up.sql +++ b/internal/database/mariadb/migrations/20260211213443_extend_mv_schedules.up.sql @@ -29,4 +29,4 @@ ALTER EVENT refresh_mvCountIssueRatingsServiceId ON SCHEDULE EVERY 2 HOUR; ALTER EVENT refresh_mvCountIssueRatingsOther -ON SCHEDULE EVERY 2 HOUR; +ON SCHEDULE EVERY 2 HOUR; \ No newline at end of file diff --git a/internal/database/mariadb/migrations/20260304131355_add_count_issue_ratings_region.down.sql b/internal/database/mariadb/migrations/20260304131355_add_count_issue_ratings_region.down.sql new file mode 100644 index 000000000..16e4d9125 --- /dev/null +++ b/internal/database/mariadb/migrations/20260304131355_add_count_issue_ratings_region.down.sql @@ -0,0 +1,8 @@ +-- SPDX-FileCopyrightText: 2026 SAP SE or an SAP affiliate company and Greenhouse contributors +-- SPDX-License-Identifier: Apache-2.0 + +DROP EVENT IF EXISTS refresh_mvCountIssueRatingsRegion; + +DROP PROCEDURE IF EXISTS refresh_mvCountIssueRatingsRegion_proc; + +DROP TABLE IF EXISTS mvCountIssueRatingsRegion; \ No newline at end of file diff --git a/internal/database/mariadb/migrations/20260304131355_add_count_issue_ratings_region.up.sql b/internal/database/mariadb/migrations/20260304131355_add_count_issue_ratings_region.up.sql new file mode 100644 index 000000000..b6c6f80c3 --- /dev/null +++ b/internal/database/mariadb/migrations/20260304131355_add_count_issue_ratings_region.up.sql @@ -0,0 +1,51 @@ +-- SPDX-FileCopyrightText: 2026 SAP SE or an SAP affiliate company and Greenhouse contributors +-- SPDX-License-Identifier: Apache-2.0 + +CREATE TABLE IF NOT EXISTS mvCountIssueRatingsRegion ( + region VARCHAR(1024) UNIQUE, + critical_count INT DEFAULT 0, + high_count INT DEFAULT 0, + medium_count INT DEFAULT 0, + low_count INT DEFAULT 0, + none_count INT DEFAULT 0, + is_active TINYINT(1) NOT NULL DEFAULT 1 +); + +CREATE PROCEDURE IF NOT EXISTS refresh_mvCountIssueRatingsRegion_proc() +BEGIN + UPDATE mvCountIssueRatingsRegion + SET is_active = 0 + WHERE is_active = 1; + + INSERT INTO mvCountIssueRatingsRegion + (region, critical_count, high_count, medium_count, low_count, none_count, is_active) + SELECT + DISTINCT CI.componentinstance_region, + COUNT(DISTINCT CASE WHEN IV.issuevariant_rating = 'Critical' THEN IV.issuevariant_issue_id END), + COUNT(DISTINCT CASE WHEN IV.issuevariant_rating = 'High' THEN IV.issuevariant_issue_id END), + COUNT(DISTINCT CASE WHEN IV.issuevariant_rating = 'Medium' THEN IV.issuevariant_issue_id END), + COUNT(DISTINCT CASE WHEN IV.issuevariant_rating = 'Low' THEN IV.issuevariant_issue_id END), + COUNT(DISTINCT CASE WHEN IV.issuevariant_rating = 'None' THEN IV.issuevariant_issue_id END), + 1 + FROM Issue I + LEFT JOIN IssueVariant IV ON IV.issuevariant_issue_id = I.issue_id + RIGHT JOIN IssueMatch IM ON I.issue_id = IM.issuematch_issue_id + LEFT JOIN ComponentInstance CI ON CI.componentinstance_id = IM.issuematch_component_instance_id + WHERE I.issue_deleted_at IS NULL + GROUP BY CI.componentinstance_region + ON DUPLICATE KEY UPDATE + critical_count = VALUES(critical_count), + high_count = VALUES(high_count), + medium_count = VALUES(medium_count), + low_count = VALUES(low_count), + none_count = VALUES(none_count), + is_active = 1; + + DELETE FROM mvCountIssueRatingsRegion + WHERE is_active = 0; +END; + +CREATE EVENT IF NOT EXISTS refresh_mvCountIssueRatingsRegion +ON SCHEDULE EVERY 2 HOUR +DO + CALL refresh_mvCountIssueRatingsRegion_proc(); \ No newline at end of file diff --git a/internal/database/mariadb/mv_vulnerabilities.go b/internal/database/mariadb/mv_vulnerabilities.go index 7df71f4af..85ed90420 100644 --- a/internal/database/mariadb/mv_vulnerabilities.go +++ b/internal/database/mariadb/mv_vulnerabilities.go @@ -33,6 +33,8 @@ func getCountTable(filter *entity.IssueFilter) string { } else if len(filter.ServiceCCRN) > 0 || len(filter.ServiceId) > 0 { // Count issues that appear in single service return "mvCountIssueRatingsServiceId" + } else if len(filter.Region) > 0 { + return "mvCountIssueRatingsRegion" } else { // Total count of issues return "mvCountIssueRatingsOther" @@ -76,6 +78,11 @@ func (s *SqlDatabase) CountIssueRatings(filter *entity.IssueFilter) (*entity.Iss fl = append(fl, buildFilterQuery(filter.SupportGroupCCRN, "CIR.supportgroup_ccrn = ?", OP_OR)) } + if len(filter.Region) > 0 { + filterParameters = buildQueryParameters(filterParameters, filter.Region) + fl = append(fl, buildFilterQuery(filter.Region, "CIR.region = ?", OP_OR)) + } + filterStr := combineFilterQueries(fl, OP_AND) if filterStr != "" { query = fmt.Sprintf("%s WHERE %s", query, filterStr) diff --git a/internal/database/mariadb/mv_vulnerabilities_test.go b/internal/database/mariadb/mv_vulnerabilities_test.go index dfe95d5ad..6f6ce0dc9 100644 --- a/internal/database/mariadb/mv_vulnerabilities_test.go +++ b/internal/database/mariadb/mv_vulnerabilities_test.go @@ -108,6 +108,16 @@ var _ = Describe("Counting Issues by Severity", Label("IssueCounts"), func() { testIssueSeverityCount(filter, counts) } + testRegionTotalCount := func(counts map[string]entity.IssueSeverityCounts) { + for _, cir := range seedCollection.ComponentInstanceRows { + filter := &entity.IssueFilter{ + Region: []*string{&cir.Region.String}, + } + + testIssueSeverityCount(filter, counts[cir.Region.String]) + } + } + insertRemediation := func(serviceRow *mariadb.BaseServiceRow, componentRow *mariadb.ComponentRow, expirationDate time.Time) *entity.Remediation { remediation := test.NewFakeRemediation() if serviceRow != nil { @@ -488,6 +498,13 @@ var _ = Describe("Counting Issues by Severity", Label("IssueCounts"), func() { testServicesTotalCount(totalCount) }) + + It("return the total count with region filter", func() { + severityCounts, err := test.LoadRegionIssueCounts(test.GetTestDataPath("../mariadb/testdata/issue_counts/issue_counts_per_region.json")) + Expect(err).ToNot(HaveOccurred()) + + testRegionTotalCount(severityCounts) + }) }) When("there is an active remediation for a vulnerability only in one service", Label("WithRemediations"), func() { BeforeEach(func() { diff --git a/internal/database/mariadb/test/common.go b/internal/database/mariadb/test/common.go index 8f7bb9c1a..a41c82738 100644 --- a/internal/database/mariadb/test/common.go +++ b/internal/database/mariadb/test/common.go @@ -319,7 +319,7 @@ func loadIssueCountsFromFile(filename string, idKey string) (map[string]entity.I return nil, err } - var tempIssueCounts []map[string]int64 + var tempIssueCounts []map[string]any if err := json.Unmarshal(data, &tempIssueCounts); err != nil { return nil, err } @@ -328,12 +328,12 @@ func loadIssueCountsFromFile(filename string, idKey string) (map[string]entity.I for _, tic := range tempIssueCounts { id := fmt.Sprintf("%v", tic[idKey]) issueCounts[id] = entity.IssueSeverityCounts{ - Critical: tic["critical"], - High: tic["high"], - Medium: tic["medium"], - Low: tic["low"], - None: tic["none"], - Total: tic["total"], + Critical: int64(tic["critical"].(float64)), + High: int64(tic["high"].(float64)), + Medium: int64(tic["medium"].(float64)), + Low: int64(tic["low"].(float64)), + None: int64(tic["none"].(float64)), + Total: int64(tic["total"].(float64)), } } @@ -352,6 +352,10 @@ func LoadSupportGroupIssueCounts(filename string) (map[string]entity.IssueSeveri return loadIssueCountsFromFile(filename, "support_group_id") } +func LoadRegionIssueCounts(filename string) (map[string]entity.IssueSeverityCounts, error) { + return loadIssueCountsFromFile(filename, "region") +} + func LoadIssueCounts(filename string) (entity.IssueSeverityCounts, error) { data, err := os.ReadFile(filename) var issueCounts entity.IssueSeverityCounts diff --git a/internal/database/mariadb/test/fixture.go b/internal/database/mariadb/test/fixture.go index 0fd01b2fd..4ba4db6f7 100644 --- a/internal/database/mariadb/test/fixture.go +++ b/internal/database/mariadb/test/fixture.go @@ -714,6 +714,8 @@ func (s *DatabaseSeeder) SeedComponentVersions(num int, components []mariadb.Com } func (s *DatabaseSeeder) SeedComponentInstances(num int, componentVersions []mariadb.ComponentVersionRow, services []mariadb.BaseServiceRow) []mariadb.ComponentInstanceRow { + regions := []string{"test-de-1", "test-de-2", "test-us-1", "test-jp-2", "test-jp-1"} + var componentInstances []mariadb.ComponentInstanceRow for i := 0; i < num; i++ { componentInstance := NewFakeComponentInstance() @@ -730,6 +732,11 @@ func (s *DatabaseSeeder) SeedComponentInstances(num int, componentVersions []mar componentInstance.Id = sql.NullInt64{Int64: componentInstanceId, Valid: true} componentInstances = append(componentInstances, componentInstance) } + + componentInstance.Region = sql.NullString{ + String: regions[i%len(regions)], + Valid: true, + } } return componentInstances } @@ -1692,6 +1699,7 @@ func (s *DatabaseSeeder) RefreshCountIssueRatings() error { CALL refresh_mvCountIssueRatingsComponentVersion_proc(); CALL refresh_mvCountIssueRatingsServiceId_proc(); CALL refresh_mvCountIssueRatingsOther_proc(); + CALL refresh_mvCountIssueRatingsRegion_proc(); `) return err } diff --git a/internal/database/mariadb/testdata/issue_counts/issue_counts_per_region.json b/internal/database/mariadb/testdata/issue_counts/issue_counts_per_region.json new file mode 100644 index 000000000..ec85b139b --- /dev/null +++ b/internal/database/mariadb/testdata/issue_counts/issue_counts_per_region.json @@ -0,0 +1,20 @@ +[ + { + "critical": 2, + "high": 0, + "medium": 1, + "low": 2, + "none": 1, + "total": 6, + "region": "test-de-1" + }, + { + "critical": 0, + "high": 2, + "medium": 1, + "low": 0, + "none": 1, + "total": 4, + "region": "test-de-2" + } +] \ No newline at end of file diff --git a/internal/e2e/vulnerability_filter_query_test.go b/internal/e2e/vulnerability_filter_query_test.go index a0f1d487e..f8a0bc778 100644 --- a/internal/e2e/vulnerability_filter_query_test.go +++ b/internal/e2e/vulnerability_filter_query_test.go @@ -107,6 +107,38 @@ var _ = Describe("Getting VulnerabilityFilterValues via API", Label("e2e", "Vuln Expect(lo.Contains(existingServiceCcrns, *ccrn)).To(BeTrue()) } }) + + It("returns correct regions", func() { + respData, err := e2e_common.ExecuteGqlQueryFromFileWithHeaders[struct { + VulnerabilityFilterValues model.VulnerabilityFilterValue `json:"VulnerabilityFilterValues"` + }]( + cfg.Port, + "../api/graphql/graph/queryCollection/vulnerabilityFilter/region.graphql", + nil, + nil, + ) + + Expect(err).ToNot(HaveOccurred()) + + regions := make([]string, 0, len(seedCollection.ComponentInstanceRows)) + + for _, cir := range seedCollection.ComponentInstanceRows { + regions = append(regions, cir.Region.String) + } + + regions = lo.Uniq(regions) + + Expect(len(respData.VulnerabilityFilterValues.Region.Values)).To(Equal(len(regions))) + + existingRegions := lo.Map(seedCollection.ComponentInstanceRows, + func(s mariadb.ComponentInstanceRow, index int) string { + return s.Region.String + }) + + for _, region := range respData.VulnerabilityFilterValues.Region.Values { + Expect(lo.Contains(existingRegions, *region)).To(BeTrue()) + } + }) }) }) }) diff --git a/internal/entity/issue.go b/internal/entity/issue.go index 22b73f0c5..148604a82 100644 --- a/internal/entity/issue.go +++ b/internal/entity/issue.go @@ -64,6 +64,7 @@ type IssueFilter struct { AllServices bool `json:"all_services"` HasIssueMatches bool `json:"has_issue_matches"` SupportGroupCCRN []*string `json:"support_group_ccrn"` + Region []*string `json:"region"` Type []*string `json:"type"` Id []*int64 `json:"id"` IssueMatchId []*int64 `json:"issue_match_id"`