From 04bcb258eeadfd3da8b9a4d7c7851b69dfdbbb19 Mon Sep 17 00:00:00 2001 From: Chris Reed Date: Mon, 27 Oct 2025 10:49:02 -0500 Subject: [PATCH 1/2] fix(policy): Return the correct total during list responses. --- service/integration/attribute_values_test.go | 85 +++++ service/integration/attributes_test.go | 79 +++++ service/integration/resource_mappings_test.go | 313 ++++++++++++++++++ service/policy/db/attribute_values.sql.go | 22 +- service/policy/db/attributes.sql.go | 34 +- service/policy/db/namespaces.sql.go | 20 +- .../policy/db/queries/attribute_values.sql | 9 +- service/policy/db/queries/attributes.sql | 17 +- service/policy/db/queries/namespaces.sql | 8 +- .../policy/db/queries/resource_mapping.sql | 16 +- service/policy/db/resource_mapping.sql.go | 32 +- 11 files changed, 514 insertions(+), 121 deletions(-) diff --git a/service/integration/attribute_values_test.go b/service/integration/attribute_values_test.go index d0f046d8c3..957dc3346e 100644 --- a/service/integration/attribute_values_test.go +++ b/service/integration/attribute_values_test.go @@ -205,6 +205,91 @@ func (s *AttributeValuesSuite) Test_ListAttributeValues_Offset_Succeeds() { } } +func (s *AttributeValuesSuite) Test_ListAttributeValues_AttributeDefID_Succeeds() { + // Create a namespace + ns, err := s.db.PolicyClient.CreateNamespace(s.ctx, &namespaces.CreateNamespaceRequest{ + Name: "test-pagination.com", + }) + s.Require().NoError(err) + s.NotNil(ns) + s.namespaces = append(s.namespaces, ns) + + // Create an attribute definition + attr, err := s.db.PolicyClient.CreateAttribute(s.ctx, &attributes.CreateAttributeRequest{ + Name: "test-attr-pagination", + NamespaceId: ns.GetId(), + Rule: policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF, + }) + s.Require().NoError(err) + s.NotNil(attr) + + // Create multiple attribute values + expectedValues := make([]string, 5) + createdValueIDs := make([]string, 5) + for i := 0; i < 5; i++ { + value := fmt.Sprintf("test-value-%d", i+1) + expectedValues[i] = value + + req := &attributes.CreateAttributeValueRequest{ + Value: value, + } + createdValue, err := s.db.PolicyClient.CreateAttributeValue(s.ctx, attr.GetId(), req) + s.Require().NoError(err) + s.NotNil(createdValue) + createdValueIDs[i] = createdValue.GetId() + s.Equal(value, createdValue.GetValue()) + } + + // Test listing all values without pagination + listRsp, err := s.db.PolicyClient.ListAttributeValues(s.ctx, &attributes.ListAttributeValuesRequest{ + AttributeId: attr.GetId(), + State: common.ActiveStateEnum_ACTIVE_STATE_ENUM_ACTIVE, + }) + s.Require().NoError(err) + s.NotNil(listRsp) + s.Len(listRsp.GetValues(), 5) + + // Verify all created values are in the response + foundValues := make(map[string]bool) + for _, val := range listRsp.GetValues() { + foundValues[val.GetValue()] = true + s.Equal(attr.GetId(), val.GetAttribute().GetId()) + } + for _, expectedValue := range expectedValues { + s.True(foundValues[expectedValue], "Expected value %s not found in list", expectedValue) + } + s.Require().Equal(int32(5), listRsp.GetPagination().GetTotal()) + s.Require().Equal(int32(0), listRsp.GetPagination().GetCurrentOffset()) + s.Require().Equal(int32(0), listRsp.GetPagination().GetNextOffset()) + + // Deactivate one of the attribute values + deactivated, err := s.db.PolicyClient.DeactivateAttributeValue(s.ctx, createdValueIDs[2]) // deactivate "test-value-3" + s.Require().NoError(err) + s.Require().NotNil(deactivated) + s.Require().False(deactivated.GetActive().GetValue()) + + // Test listing only active values after deactivation + activeListRsp, err := s.db.PolicyClient.ListAttributeValues(s.ctx, &attributes.ListAttributeValuesRequest{ + AttributeId: attr.GetId(), + State: common.ActiveStateEnum_ACTIVE_STATE_ENUM_ACTIVE, + }) + s.Require().NoError(err) + s.NotNil(activeListRsp) + s.Len(activeListRsp.GetValues(), 4) // should be 4 active values now + + // Verify that the deactivated value is not in the active list + activeFoundValues := make(map[string]bool) + for _, val := range activeListRsp.GetValues() { + activeFoundValues[val.GetValue()] = true + s.True(val.GetActive().GetValue(), "All values in active list should be active") + s.Equal(attr.GetId(), val.GetAttribute().GetId()) + } + s.False(activeFoundValues["test-value-3"], "Deactivated value should not be in active list") + s.Require().Equal(int32(4), activeListRsp.GetPagination().GetTotal()) + s.Require().Equal(int32(0), listRsp.GetPagination().GetCurrentOffset()) + s.Require().Equal(int32(0), listRsp.GetPagination().GetNextOffset()) +} + func (s *AttributeValuesSuite) Test_GetAttributeValue() { f := s.f.GetAttributeValueKey("example.com/attr/attr1/value/value1") diff --git a/service/integration/attributes_test.go b/service/integration/attributes_test.go index 7168df3927..29d57cdc38 100644 --- a/service/integration/attributes_test.go +++ b/service/integration/attributes_test.go @@ -560,6 +560,85 @@ func (s *AttributesSuite) Test_ListAttributes_ByNamespaceIdOrName() { } } +func (s *AttributesSuite) Test_ListAttributes_MultipleAttributes_Succeeds() { + // Create two test namespaces + createdNS := make([]*policy.Namespace, 2) + ns1, err := s.db.PolicyClient.CreateNamespace(s.ctx, &namespaces.CreateNamespaceRequest{ + Name: "test-list-ns1.com", + }) + s.Require().NoError(err) + s.NotNil(ns1) + createdNS[0] = ns1 + + ns2, err := s.db.PolicyClient.CreateNamespace(s.ctx, &namespaces.CreateNamespaceRequest{ + Name: "test-list-ns2.com", + }) + s.Require().NoError(err) + s.NotNil(ns2) + createdNS[1] = ns2 + + // Cleanup function + defer func() { + // Delete namespaces (this will cascade delete attributes) + for _, ns := range createdNS { + _, err := s.db.PolicyClient.UnsafeDeleteNamespace(s.ctx, ns, ns.GetFqn()) + s.Require().NoError(err) + } + }() + + // Create one attribute in each namespace + attr1, err := s.db.PolicyClient.CreateAttribute(s.ctx, &attributes.CreateAttributeRequest{ + Name: "test_attr_1", + NamespaceId: ns1.GetId(), + Rule: policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF, + Values: []string{"value1", "value2"}, + }) + s.Require().NoError(err) + s.NotNil(attr1) + + attr2, err := s.db.PolicyClient.CreateAttribute(s.ctx, &attributes.CreateAttributeRequest{ + Name: "test_attr_2", + NamespaceId: ns2.GetId(), + Rule: policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ANY_OF, + Values: []string{"valueA", "valueB"}, + }) + s.Require().NoError(err) + s.NotNil(attr2) + + // Test 1: List attributes from first namespace + listReq := &attributes.ListAttributesRequest{ + State: common.ActiveStateEnum_ACTIVE_STATE_ENUM_ACTIVE, + Namespace: ns1.GetId(), + } + listResp, err := s.db.PolicyClient.ListAttributes(s.ctx, listReq) + s.Require().NoError(err) + s.NotNil(listResp) + + listedAttrs := listResp.GetAttributes() + s.Require().Len(listedAttrs, 1, "Should list one attribute from first namespace") + s.Require().Equal(attr1.GetId(), listedAttrs[0].GetId()) + s.Require().Equal(ns1.GetId(), listedAttrs[0].GetNamespace().GetId()) + s.Require().NotEmpty(listedAttrs[0].GetFqn()) + s.Require().Equal(int32(1), listResp.GetPagination().GetTotal()) + s.Require().Equal(int32(0), listResp.GetPagination().GetNextOffset()) + s.Require().Equal(int32(0), listResp.GetPagination().GetCurrentOffset()) + + // Test 2: List attributes from second namespace + listReq.Namespace = ns2.GetId() + listResp, err = s.db.PolicyClient.ListAttributes(s.ctx, listReq) + s.Require().NoError(err) + s.NotNil(listResp) + + listedAttrs = listResp.GetAttributes() + s.Require().Len(listedAttrs, 1, "Should list one attribute from second namespace") + s.Require().Equal(attr2.GetId(), listedAttrs[0].GetId()) + s.Require().Equal(ns2.GetId(), listedAttrs[0].GetNamespace().GetId()) + s.Require().NotEmpty(listedAttrs[0].GetFqn()) + s.Require().Equal(int32(1), listResp.GetPagination().GetTotal()) + s.Require().Equal(int32(0), listResp.GetPagination().GetNextOffset()) + s.Require().Equal(int32(0), listResp.GetPagination().GetCurrentOffset()) +} + func (s *AttributesSuite) Test_UpdateAttribute() { fixedLabel := "fixed label" updateLabel := "update label" diff --git a/service/integration/resource_mappings_test.go b/service/integration/resource_mappings_test.go index 7e5ea48898..0cb92798af 100644 --- a/service/integration/resource_mappings_test.go +++ b/service/integration/resource_mappings_test.go @@ -8,6 +8,8 @@ import ( "github.com/opentdf/platform/protocol/go/common" "github.com/opentdf/platform/protocol/go/policy" + "github.com/opentdf/platform/protocol/go/policy/attributes" + "github.com/opentdf/platform/protocol/go/policy/namespaces" "github.com/opentdf/platform/protocol/go/policy/resourcemapping" "github.com/opentdf/platform/service/internal/fixtures" "github.com/opentdf/platform/service/pkg/db" @@ -136,6 +138,137 @@ func (s *ResourceMappingsSuite) Test_ListResourceMappingGroups_WithNamespaceId_S s.Equal(scenarioDotComRmGroup.Name, list[0].GetName()) } +func (s *ResourceMappingsSuite) Test_ListResourceMappingGroups_MultipleNamespaces_Succeeds() { + createdNS := make([]*policy.Namespace, 2) + ns1, err := s.db.PolicyClient.CreateNamespace(s.ctx, &namespaces.CreateNamespaceRequest{ + Name: "test-rmgroup-ns1.com", + }) + s.Require().NoError(err) + s.Require().NotNil(ns1) + createdNS[0] = ns1 + + ns2, err := s.db.PolicyClient.CreateNamespace(s.ctx, &namespaces.CreateNamespaceRequest{ + Name: "test-rmgroup-ns2.com", + }) + s.Require().NoError(err) + s.Require().NotNil(ns2) + createdNS[1] = ns2 + + // Cleanup function + defer func() { + // Delete namespaces (this will cascade delete resource mapping groups) + for _, ns := range createdNS { + _, err := s.db.PolicyClient.UnsafeDeleteNamespace(s.ctx, ns, ns.GetFqn()) + s.Require().NoError(err) + } + }() + + // Create one resource mapping group in each namespace + rmGroup1, err := s.db.PolicyClient.CreateResourceMappingGroup(s.ctx, &resourcemapping.CreateResourceMappingGroupRequest{ + NamespaceId: ns1.GetId(), + Name: "test-group-1", + }) + s.Require().NoError(err) + s.Require().NotNil(rmGroup1) + + rmGroup2, err := s.db.PolicyClient.CreateResourceMappingGroup(s.ctx, &resourcemapping.CreateResourceMappingGroupRequest{ + NamespaceId: ns2.GetId(), + Name: "test-group-2", + }) + s.Require().NoError(err) + s.Require().NotNil(rmGroup2) + + // Test 1: List all resource mapping groups without namespace filter + allGroupsResp, err := s.db.PolicyClient.ListResourceMappingGroups(s.ctx, &resourcemapping.ListResourceMappingGroupsRequest{}) + s.Require().NoError(err) + s.Require().NotNil(allGroupsResp) + allGroups := allGroupsResp.GetResourceMappingGroups() + + // Check pagination object + pagination := allGroupsResp.GetPagination() + s.Require().NotNil(pagination) + s.Require().GreaterOrEqual(pagination.GetTotal(), int32(2), "should have at least 2 groups") + s.Require().Equal(int32(0), pagination.GetCurrentOffset()) + s.Require().Equal(int32(0), pagination.GetNextOffset()) + + // Verify both created groups are in the response + found1, found2 := false, false + for _, group := range allGroups { + if group.GetId() == rmGroup1.GetId() { + found1 = true + s.Require().Equal(ns1.GetId(), group.GetNamespaceId()) + s.Require().Equal("test-group-1", group.GetName()) + } + if group.GetId() == rmGroup2.GetId() { + found2 = true + s.Require().Equal(ns2.GetId(), group.GetNamespaceId()) + s.Require().Equal("test-group-2", group.GetName()) + } + } + s.Require().True(found1, "expected to find resource mapping group 1") + s.Require().True(found2, "expected to find resource mapping group 2") + + // Test 2: List resource mapping groups for namespace 1 only + ns1GroupsResp, err := s.db.PolicyClient.ListResourceMappingGroups(s.ctx, &resourcemapping.ListResourceMappingGroupsRequest{ + NamespaceId: ns1.GetId(), + }) + s.Require().NoError(err) + s.Require().NotNil(ns1GroupsResp) + ns1Groups := ns1GroupsResp.GetResourceMappingGroups() + + // Check pagination object for namespace 1 filter + ns1Pagination := ns1GroupsResp.GetPagination() + s.Require().NotNil(ns1Pagination) + s.Require().Equal(int32(1), ns1Pagination.GetTotal(), "should have exactly 1 group for namespace 1") + s.Require().Equal(int32(0), ns1Pagination.GetCurrentOffset()) + s.Require().Equal(int32(0), ns1Pagination.GetNextOffset()) + + // Should only contain group from namespace 1 + found1, found2 = false, false + for _, group := range ns1Groups { + if group.GetId() == rmGroup1.GetId() { + found1 = true + s.Require().Equal(ns1.GetId(), group.GetNamespaceId()) + s.Require().Equal("test-group-1", group.GetName()) + } + if group.GetId() == rmGroup2.GetId() { + found2 = true + } + } + s.Require().True(found1, "expected to find resource mapping group 1 when filtering by namespace 1") + s.Require().False(found2, "should not find resource mapping group 2 when filtering by namespace 1") + + // Test 3: List resource mapping groups for namespace 2 only + ns2GroupsResp, err := s.db.PolicyClient.ListResourceMappingGroups(s.ctx, &resourcemapping.ListResourceMappingGroupsRequest{ + NamespaceId: ns2.GetId(), + }) + s.Require().NoError(err) + s.Require().NotNil(ns2GroupsResp) + ns2Groups := ns2GroupsResp.GetResourceMappingGroups() + + // Check pagination object for namespace 2 filter + ns2Pagination := ns2GroupsResp.GetPagination() + s.Require().NotNil(ns2Pagination) + s.Require().Equal(int32(1), ns2Pagination.GetTotal(), "should have exactly 1 group for namespace 2") + s.Require().Equal(int32(0), ns2Pagination.GetCurrentOffset()) + s.Require().Equal(int32(0), ns2Pagination.GetNextOffset()) + + // Should only contain group from namespace 2 + found1, found2 = false, false + for _, group := range ns2Groups { + if group.GetId() == rmGroup1.GetId() { + found1 = true + } + if group.GetId() == rmGroup2.GetId() { + found2 = true + s.Require().Equal(ns2.GetId(), group.GetNamespaceId()) + s.Require().Equal("test-group-2", group.GetName()) + } + } + s.Require().False(found1, "should not find resource mapping group 1 when filtering by namespace 2") + s.Require().True(found2, "expected to find resource mapping group 2 when filtering by namespace 2") +} + func (s *ResourceMappingsSuite) Test_GetResourceMappingGroup() { testData := s.getResourceMappingGroupFixtures() for _, testRmGroup := range testData { @@ -559,6 +692,186 @@ func (s *ResourceMappingsSuite) Test_ListResourceMappings_Offset_Succeeds() { } } +func (s *ResourceMappingsSuite) Test_ListResourceMappings_WithMultipleGroups_Succeeds() { + // Create two namespaces for testing + createdNS := make([]*policy.Namespace, 2) + ns1, err := s.db.PolicyClient.CreateNamespace(s.ctx, &namespaces.CreateNamespaceRequest{ + Name: "test-rmmapping-ns1.com", + }) + s.Require().NoError(err) + s.Require().NotNil(ns1) + createdNS[0] = ns1 + + ns2, err := s.db.PolicyClient.CreateNamespace(s.ctx, &namespaces.CreateNamespaceRequest{ + Name: "test-rmmapping-ns2.com", + }) + s.Require().NoError(err) + s.Require().NotNil(ns2) + createdNS[1] = ns2 + + // Cleanup function + defer func() { + // Delete namespaces (this will cascade delete resource mapping groups and mappings) + for _, ns := range createdNS { + _, err := s.db.PolicyClient.UnsafeDeleteNamespace(s.ctx, ns, ns.GetFqn()) + s.Require().NoError(err) + } + }() + + // Create attributes and attribute values for each namespace + attr1, err := s.db.PolicyClient.CreateAttribute(s.ctx, &attributes.CreateAttributeRequest{ + NamespaceId: ns1.GetId(), + Name: "test-attr1", + Rule: policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF, + }) + s.Require().NoError(err) + s.Require().NotNil(attr1) + + attr2, err := s.db.PolicyClient.CreateAttribute(s.ctx, &attributes.CreateAttributeRequest{ + NamespaceId: ns2.GetId(), + Name: "test-attr2", + Rule: policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF, + }) + s.Require().NoError(err) + s.Require().NotNil(attr2) + + attrVal1, err := s.db.PolicyClient.CreateAttributeValue(s.ctx, attr1.GetId(), &attributes.CreateAttributeValueRequest{ + Value: "test-value1", + }) + s.Require().NoError(err) + s.Require().NotNil(attrVal1) + + attrVal2, err := s.db.PolicyClient.CreateAttributeValue(s.ctx, attr2.GetId(), &attributes.CreateAttributeValueRequest{ + Value: "test-value2", + }) + s.Require().NoError(err) + s.Require().NotNil(attrVal2) + + // Create one resource mapping group in each namespace + rmGroup1, err := s.db.PolicyClient.CreateResourceMappingGroup(s.ctx, &resourcemapping.CreateResourceMappingGroupRequest{ + NamespaceId: ns1.GetId(), + Name: "test-mapping-group-1", + }) + s.Require().NoError(err) + s.Require().NotNil(rmGroup1) + + rmGroup2, err := s.db.PolicyClient.CreateResourceMappingGroup(s.ctx, &resourcemapping.CreateResourceMappingGroupRequest{ + NamespaceId: ns2.GetId(), + Name: "test-mapping-group-2", + }) + s.Require().NoError(err) + s.Require().NotNil(rmGroup2) + + // Create one resource mapping in each group + mapping1, err := s.db.PolicyClient.CreateResourceMapping(s.ctx, &resourcemapping.CreateResourceMappingRequest{ + AttributeValueId: attrVal1.GetId(), + GroupId: rmGroup1.GetId(), + Terms: []string{"mapping1-term1", "mapping1-term2"}, + Metadata: &common.MetadataMutable{}, + }) + s.Require().NoError(err) + s.Require().NotNil(mapping1) + + mapping2, err := s.db.PolicyClient.CreateResourceMapping(s.ctx, &resourcemapping.CreateResourceMappingRequest{ + AttributeValueId: attrVal2.GetId(), + GroupId: rmGroup2.GetId(), + Terms: []string{"mapping2-term1", "mapping2-term2"}, + Metadata: &common.MetadataMutable{}, + }) + s.Require().NoError(err) + s.Require().NotNil(mapping2) + + // Test: List all resource mappings without filters + allMappingsResp, err := s.db.PolicyClient.ListResourceMappings(s.ctx, &resourcemapping.ListResourceMappingsRequest{}) + s.Require().NoError(err) + s.Require().NotNil(allMappingsResp) + allMappings := allMappingsResp.GetResourceMappings() + + // Check pagination object + pagination := allMappingsResp.GetPagination() + s.Require().NotNil(pagination) + s.Require().GreaterOrEqual(pagination.GetTotal(), int32(2), "should have at least 2 mappings") + s.Require().Equal(int32(0), pagination.GetCurrentOffset()) + s.Require().Equal(int32(0), pagination.GetNextOffset()) + + // Verify both created mappings are in the response + found1, found2 := false, false + for _, mapping := range allMappings { + if mapping.GetId() == mapping1.GetId() { + found1 = true + s.Require().Equal(rmGroup1.GetId(), mapping.GetGroup().GetId()) + s.Require().Equal(attrVal1.GetId(), mapping.GetAttributeValue().GetId()) + s.Require().Equal([]string{"mapping1-term1", "mapping1-term2"}, mapping.GetTerms()) + } + if mapping.GetId() == mapping2.GetId() { + found2 = true + s.Require().Equal(rmGroup2.GetId(), mapping.GetGroup().GetId()) + s.Require().Equal(attrVal2.GetId(), mapping.GetAttributeValue().GetId()) + s.Require().Equal([]string{"mapping2-term1", "mapping2-term2"}, mapping.GetTerms()) + } + } + s.Require().True(found1, "expected to find resource mapping 1") + s.Require().True(found2, "expected to find resource mapping 2") + + // Test: List resource mappings filtered by group 1 + group1MappingsResp, err := s.db.PolicyClient.ListResourceMappings(s.ctx, &resourcemapping.ListResourceMappingsRequest{ + GroupId: rmGroup1.GetId(), + }) + s.Require().NoError(err) + s.Require().NotNil(group1MappingsResp) + group1Mappings := group1MappingsResp.GetResourceMappings() + + // Check pagination object for group 1 filter + group1Pagination := group1MappingsResp.GetPagination() + s.Require().NotNil(group1Pagination) + s.Require().Equal(int32(1), group1Pagination.GetTotal(), "should have exactly 1 mapping for group 1") + s.Require().Equal(int32(0), group1Pagination.GetCurrentOffset()) + s.Require().Equal(int32(0), group1Pagination.GetNextOffset()) + + // Should only contain mapping from group 1 + found1, found2 = false, false + for _, mapping := range group1Mappings { + if mapping.GetId() == mapping1.GetId() { + found1 = true + s.Require().Equal(rmGroup1.GetId(), mapping.GetGroup().GetId()) + } + if mapping.GetId() == mapping2.GetId() { + found2 = true + } + } + s.Require().True(found1, "expected to find resource mapping 1 when filtering by group 1") + s.Require().False(found2, "should not find resource mapping 2 when filtering by group 1") + + // Test: List resource mappings filtered by group 2 + group2MappingsResp, err := s.db.PolicyClient.ListResourceMappings(s.ctx, &resourcemapping.ListResourceMappingsRequest{ + GroupId: rmGroup2.GetId(), + }) + s.Require().NoError(err) + s.Require().NotNil(group2MappingsResp) + group2Mappings := group2MappingsResp.GetResourceMappings() + + // Check pagination object for group 2 filter + group2Pagination := group2MappingsResp.GetPagination() + s.Require().NotNil(group2Pagination) + s.Require().Equal(int32(1), group2Pagination.GetTotal(), "should have exactly 1 mapping for group 2") + s.Require().Equal(int32(0), group2Pagination.GetCurrentOffset()) + s.Require().Equal(int32(0), group2Pagination.GetNextOffset()) + + // Should only contain mapping from group 2 + found1, found2 = false, false + for _, mapping := range group2Mappings { + if mapping.GetId() == mapping1.GetId() { + found1 = true + } + if mapping.GetId() == mapping2.GetId() { + found2 = true + s.Require().Equal(rmGroup2.GetId(), mapping.GetGroup().GetId()) + } + } + s.Require().False(found1, "should not find resource mapping 1 when filtering by group 2") + s.Require().True(found2, "expected to find resource mapping 2 when filtering by group 2") +} + func (s *ResourceMappingsSuite) Test_ListResourceMappings_ByGroupId_Succeeds() { req := &resourcemapping.ListResourceMappingsRequest{ GroupId: s.getResourceMappingGroupFixtures()[0].ID, diff --git a/service/policy/db/attribute_values.sql.go b/service/policy/db/attribute_values.sql.go index c5d886baa8..a717774581 100644 --- a/service/policy/db/attribute_values.sql.go +++ b/service/policy/db/attribute_values.sql.go @@ -350,20 +350,15 @@ func (q *Queries) getAttributeValue(ctx context.Context, arg getAttributeValuePa const listAttributeValues = `-- name: listAttributeValues :many -WITH counted AS ( - SELECT COUNT(av.id) AS total - FROM attribute_values av -) SELECT + COUNT(*) OVER() AS total, av.id, av.value, av.active, JSON_STRIP_NULLS(JSON_BUILD_OBJECT('labels', av.metadata -> 'labels', 'created_at', av.created_at, 'updated_at', av.updated_at)) as metadata, av.attribute_definition_id, - fqns.fqn, - counted.total + fqns.fqn FROM attribute_values av -CROSS JOIN counted LEFT JOIN attribute_fqns fqns ON av.id = fqns.value_id WHERE ( ($1::BOOLEAN IS NULL OR av.active = $1) AND @@ -381,33 +376,28 @@ type listAttributeValuesParams struct { } type listAttributeValuesRow struct { + Total int64 `json:"total"` ID string `json:"id"` Value string `json:"value"` Active bool `json:"active"` Metadata []byte `json:"metadata"` AttributeDefinitionID string `json:"attribute_definition_id"` Fqn pgtype.Text `json:"fqn"` - Total int64 `json:"total"` } // -------------------------------------------------------------- // ATTRIBUTE VALUES // -------------------------------------------------------------- // -// WITH counted AS ( -// SELECT COUNT(av.id) AS total -// FROM attribute_values av -// ) // SELECT +// COUNT(*) OVER() AS total, // av.id, // av.value, // av.active, // JSON_STRIP_NULLS(JSON_BUILD_OBJECT('labels', av.metadata -> 'labels', 'created_at', av.created_at, 'updated_at', av.updated_at)) as metadata, // av.attribute_definition_id, -// fqns.fqn, -// counted.total +// fqns.fqn // FROM attribute_values av -// CROSS JOIN counted // LEFT JOIN attribute_fqns fqns ON av.id = fqns.value_id // WHERE ( // ($1::BOOLEAN IS NULL OR av.active = $1) AND @@ -430,13 +420,13 @@ func (q *Queries) listAttributeValues(ctx context.Context, arg listAttributeValu for rows.Next() { var i listAttributeValuesRow if err := rows.Scan( + &i.Total, &i.ID, &i.Value, &i.Active, &i.Metadata, &i.AttributeDefinitionID, &i.Fqn, - &i.Total, ); err != nil { return nil, err } diff --git a/service/policy/db/attributes.sql.go b/service/policy/db/attributes.sql.go index 0898808948..33edec005d 100644 --- a/service/policy/db/attributes.sql.go +++ b/service/policy/db/attributes.sql.go @@ -737,10 +737,6 @@ func (q *Queries) listAttributesByDefOrValueFqns(ctx context.Context, fqns []str const listAttributesDetail = `-- name: listAttributesDetail :many -WITH counted AS ( - SELECT COUNT(ad.id) AS total - FROM attribute_definitions ad -) SELECT ad.id, ad.name as attribute_name, @@ -758,9 +754,8 @@ SELECT ) ORDER BY ARRAY_POSITION(ad.values_order, avt.id) ) AS values, fqns.fqn, - counted.total + COUNT(*) OVER() AS total FROM attribute_definitions ad -CROSS JOIN counted LEFT JOIN attribute_namespaces n ON n.id = ad.namespace_id LEFT JOIN ( SELECT @@ -776,7 +771,7 @@ WHERE ($1::BOOLEAN IS NULL OR ad.active = $1) AND (NULLIF($2, '') IS NULL OR ad.namespace_id = $2::uuid) AND (NULLIF($3, '') IS NULL OR n.name = $3) -GROUP BY ad.id, n.name, fqns.fqn, counted.total +GROUP BY ad.id, n.name, fqns.fqn LIMIT $5 OFFSET $4 ` @@ -806,10 +801,6 @@ type listAttributesDetailRow struct { // ATTRIBUTES // -------------------------------------------------------------- // -// WITH counted AS ( -// SELECT COUNT(ad.id) AS total -// FROM attribute_definitions ad -// ) // SELECT // ad.id, // ad.name as attribute_name, @@ -827,9 +818,8 @@ type listAttributesDetailRow struct { // ) ORDER BY ARRAY_POSITION(ad.values_order, avt.id) // ) AS values, // fqns.fqn, -// counted.total +// COUNT(*) OVER() AS total // FROM attribute_definitions ad -// CROSS JOIN counted // LEFT JOIN attribute_namespaces n ON n.id = ad.namespace_id // LEFT JOIN ( // SELECT @@ -845,7 +835,7 @@ type listAttributesDetailRow struct { // ($1::BOOLEAN IS NULL OR ad.active = $1) AND // (NULLIF($2, '') IS NULL OR ad.namespace_id = $2::uuid) AND // (NULLIF($3, '') IS NULL OR n.name = $3) -// GROUP BY ad.id, n.name, fqns.fqn, counted.total +// GROUP BY ad.id, n.name, fqns.fqn // LIMIT $5 // OFFSET $4 func (q *Queries) listAttributesDetail(ctx context.Context, arg listAttributesDetailParams) ([]listAttributesDetailRow, error) { @@ -886,9 +876,6 @@ func (q *Queries) listAttributesDetail(ctx context.Context, arg listAttributesDe } const listAttributesSummary = `-- name: listAttributesSummary :many -WITH counted AS ( - SELECT COUNT(ad.id) AS total FROM attribute_definitions ad -) SELECT ad.id, ad.name as attribute_name, @@ -897,12 +884,11 @@ SELECT ad.namespace_id, ad.active, n.name as namespace_name, - counted.total + COUNT(*) OVER() AS total FROM attribute_definitions ad -CROSS JOIN counted LEFT JOIN attribute_namespaces n ON n.id = ad.namespace_id WHERE ad.namespace_id = $1 -GROUP BY ad.id, n.name, counted.total +GROUP BY ad.id, n.name LIMIT $3 OFFSET $2 ` @@ -926,9 +912,6 @@ type listAttributesSummaryRow struct { // listAttributesSummary // -// WITH counted AS ( -// SELECT COUNT(ad.id) AS total FROM attribute_definitions ad -// ) // SELECT // ad.id, // ad.name as attribute_name, @@ -937,12 +920,11 @@ type listAttributesSummaryRow struct { // ad.namespace_id, // ad.active, // n.name as namespace_name, -// counted.total +// COUNT(*) OVER() AS total // FROM attribute_definitions ad -// CROSS JOIN counted // LEFT JOIN attribute_namespaces n ON n.id = ad.namespace_id // WHERE ad.namespace_id = $1 -// GROUP BY ad.id, n.name, counted.total +// GROUP BY ad.id, n.name // LIMIT $3 // OFFSET $2 func (q *Queries) listAttributesSummary(ctx context.Context, arg listAttributesSummaryParams) ([]listAttributesSummaryRow, error) { diff --git a/service/policy/db/namespaces.sql.go b/service/policy/db/namespaces.sql.go index b70527c1e0..86d34ad3b2 100644 --- a/service/policy/db/namespaces.sql.go +++ b/service/policy/db/namespaces.sql.go @@ -359,18 +359,14 @@ func (q *Queries) getNamespace(ctx context.Context, arg getNamespaceParams) (get const listNamespaces = `-- name: listNamespaces :many -WITH counted AS ( - SELECT COUNT(id) AS total FROM attribute_namespaces -) SELECT + COUNT(*) OVER() AS total, ns.id, ns.name, ns.active, JSON_STRIP_NULLS(JSON_BUILD_OBJECT('labels', ns.metadata -> 'labels', 'created_at', ns.created_at, 'updated_at', ns.updated_at)) as metadata, - fqns.fqn, - counted.total + fqns.fqn FROM attribute_namespaces ns -CROSS JOIN counted LEFT JOIN attribute_fqns fqns ON ns.id = fqns.namespace_id AND fqns.attribute_id IS NULL WHERE ($1::BOOLEAN IS NULL OR ns.active = $1::BOOLEAN) LIMIT $3 @@ -384,30 +380,26 @@ type listNamespacesParams struct { } type listNamespacesRow struct { + Total int64 `json:"total"` ID string `json:"id"` Name string `json:"name"` Active bool `json:"active"` Metadata []byte `json:"metadata"` Fqn pgtype.Text `json:"fqn"` - Total int64 `json:"total"` } // -------------------------------------------------------------- // NAMESPACES // -------------------------------------------------------------- // -// WITH counted AS ( -// SELECT COUNT(id) AS total FROM attribute_namespaces -// ) // SELECT +// COUNT(*) OVER() AS total, // ns.id, // ns.name, // ns.active, // JSON_STRIP_NULLS(JSON_BUILD_OBJECT('labels', ns.metadata -> 'labels', 'created_at', ns.created_at, 'updated_at', ns.updated_at)) as metadata, -// fqns.fqn, -// counted.total +// fqns.fqn // FROM attribute_namespaces ns -// CROSS JOIN counted // LEFT JOIN attribute_fqns fqns ON ns.id = fqns.namespace_id AND fqns.attribute_id IS NULL // WHERE ($1::BOOLEAN IS NULL OR ns.active = $1::BOOLEAN) // LIMIT $3 @@ -422,12 +414,12 @@ func (q *Queries) listNamespaces(ctx context.Context, arg listNamespacesParams) for rows.Next() { var i listNamespacesRow if err := rows.Scan( + &i.Total, &i.ID, &i.Name, &i.Active, &i.Metadata, &i.Fqn, - &i.Total, ); err != nil { return nil, err } diff --git a/service/policy/db/queries/attribute_values.sql b/service/policy/db/queries/attribute_values.sql index c77dedc845..ae39e3ee2d 100644 --- a/service/policy/db/queries/attribute_values.sql +++ b/service/policy/db/queries/attribute_values.sql @@ -3,20 +3,15 @@ ---------------------------------------------------------------- -- name: listAttributeValues :many -WITH counted AS ( - SELECT COUNT(av.id) AS total - FROM attribute_values av -) SELECT + COUNT(*) OVER() AS total, av.id, av.value, av.active, JSON_STRIP_NULLS(JSON_BUILD_OBJECT('labels', av.metadata -> 'labels', 'created_at', av.created_at, 'updated_at', av.updated_at)) as metadata, av.attribute_definition_id, - fqns.fqn, - counted.total + fqns.fqn FROM attribute_values av -CROSS JOIN counted LEFT JOIN attribute_fqns fqns ON av.id = fqns.value_id WHERE ( (sqlc.narg('active')::BOOLEAN IS NULL OR av.active = sqlc.narg('active')) AND diff --git a/service/policy/db/queries/attributes.sql b/service/policy/db/queries/attributes.sql index edb6bb6c8f..58e2859c53 100644 --- a/service/policy/db/queries/attributes.sql +++ b/service/policy/db/queries/attributes.sql @@ -3,10 +3,6 @@ ---------------------------------------------------------------- -- name: listAttributesDetail :many -WITH counted AS ( - SELECT COUNT(ad.id) AS total - FROM attribute_definitions ad -) SELECT ad.id, ad.name as attribute_name, @@ -24,9 +20,8 @@ SELECT ) ORDER BY ARRAY_POSITION(ad.values_order, avt.id) ) AS values, fqns.fqn, - counted.total + COUNT(*) OVER() AS total FROM attribute_definitions ad -CROSS JOIN counted LEFT JOIN attribute_namespaces n ON n.id = ad.namespace_id LEFT JOIN ( SELECT @@ -42,14 +37,11 @@ WHERE (sqlc.narg('active')::BOOLEAN IS NULL OR ad.active = sqlc.narg('active')) AND (NULLIF(@namespace_id, '') IS NULL OR ad.namespace_id = @namespace_id::uuid) AND (NULLIF(@namespace_name, '') IS NULL OR n.name = @namespace_name) -GROUP BY ad.id, n.name, fqns.fqn, counted.total +GROUP BY ad.id, n.name, fqns.fqn LIMIT @limit_ OFFSET @offset_; -- name: listAttributesSummary :many -WITH counted AS ( - SELECT COUNT(ad.id) AS total FROM attribute_definitions ad -) SELECT ad.id, ad.name as attribute_name, @@ -58,12 +50,11 @@ SELECT ad.namespace_id, ad.active, n.name as namespace_name, - counted.total + COUNT(*) OVER() AS total FROM attribute_definitions ad -CROSS JOIN counted LEFT JOIN attribute_namespaces n ON n.id = ad.namespace_id WHERE ad.namespace_id = $1 -GROUP BY ad.id, n.name, counted.total +GROUP BY ad.id, n.name LIMIT @limit_ OFFSET @offset_; diff --git a/service/policy/db/queries/namespaces.sql b/service/policy/db/queries/namespaces.sql index 1b019778a7..f7038d27b3 100644 --- a/service/policy/db/queries/namespaces.sql +++ b/service/policy/db/queries/namespaces.sql @@ -3,18 +3,14 @@ ---------------------------------------------------------------- -- name: listNamespaces :many -WITH counted AS ( - SELECT COUNT(id) AS total FROM attribute_namespaces -) SELECT + COUNT(*) OVER() AS total, ns.id, ns.name, ns.active, JSON_STRIP_NULLS(JSON_BUILD_OBJECT('labels', ns.metadata -> 'labels', 'created_at', ns.created_at, 'updated_at', ns.updated_at)) as metadata, - fqns.fqn, - counted.total + fqns.fqn FROM attribute_namespaces ns -CROSS JOIN counted LEFT JOIN attribute_fqns fqns ON ns.id = fqns.namespace_id AND fqns.attribute_id IS NULL WHERE (sqlc.narg('active')::BOOLEAN IS NULL OR ns.active = sqlc.narg('active')::BOOLEAN) LIMIT @limit_ diff --git a/service/policy/db/queries/resource_mapping.sql b/service/policy/db/queries/resource_mapping.sql index 3ac30443dd..a14741ad7f 100644 --- a/service/policy/db/queries/resource_mapping.sql +++ b/service/policy/db/queries/resource_mapping.sql @@ -3,17 +3,12 @@ ---------------------------------------------------------------- -- name: listResourceMappingGroups :many -WITH counted AS ( - SELECT COUNT(rmg.id) AS total - FROM resource_mapping_groups rmg -) SELECT rmg.id, rmg.namespace_id, rmg.name, JSON_STRIP_NULLS(JSON_BUILD_OBJECT('labels', rmg.metadata -> 'labels', 'created_at', rmg.created_at, 'updated_at', rmg.updated_at)) as metadata, - counted.total + COUNT(*) OVER() AS total FROM resource_mapping_groups rmg -CROSS JOIN counted WHERE (NULLIF(@namespace_id, '') IS NULL OR rmg.namespace_id = @namespace_id::uuid) LIMIT @limit_ OFFSET @offset_; @@ -45,10 +40,6 @@ DELETE FROM resource_mapping_groups WHERE id = $1; ---------------------------------------------------------------- -- name: listResourceMappings :many -WITH counted AS ( - SELECT COUNT(rm.id) AS total - FROM resource_mappings rm -) SELECT m.id, JSON_BUILD_OBJECT('id', av.id, 'value', av.value, 'fqn', fqns.fqn) as attribute_value, @@ -61,14 +52,13 @@ SELECT 'namespace_id', rmg.namespace_id ) ) AS group, - counted.total + COUNT(*) OVER() AS total FROM resource_mappings m -CROSS JOIN counted LEFT JOIN attribute_values av on m.attribute_value_id = av.id LEFT JOIN attribute_fqns fqns on av.id = fqns.value_id LEFT JOIN resource_mapping_groups rmg ON m.group_id = rmg.id WHERE (NULLIF(@group_id, '') IS NULL OR m.group_id = @group_id::UUID) -GROUP BY av.id, m.id, fqns.fqn, rmg.id, rmg.name, rmg.namespace_id, counted.total +GROUP BY av.id, m.id, fqns.fqn, rmg.id, rmg.name, rmg.namespace_id LIMIT @limit_ OFFSET @offset_; diff --git a/service/policy/db/resource_mapping.sql.go b/service/policy/db/resource_mapping.sql.go index 151b55083d..41f483aae6 100644 --- a/service/policy/db/resource_mapping.sql.go +++ b/service/policy/db/resource_mapping.sql.go @@ -177,17 +177,12 @@ func (q *Queries) getResourceMappingGroup(ctx context.Context, id string) (getRe const listResourceMappingGroups = `-- name: listResourceMappingGroups :many -WITH counted AS ( - SELECT COUNT(rmg.id) AS total - FROM resource_mapping_groups rmg -) SELECT rmg.id, rmg.namespace_id, rmg.name, JSON_STRIP_NULLS(JSON_BUILD_OBJECT('labels', rmg.metadata -> 'labels', 'created_at', rmg.created_at, 'updated_at', rmg.updated_at)) as metadata, - counted.total + COUNT(*) OVER() AS total FROM resource_mapping_groups rmg -CROSS JOIN counted WHERE (NULLIF($1, '') IS NULL OR rmg.namespace_id = $1::uuid) LIMIT $3 OFFSET $2 @@ -211,17 +206,12 @@ type listResourceMappingGroupsRow struct { // RESOURCE MAPPING GROUPS // -------------------------------------------------------------- // -// WITH counted AS ( -// SELECT COUNT(rmg.id) AS total -// FROM resource_mapping_groups rmg -// ) // SELECT rmg.id, // rmg.namespace_id, // rmg.name, // JSON_STRIP_NULLS(JSON_BUILD_OBJECT('labels', rmg.metadata -> 'labels', 'created_at', rmg.created_at, 'updated_at', rmg.updated_at)) as metadata, -// counted.total +// COUNT(*) OVER() AS total // FROM resource_mapping_groups rmg -// CROSS JOIN counted // WHERE (NULLIF($1, '') IS NULL OR rmg.namespace_id = $1::uuid) // LIMIT $3 // OFFSET $2 @@ -253,10 +243,6 @@ func (q *Queries) listResourceMappingGroups(ctx context.Context, arg listResourc const listResourceMappings = `-- name: listResourceMappings :many -WITH counted AS ( - SELECT COUNT(rm.id) AS total - FROM resource_mappings rm -) SELECT m.id, JSON_BUILD_OBJECT('id', av.id, 'value', av.value, 'fqn', fqns.fqn) as attribute_value, @@ -269,14 +255,13 @@ SELECT 'namespace_id', rmg.namespace_id ) ) AS group, - counted.total + COUNT(*) OVER() AS total FROM resource_mappings m -CROSS JOIN counted LEFT JOIN attribute_values av on m.attribute_value_id = av.id LEFT JOIN attribute_fqns fqns on av.id = fqns.value_id LEFT JOIN resource_mapping_groups rmg ON m.group_id = rmg.id WHERE (NULLIF($1, '') IS NULL OR m.group_id = $1::UUID) -GROUP BY av.id, m.id, fqns.fqn, rmg.id, rmg.name, rmg.namespace_id, counted.total +GROUP BY av.id, m.id, fqns.fqn, rmg.id, rmg.name, rmg.namespace_id LIMIT $3 OFFSET $2 ` @@ -300,10 +285,6 @@ type listResourceMappingsRow struct { // RESOURCE MAPPING // -------------------------------------------------------------- // -// WITH counted AS ( -// SELECT COUNT(rm.id) AS total -// FROM resource_mappings rm -// ) // SELECT // m.id, // JSON_BUILD_OBJECT('id', av.id, 'value', av.value, 'fqn', fqns.fqn) as attribute_value, @@ -316,14 +297,13 @@ type listResourceMappingsRow struct { // 'namespace_id', rmg.namespace_id // ) // ) AS group, -// counted.total +// COUNT(*) OVER() AS total // FROM resource_mappings m -// CROSS JOIN counted // LEFT JOIN attribute_values av on m.attribute_value_id = av.id // LEFT JOIN attribute_fqns fqns on av.id = fqns.value_id // LEFT JOIN resource_mapping_groups rmg ON m.group_id = rmg.id // WHERE (NULLIF($1, '') IS NULL OR m.group_id = $1::UUID) -// GROUP BY av.id, m.id, fqns.fqn, rmg.id, rmg.name, rmg.namespace_id, counted.total +// GROUP BY av.id, m.id, fqns.fqn, rmg.id, rmg.name, rmg.namespace_id // LIMIT $3 // OFFSET $2 func (q *Queries) listResourceMappings(ctx context.Context, arg listResourceMappingsParams) ([]listResourceMappingsRow, error) { From 07fcc2160ed2de4de3ef9ceabcb1a9bdcdd81286 Mon Sep 17 00:00:00 2001 From: Chris Reed Date: Mon, 27 Oct 2025 10:56:08 -0500 Subject: [PATCH 2/2] fix attribute values list response test. --- service/integration/attribute_values_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/service/integration/attribute_values_test.go b/service/integration/attribute_values_test.go index 957dc3346e..17bf98ba99 100644 --- a/service/integration/attribute_values_test.go +++ b/service/integration/attribute_values_test.go @@ -286,8 +286,8 @@ func (s *AttributeValuesSuite) Test_ListAttributeValues_AttributeDefID_Succeeds( } s.False(activeFoundValues["test-value-3"], "Deactivated value should not be in active list") s.Require().Equal(int32(4), activeListRsp.GetPagination().GetTotal()) - s.Require().Equal(int32(0), listRsp.GetPagination().GetCurrentOffset()) - s.Require().Equal(int32(0), listRsp.GetPagination().GetNextOffset()) + s.Require().Equal(int32(0), activeListRsp.GetPagination().GetCurrentOffset()) + s.Require().Equal(int32(0), activeListRsp.GetPagination().GetNextOffset()) } func (s *AttributeValuesSuite) Test_GetAttributeValue() {