diff --git a/internal/api/grpc/org/v2/integration_test/query_test.go b/internal/api/grpc/org/v2/integration_test/query_test.go index d7acf45121f..a393d9a1f66 100644 --- a/internal/api/grpc/org/v2/integration_test/query_test.go +++ b/internal/api/grpc/org/v2/integration_test/query_test.go @@ -46,6 +46,12 @@ func createOrganizationWithCustomOrgID(ctx context.Context, name string, orgID s } } +// TODO(IAM-Marco): When permission checks will be implemented, this test needs to be updated to +// add the feature flag switch: +// +// relTableState := integration.RelationalTablesEnableMatrix() +// +// See TestServer_ListOrganizations in org/v2beta/integration_test func TestServer_ListOrganizations(t *testing.T) { type args struct { ctx context.Context diff --git a/internal/api/grpc/org/v2/query.go b/internal/api/grpc/org/v2/query.go index 09e2534e8dc..1fff47f490c 100644 --- a/internal/api/grpc/org/v2/query.go +++ b/internal/api/grpc/org/v2/query.go @@ -5,6 +5,7 @@ import ( "connectrpc.com/connect" + orgv2 "github.com/zitadel/zitadel/backend/v3/api/org/v2" "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/api/grpc/object/v2" "github.com/zitadel/zitadel/internal/domain" @@ -14,6 +15,10 @@ import ( ) func (s *Server) ListOrganizations(ctx context.Context, req *connect.Request[org.ListOrganizationsRequest]) (*connect.Response[org.ListOrganizationsResponse], error) { + if authz.GetFeatures(ctx).EnableRelationalTables { + return orgv2.ListOrganizations(ctx, req) + } + queries, err := listOrgRequestToModel(ctx, req) if err != nil { return nil, err diff --git a/internal/api/grpc/org/v2beta/integration_test/org_test.go b/internal/api/grpc/org/v2beta/integration_test/org_test.go index 269ff9eef75..6fd97556fd7 100644 --- a/internal/api/grpc/org/v2beta/integration_test/org_test.go +++ b/internal/api/grpc/org/v2beta/integration_test/org_test.go @@ -305,19 +305,7 @@ func TestServer_UpdateOrganization(t *testing.T) { orgs, orgsName, _ := createOrgs(CTX, t, Client, 2) - relTableState := []struct { - state string - featureSet *feature.SetInstanceFeaturesRequest - }{ - { - state: "when relational tables are enabled", - featureSet: &feature.SetInstanceFeaturesRequest{EnableRelationalTables: gu.Ptr(true)}, - }, - { - state: "when relational tables are disabled", - featureSet: &feature.SetInstanceFeaturesRequest{EnableRelationalTables: gu.Ptr(false)}, - }, - } + relTableState := integration.RelationalTablesEnableMatrix() tests := []struct { name string @@ -359,18 +347,11 @@ func TestServer_UpdateOrganization(t *testing.T) { } for _, stateCase := range relTableState { - _, err := Instance.Client.FeatureV2.SetInstanceFeatures(ctx, stateCase.featureSet) - require.NoError(t, err) - retryDuration, tick := integration.WaitForAndTickWithMaxDuration(ctx, time.Minute) - // It is necessary to use EventuallyWithT otherwise the test cases are going to fail randomly - // because the flag has not been updated in the projections on time. - require.EventuallyWithT(t, func(collect *assert.CollectT) { - features, err := Instance.Client.FeatureV2.GetInstanceFeatures(ctx, &feature.GetInstanceFeaturesRequest{}) - assert.NoError(collect, err) - assert.Equal(collect, stateCase.featureSet.GetEnableRelationalTables(), features.EnableRelationalTables.GetEnabled()) - }, retryDuration, tick) + integration.EnsureInstanceFeature(t, CTX, Instance, stateCase.FeatureSet, func(tCollect *assert.CollectT, got *feature.GetInstanceFeaturesResponse) { + assert.Equal(tCollect, stateCase.FeatureSet.GetEnableRelationalTables(), got.EnableRelationalTables.GetEnabled()) + }) for _, tt := range tests { - t.Run(fmt.Sprintf("%s - %s", stateCase.state, tt.name), func(t1 *testing.T) { + t.Run(fmt.Sprintf("%s - %s", stateCase.State, tt.name), func(t1 *testing.T) { got, err := Client.UpdateOrganization(ctx, tt.req) if tt.wantErr { require.Error(t1, err) @@ -396,12 +377,14 @@ func TestServer_ListOrganizations(t *testing.T) { noOfOrgs := 3 orgs, orgsName, orgsDomain := createOrgs(listOrgIAmOwnerCtx, t, listOrgClient, noOfOrgs) - // deactivat org[1] + // deactivate org[1] _, err := listOrgClient.DeactivateOrganization(listOrgIAmOwnerCtx, &v2beta_org.DeactivateOrganizationRequest{ Id: orgs[1].Id, }) require.NoError(t, err) + relTableState := integration.RelationalTablesEnableMatrix() + tests := []struct { name string ctx context.Context @@ -639,47 +622,54 @@ func TestServer_ListOrganizations(t *testing.T) { }, }, } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - retryDuration, tick := integration.WaitForAndTickWithMaxDuration(CTX, 10*time.Minute) - require.EventuallyWithT(t, func(ttt *assert.CollectT) { - got, err := listOrgClient.ListOrganizations(tt.ctx, &v2beta_org.ListOrganizationsRequest{ - Filter: tt.query, - }) - if tt.err != nil { - require.ErrorContains(ttt, err, tt.err.Error()) - return - } - require.NoError(ttt, err) - require.Equal(ttt, uint64(len(tt.want)), got.Pagination.GetTotalResult()) + for _, stateCase := range relTableState { + integration.EnsureInstanceFeature(t, CTX, Instance, stateCase.FeatureSet, func(tCollect *assert.CollectT, got *feature.GetInstanceFeaturesResponse) { + assert.Equal(tCollect, stateCase.FeatureSet.GetEnableRelationalTables(), got.EnableRelationalTables.GetEnabled()) + }) - foundOrgs := 0 - for _, got := range got.Organizations { - for _, org := range tt.want { + for _, tt := range tests { + t.Run(fmt.Sprintf("%s - %s", stateCase.State, tt.name), func(t *testing.T) { + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(CTX, 10*time.Minute) + require.EventuallyWithT(t, func(ttt *assert.CollectT) { + got, err := listOrgClient.ListOrganizations(tt.ctx, &v2beta_org.ListOrganizationsRequest{ + Filter: tt.query, + }) + if tt.err != nil { + require.ErrorContains(ttt, err, tt.err.Error()) + return + } + require.NoError(ttt, err) - // created/chagned date - gotCD := got.GetCreationDate().AsTime() - now := time.Now() - assert.WithinRange(ttt, gotCD, testStartTimestamp, now.Add(time.Minute)) - gotCD = got.GetChangedDate().AsTime() - assert.WithinRange(ttt, gotCD, testStartTimestamp, now.Add(time.Minute)) + require.Equal(ttt, uint64(len(tt.want)), got.Pagination.GetTotalResult()) - // default org - if org.Name == got.Name && got.Name == "testinstance" { - foundOrgs += 1 - continue - } + foundOrgs := 0 + for _, got := range got.Organizations { + for _, org := range tt.want { - if org.Name == got.Name && - org.Id == got.Id { - foundOrgs += 1 + // created/chagned date + gotCD := got.GetCreationDate().AsTime() + now := time.Now() + assert.WithinRange(ttt, gotCD, testStartTimestamp, now.Add(time.Minute)) + gotCD = got.GetChangedDate().AsTime() + assert.WithinRange(ttt, gotCD, testStartTimestamp, now.Add(time.Minute)) + + // default org + if org.Name == got.Name && got.Name == "testinstance" { + foundOrgs += 1 + continue + } + + if org.Name == got.Name && + org.Id == got.Id { + foundOrgs += 1 + } } } - } - require.Equal(ttt, len(tt.want), foundOrgs) - }, retryDuration, tick, "timeout waiting for expected organizations being created") - }) + require.Equal(ttt, len(tt.want), foundOrgs) + }, retryDuration, tick, "timeout waiting for expected organizations being created") + }) + } } } diff --git a/internal/api/grpc/org/v2beta/org.go b/internal/api/grpc/org/v2beta/org.go index 60e6018fbf2..ec59970b9ba 100644 --- a/internal/api/grpc/org/v2beta/org.go +++ b/internal/api/grpc/org/v2beta/org.go @@ -48,6 +48,10 @@ func (s *Server) UpdateOrganization(ctx context.Context, request *connect.Reques } func (s *Server) ListOrganizations(ctx context.Context, request *connect.Request[v2beta_org.ListOrganizationsRequest]) (*connect.Response[v2beta_org.ListOrganizationsResponse], error) { + if authz.GetFeatures(ctx).EnableRelationalTables { + return orgv2.ListOrganizationsBeta(ctx, request) + } + queries, err := listOrgRequestToModel(s.systemDefaults, request.Msg) if err != nil { return nil, err diff --git a/internal/integration/feature.go b/internal/integration/feature.go index 07942fcdcd3..41b9eaba880 100644 --- a/internal/integration/feature.go +++ b/internal/integration/feature.go @@ -5,6 +5,7 @@ import ( "testing" "time" + "github.com/muhlemmer/gu" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -28,3 +29,22 @@ func EnsureInstanceFeature(t *testing.T, ctx context.Context, instance *Instance tick, "timed out waiting for ensuring instance feature") } + +type RelationalTableFeatureMatrix struct { + State string + FeatureSet *feature.SetInstanceFeaturesRequest +} + +// TODO(IAM-Marco): Once we have gotten rid of eventstore, this can be removed +func RelationalTablesEnableMatrix() []RelationalTableFeatureMatrix { + return []RelationalTableFeatureMatrix{ + { + State: "when relational tables are enabled", + FeatureSet: &feature.SetInstanceFeaturesRequest{EnableRelationalTables: gu.Ptr(true)}, + }, + { + State: "when relational tables are disabled", + FeatureSet: &feature.SetInstanceFeaturesRequest{EnableRelationalTables: gu.Ptr(false)}, + }, + } +}