diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index ee49ed33c9..6796658f6f 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -5,6 +5,7 @@ on: jobs: test: + timeout-minutes: 10 strategy: fail-fast: false matrix: diff --git a/internal/api/grpc/feature/v2/converter.go b/internal/api/grpc/feature/v2/converter.go index aa8b1287f4..259a6f8a42 100644 --- a/internal/api/grpc/feature/v2/converter.go +++ b/internal/api/grpc/feature/v2/converter.go @@ -16,6 +16,7 @@ func systemFeaturesToCommand(req *feature_pb.SetSystemFeaturesRequest) *command. UserSchema: req.UserSchema, Actions: req.Actions, TokenExchange: req.OidcTokenExchange, + ImprovedPerformance: improvedPerformanceListToDomain(req.ImprovedPerformance), } } @@ -28,6 +29,7 @@ func systemFeaturesToPb(f *query.SystemFeatures) *feature_pb.GetSystemFeaturesRe UserSchema: featureSourceToFlagPb(&f.UserSchema), OidcTokenExchange: featureSourceToFlagPb(&f.TokenExchange), Actions: featureSourceToFlagPb(&f.Actions), + ImprovedPerformance: featureSourceToImprovedPerformanceFlagPb(&f.ImprovedPerformance), } } @@ -39,6 +41,7 @@ func instanceFeaturesToCommand(req *feature_pb.SetInstanceFeaturesRequest) *comm UserSchema: req.UserSchema, TokenExchange: req.OidcTokenExchange, Actions: req.Actions, + ImprovedPerformance: improvedPerformanceListToDomain(req.ImprovedPerformance), } } @@ -51,6 +54,14 @@ func instanceFeaturesToPb(f *query.InstanceFeatures) *feature_pb.GetInstanceFeat UserSchema: featureSourceToFlagPb(&f.UserSchema), OidcTokenExchange: featureSourceToFlagPb(&f.TokenExchange), Actions: featureSourceToFlagPb(&f.Actions), + ImprovedPerformance: featureSourceToImprovedPerformanceFlagPb(&f.ImprovedPerformance), + } +} + +func featureSourceToImprovedPerformanceFlagPb(fs *query.FeatureSource[[]feature.ImprovedPerformanceType]) *feature_pb.ImprovedPerformanceFeatureFlag { + return &feature_pb.ImprovedPerformanceFeatureFlag{ + ExecutionPaths: improvedPerformanceTypesToPb(fs.Value), + Source: featureLevelToSourcePb(fs.Level), } } @@ -81,3 +92,48 @@ func featureLevelToSourcePb(level feature.Level) feature_pb.Source { return feature_pb.Source(level) } } + +func improvedPerformanceTypesToPb(types []feature.ImprovedPerformanceType) []feature_pb.ImprovedPerformance { + res := make([]feature_pb.ImprovedPerformance, len(types)) + + for i, typ := range types { + res[i] = improvedPerformanceTypeToPb(typ) + } + + return res +} + +func improvedPerformanceTypeToPb(typ feature.ImprovedPerformanceType) feature_pb.ImprovedPerformance { + switch typ { + case feature.ImprovedPerformanceTypeUnknown: + return feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_UNSPECIFIED + case feature.ImprovedPerformanceTypeOrgByID: + return feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_ORG_BY_ID + default: + return feature_pb.ImprovedPerformance(typ) + } +} + +func improvedPerformanceListToDomain(list []feature_pb.ImprovedPerformance) []feature.ImprovedPerformanceType { + if list == nil { + return nil + } + res := make([]feature.ImprovedPerformanceType, len(list)) + + for i, typ := range list { + res[i] = improvedPerformanceToDomain(typ) + } + + return res +} + +func improvedPerformanceToDomain(typ feature_pb.ImprovedPerformance) feature.ImprovedPerformanceType { + switch typ { + case feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_UNSPECIFIED: + return feature.ImprovedPerformanceTypeUnknown + case feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_ORG_BY_ID: + return feature.ImprovedPerformanceTypeOrgByID + default: + return feature.ImprovedPerformanceTypeUnknown + } +} diff --git a/internal/api/grpc/feature/v2/converter_test.go b/internal/api/grpc/feature/v2/converter_test.go index 78aea5eb15..35dbf98014 100644 --- a/internal/api/grpc/feature/v2/converter_test.go +++ b/internal/api/grpc/feature/v2/converter_test.go @@ -24,6 +24,7 @@ func Test_systemFeaturesToCommand(t *testing.T) { UserSchema: gu.Ptr(true), Actions: gu.Ptr(true), OidcTokenExchange: gu.Ptr(true), + ImprovedPerformance: nil, } want := &command.SystemFeatures{ LoginDefaultOrg: gu.Ptr(true), @@ -32,6 +33,7 @@ func Test_systemFeaturesToCommand(t *testing.T) { UserSchema: gu.Ptr(true), Actions: gu.Ptr(true), TokenExchange: gu.Ptr(true), + ImprovedPerformance: nil, } got := systemFeaturesToCommand(arg) assert.Equal(t, want, got) @@ -68,6 +70,10 @@ func Test_systemFeaturesToPb(t *testing.T) { Level: feature.LevelSystem, Value: false, }, + ImprovedPerformance: query.FeatureSource[[]feature.ImprovedPerformanceType]{ + Level: feature.LevelSystem, + Value: []feature.ImprovedPerformanceType{feature.ImprovedPerformanceTypeOrgByID}, + }, } want := &feature_pb.GetSystemFeaturesResponse{ Details: &object.Details{ @@ -99,6 +105,10 @@ func Test_systemFeaturesToPb(t *testing.T) { Enabled: true, Source: feature_pb.Source_SOURCE_SYSTEM, }, + ImprovedPerformance: &feature_pb.ImprovedPerformanceFeatureFlag{ + ExecutionPaths: []feature_pb.ImprovedPerformance{feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_ORG_BY_ID}, + Source: feature_pb.Source_SOURCE_SYSTEM, + }, } got := systemFeaturesToPb(arg) assert.Equal(t, want, got) @@ -112,6 +122,7 @@ func Test_instanceFeaturesToCommand(t *testing.T) { UserSchema: gu.Ptr(true), OidcTokenExchange: gu.Ptr(true), Actions: gu.Ptr(true), + ImprovedPerformance: nil, } want := &command.InstanceFeatures{ LoginDefaultOrg: gu.Ptr(true), @@ -120,6 +131,7 @@ func Test_instanceFeaturesToCommand(t *testing.T) { UserSchema: gu.Ptr(true), TokenExchange: gu.Ptr(true), Actions: gu.Ptr(true), + ImprovedPerformance: nil, } got := instanceFeaturesToCommand(arg) assert.Equal(t, want, got) @@ -156,6 +168,10 @@ func Test_instanceFeaturesToPb(t *testing.T) { Level: feature.LevelSystem, Value: false, }, + ImprovedPerformance: query.FeatureSource[[]feature.ImprovedPerformanceType]{ + Level: feature.LevelSystem, + Value: []feature.ImprovedPerformanceType{feature.ImprovedPerformanceTypeOrgByID}, + }, } want := &feature_pb.GetInstanceFeaturesResponse{ Details: &object.Details{ @@ -187,6 +203,10 @@ func Test_instanceFeaturesToPb(t *testing.T) { Enabled: false, Source: feature_pb.Source_SOURCE_SYSTEM, }, + ImprovedPerformance: &feature_pb.ImprovedPerformanceFeatureFlag{ + ExecutionPaths: []feature_pb.ImprovedPerformance{feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_ORG_BY_ID}, + Source: feature_pb.Source_SOURCE_SYSTEM, + }, } got := instanceFeaturesToPb(arg) assert.Equal(t, want, got) diff --git a/internal/command/instance_features.go b/internal/command/instance_features.go index 35ef57da37..3acc789d1b 100644 --- a/internal/command/instance_features.go +++ b/internal/command/instance_features.go @@ -7,6 +7,7 @@ import ( "github.com/zitadel/zitadel/internal/command/preparation" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/eventstore" + "github.com/zitadel/zitadel/internal/feature" "github.com/zitadel/zitadel/internal/repository/feature/feature_v2" "github.com/zitadel/zitadel/internal/zerrors" ) @@ -18,6 +19,7 @@ type InstanceFeatures struct { UserSchema *bool TokenExchange *bool Actions *bool + ImprovedPerformance []feature.ImprovedPerformanceType } func (m *InstanceFeatures) isEmpty() bool { @@ -26,7 +28,9 @@ func (m *InstanceFeatures) isEmpty() bool { m.LegacyIntrospection == nil && m.UserSchema == nil && m.TokenExchange == nil && - m.Actions == nil + m.Actions == nil && + // nil check to allow unset improvements + m.ImprovedPerformance == nil } func (c *Commands) SetInstanceFeatures(ctx context.Context, f *InstanceFeatures) (*domain.ObjectDetails, error) { @@ -37,11 +41,11 @@ func (c *Commands) SetInstanceFeatures(ctx context.Context, f *InstanceFeatures) if err := c.eventstore.FilterToQueryReducer(ctx, wm); err != nil { return nil, err } - cmds := wm.setCommands(ctx, f) - if len(cmds) == 0 { + commands := wm.setCommands(ctx, f) + if len(commands) == 0 { return writeModelToObjectDetails(wm.WriteModel), nil } - events, err := c.eventstore.Push(ctx, cmds...) + events, err := c.eventstore.Push(ctx, commands...) if err != nil { return nil, err } diff --git a/internal/command/instance_features_model.go b/internal/command/instance_features_model.go index 55ce5ea3da..bfd606e672 100644 --- a/internal/command/instance_features_model.go +++ b/internal/command/instance_features_model.go @@ -30,14 +30,23 @@ func (m *InstanceFeaturesWriteModel) Reduce() (err error) { case *feature_v2.ResetEvent: m.reduceReset() case *feature_v1.SetEvent[feature_v1.Boolean]: - err = m.reduceBoolFeature( - feature_v1.DefaultLoginInstanceEventToV2(e), + reduceInstanceFeature( + &m.InstanceFeatures, + feature.KeyLoginDefaultOrg, + feature_v1.DefaultLoginInstanceEventToV2(e).Value, ) case *feature_v2.SetEvent[bool]: - err = m.reduceBoolFeature(e) - } - if err != nil { - return err + _, key, err := e.FeatureInfo() + if err != nil { + return err + } + reduceInstanceFeature(&m.InstanceFeatures, key, e.Value) + case *feature_v2.SetEvent[[]feature.ImprovedPerformanceType]: + _, key, err := e.FeatureInfo() + if err != nil { + return err + } + reduceInstanceFeature(&m.InstanceFeatures, key, e.Value) } } return m.WriteModel.Reduce() @@ -57,41 +66,41 @@ func (m *InstanceFeaturesWriteModel) Query() *eventstore.SearchQueryBuilder { feature_v2.InstanceUserSchemaEventType, feature_v2.InstanceTokenExchangeEventType, feature_v2.InstanceActionsEventType, + feature_v2.InstanceImprovedPerformanceEventType, ). Builder().ResourceOwner(m.ResourceOwner) } func (m *InstanceFeaturesWriteModel) reduceReset() { - m.LoginDefaultOrg = nil - m.TriggerIntrospectionProjections = nil - m.LegacyIntrospection = nil - m.UserSchema = nil - m.TokenExchange = nil - m.Actions = nil + m.InstanceFeatures = InstanceFeatures{} } -func (m *InstanceFeaturesWriteModel) reduceBoolFeature(event *feature_v2.SetEvent[bool]) error { - _, key, err := event.FeatureInfo() - if err != nil { - return err - } +func reduceInstanceFeature(features *InstanceFeatures, key feature.Key, value any) { switch key { case feature.KeyUnspecified: - return nil + return case feature.KeyLoginDefaultOrg: - m.LoginDefaultOrg = &event.Value + v := value.(bool) + features.LoginDefaultOrg = &v case feature.KeyTriggerIntrospectionProjections: - m.TriggerIntrospectionProjections = &event.Value + v := value.(bool) + features.TriggerIntrospectionProjections = &v case feature.KeyLegacyIntrospection: - m.LegacyIntrospection = &event.Value + v := value.(bool) + features.LegacyIntrospection = &v case feature.KeyTokenExchange: - m.TokenExchange = &event.Value + v := value.(bool) + features.TokenExchange = &v case feature.KeyUserSchema: - m.UserSchema = &event.Value + v := value.(bool) + features.UserSchema = &v case feature.KeyActions: - m.Actions = &event.Value + v := value.(bool) + features.Actions = &v + case feature.KeyImprovedPerformance: + v := value.([]feature.ImprovedPerformanceType) + features.ImprovedPerformance = v } - return nil } func (wm *InstanceFeaturesWriteModel) setCommands(ctx context.Context, f *InstanceFeatures) []eventstore.Command { @@ -103,5 +112,6 @@ func (wm *InstanceFeaturesWriteModel) setCommands(ctx context.Context, f *Instan cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.TokenExchange, f.TokenExchange, feature_v2.InstanceTokenExchangeEventType) cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.UserSchema, f.UserSchema, feature_v2.InstanceUserSchemaEventType) cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.Actions, f.Actions, feature_v2.InstanceActionsEventType) + cmds = appendFeatureSliceUpdate(ctx, cmds, aggregate, wm.ImprovedPerformance, f.ImprovedPerformance, feature_v2.InstanceImprovedPerformanceEventType) return cmds } diff --git a/internal/command/system_features.go b/internal/command/system_features.go index 838fef5b9d..5aa7c8dda1 100644 --- a/internal/command/system_features.go +++ b/internal/command/system_features.go @@ -4,6 +4,7 @@ import ( "context" "github.com/zitadel/zitadel/internal/domain" + "github.com/zitadel/zitadel/internal/feature" "github.com/zitadel/zitadel/internal/repository/feature/feature_v2" "github.com/zitadel/zitadel/internal/zerrors" ) @@ -15,6 +16,7 @@ type SystemFeatures struct { TokenExchange *bool UserSchema *bool Actions *bool + ImprovedPerformance []feature.ImprovedPerformanceType } func (m *SystemFeatures) isEmpty() bool { @@ -23,7 +25,9 @@ func (m *SystemFeatures) isEmpty() bool { m.LegacyIntrospection == nil && m.UserSchema == nil && m.TokenExchange == nil && - m.Actions == nil + m.Actions == nil && + // nil check to allow unset improvements + m.ImprovedPerformance == nil } func (c *Commands) SetSystemFeatures(ctx context.Context, f *SystemFeatures) (*domain.ObjectDetails, error) { diff --git a/internal/command/system_features_model.go b/internal/command/system_features_model.go index d21aa85cbc..46b36d15ad 100644 --- a/internal/command/system_features_model.go +++ b/internal/command/system_features_model.go @@ -23,16 +23,23 @@ func NewSystemFeaturesWriteModel() *SystemFeaturesWriteModel { return m } -func (m *SystemFeaturesWriteModel) Reduce() (err error) { +func (m *SystemFeaturesWriteModel) Reduce() error { for _, event := range m.Events { switch e := event.(type) { case *feature_v2.ResetEvent: m.reduceReset() case *feature_v2.SetEvent[bool]: - err = m.reduceBoolFeature(e) - } - if err != nil { - return err + _, key, err := e.FeatureInfo() + if err != nil { + return err + } + reduceSystemFeature(&m.SystemFeatures, key, e.Value) + case *feature_v2.SetEvent[[]feature.ImprovedPerformanceType]: + _, key, err := e.FeatureInfo() + if err != nil { + return err + } + reduceSystemFeature(&m.SystemFeatures, key, e.Value) } } return m.WriteModel.Reduce() @@ -52,41 +59,40 @@ func (m *SystemFeaturesWriteModel) Query() *eventstore.SearchQueryBuilder { feature_v2.SystemUserSchemaEventType, feature_v2.SystemTokenExchangeEventType, feature_v2.SystemActionsEventType, + feature_v2.SystemImprovedPerformanceEventType, ). Builder().ResourceOwner(m.ResourceOwner) } func (m *SystemFeaturesWriteModel) reduceReset() { - m.LoginDefaultOrg = nil - m.TriggerIntrospectionProjections = nil - m.LegacyIntrospection = nil - m.TokenExchange = nil - m.UserSchema = nil - m.Actions = nil + m.SystemFeatures = SystemFeatures{} } -func (m *SystemFeaturesWriteModel) reduceBoolFeature(event *feature_v2.SetEvent[bool]) error { - _, key, err := event.FeatureInfo() - if err != nil { - return err - } +func reduceSystemFeature(features *SystemFeatures, key feature.Key, value any) { switch key { case feature.KeyUnspecified: - return nil + return case feature.KeyLoginDefaultOrg: - m.LoginDefaultOrg = &event.Value + v := value.(bool) + features.LoginDefaultOrg = &v case feature.KeyTriggerIntrospectionProjections: - m.TriggerIntrospectionProjections = &event.Value + v := value.(bool) + features.TriggerIntrospectionProjections = &v case feature.KeyLegacyIntrospection: - m.LegacyIntrospection = &event.Value + v := value.(bool) + features.LegacyIntrospection = &v case feature.KeyUserSchema: - m.UserSchema = &event.Value + v := value.(bool) + features.UserSchema = &v case feature.KeyTokenExchange: - m.TokenExchange = &event.Value + v := value.(bool) + features.TokenExchange = &v case feature.KeyActions: - m.Actions = &event.Value + v := value.(bool) + features.Actions = &v + case feature.KeyImprovedPerformance: + features.ImprovedPerformance = value.([]feature.ImprovedPerformanceType) } - return nil } func (wm *SystemFeaturesWriteModel) setCommands(ctx context.Context, f *SystemFeatures) []eventstore.Command { @@ -98,6 +104,7 @@ func (wm *SystemFeaturesWriteModel) setCommands(ctx context.Context, f *SystemFe cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.UserSchema, f.UserSchema, feature_v2.SystemUserSchemaEventType) cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.TokenExchange, f.TokenExchange, feature_v2.SystemTokenExchangeEventType) cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.Actions, f.Actions, feature_v2.SystemActionsEventType) + cmds = appendFeatureSliceUpdate(ctx, cmds, aggregate, wm.ImprovedPerformance, f.ImprovedPerformance, feature_v2.SystemImprovedPerformanceEventType) return cmds } @@ -107,3 +114,15 @@ func appendFeatureUpdate[T comparable](ctx context.Context, cmds []eventstore.Co } return cmds } + +func appendFeatureSliceUpdate[T comparable](ctx context.Context, cmds []eventstore.Command, aggregate *feature_v2.Aggregate, oldValues, newValues []T, eventType eventstore.EventType) []eventstore.Command { + if len(newValues) != len(oldValues) { + return append(cmds, feature_v2.NewSetEvent[[]T](ctx, aggregate, eventType, newValues)) + } + for i, oldValue := range oldValues { + if oldValue != newValues[i] { + return append(cmds, feature_v2.NewSetEvent[[]T](ctx, aggregate, eventType, newValues)) + } + } + return cmds +} diff --git a/internal/feature/feature.go b/internal/feature/feature.go index 5edab4a8ba..dc3e0eb597 100644 --- a/internal/feature/feature.go +++ b/internal/feature/feature.go @@ -11,6 +11,7 @@ const ( KeyUserSchema KeyTokenExchange KeyActions + KeyImprovedPerformance ) //go:generate enumer -type Level -transform snake -trimprefix Level @@ -27,10 +28,27 @@ const ( ) type Features struct { - LoginDefaultOrg bool `json:"login_default_org,omitempty"` - TriggerIntrospectionProjections bool `json:"trigger_introspection_projections,omitempty"` - LegacyIntrospection bool `json:"legacy_introspection,omitempty"` - UserSchema bool `json:"user_schema,omitempty"` - TokenExchange bool `json:"token_exchange,omitempty"` - Actions bool `json:"actions,omitempty"` + LoginDefaultOrg bool `json:"login_default_org,omitempty"` + TriggerIntrospectionProjections bool `json:"trigger_introspection_projections,omitempty"` + LegacyIntrospection bool `json:"legacy_introspection,omitempty"` + UserSchema bool `json:"user_schema,omitempty"` + TokenExchange bool `json:"token_exchange,omitempty"` + Actions bool `json:"actions,omitempty"` + ImprovedPerformance []ImprovedPerformanceType `json:"improved_performance,omitempty"` +} + +type ImprovedPerformanceType int32 + +const ( + ImprovedPerformanceTypeUnknown = iota + ImprovedPerformanceTypeOrgByID +) + +func (f Features) ShouldUseImprovedPerformance(typ ImprovedPerformanceType) bool { + for _, improvedType := range f.ImprovedPerformance { + if improvedType == typ { + return true + } + } + return false } diff --git a/internal/feature/key_enumer.go b/internal/feature/key_enumer.go index 172d5f9d01..ca3e156b61 100644 --- a/internal/feature/key_enumer.go +++ b/internal/feature/key_enumer.go @@ -7,11 +7,11 @@ import ( "strings" ) -const _KeyName = "unspecifiedlogin_default_orgtrigger_introspection_projectionslegacy_introspectionuser_schematoken_exchangeactions" +const _KeyName = "unspecifiedlogin_default_orgtrigger_introspection_projectionslegacy_introspectionuser_schematoken_exchangeactionsimproved_performance" -var _KeyIndex = [...]uint8{0, 11, 28, 61, 81, 92, 106, 113} +var _KeyIndex = [...]uint8{0, 11, 28, 61, 81, 92, 106, 113, 133} -const _KeyLowerName = "unspecifiedlogin_default_orgtrigger_introspection_projectionslegacy_introspectionuser_schematoken_exchangeactions" +const _KeyLowerName = "unspecifiedlogin_default_orgtrigger_introspection_projectionslegacy_introspectionuser_schematoken_exchangeactionsimproved_performance" func (i Key) String() string { if i < 0 || i >= Key(len(_KeyIndex)-1) { @@ -31,9 +31,10 @@ func _KeyNoOp() { _ = x[KeyUserSchema-(4)] _ = x[KeyTokenExchange-(5)] _ = x[KeyActions-(6)] + _ = x[KeyImprovedPerformance-(7)] } -var _KeyValues = []Key{KeyUnspecified, KeyLoginDefaultOrg, KeyTriggerIntrospectionProjections, KeyLegacyIntrospection, KeyUserSchema, KeyTokenExchange, KeyActions} +var _KeyValues = []Key{KeyUnspecified, KeyLoginDefaultOrg, KeyTriggerIntrospectionProjections, KeyLegacyIntrospection, KeyUserSchema, KeyTokenExchange, KeyActions, KeyImprovedPerformance} var _KeyNameToValueMap = map[string]Key{ _KeyName[0:11]: KeyUnspecified, @@ -50,6 +51,8 @@ var _KeyNameToValueMap = map[string]Key{ _KeyLowerName[92:106]: KeyTokenExchange, _KeyName[106:113]: KeyActions, _KeyLowerName[106:113]: KeyActions, + _KeyName[113:133]: KeyImprovedPerformance, + _KeyLowerName[113:133]: KeyImprovedPerformance, } var _KeyNames = []string{ @@ -60,6 +63,7 @@ var _KeyNames = []string{ _KeyName[81:92], _KeyName[92:106], _KeyName[106:113], + _KeyName[113:133], } // KeyString retrieves an enum value from the enum constants string name. diff --git a/internal/query/instance_features.go b/internal/query/instance_features.go index a362b6a5ad..12d8e0d80d 100644 --- a/internal/query/instance_features.go +++ b/internal/query/instance_features.go @@ -4,6 +4,7 @@ import ( "context" "github.com/zitadel/zitadel/internal/domain" + "github.com/zitadel/zitadel/internal/feature" ) type InstanceFeatures struct { @@ -14,6 +15,7 @@ type InstanceFeatures struct { UserSchema FeatureSource[bool] TokenExchange FeatureSource[bool] Actions FeatureSource[bool] + ImprovedPerformance FeatureSource[[]feature.ImprovedPerformanceType] } func (q *Queries) GetInstanceFeatures(ctx context.Context, cascade bool) (_ *InstanceFeatures, err error) { diff --git a/internal/query/instance_features_model.go b/internal/query/instance_features_model.go index 068e56a23c..215442c911 100644 --- a/internal/query/instance_features_model.go +++ b/internal/query/instance_features_model.go @@ -36,11 +36,14 @@ func (m *InstanceFeaturesReadModel) Reduce() (err error) { case *feature_v2.ResetEvent: m.reduceReset() case *feature_v1.SetEvent[feature_v1.Boolean]: - err = m.reduceBoolFeature( + err = reduceInstanceFeatureSet( + m.instance, feature_v1.DefaultLoginInstanceEventToV2(e), ) case *feature_v2.SetEvent[bool]: - err = m.reduceBoolFeature(e) + err = reduceInstanceFeatureSet(m.instance, e) + case *feature_v2.SetEvent[[]feature.ImprovedPerformanceType]: + err = reduceInstanceFeatureSet(m.instance, e) } if err != nil { return err @@ -63,6 +66,7 @@ func (m *InstanceFeaturesReadModel) Query() *eventstore.SearchQueryBuilder { feature_v2.InstanceUserSchemaEventType, feature_v2.InstanceTokenExchangeEventType, feature_v2.InstanceActionsEventType, + feature_v2.InstanceImprovedPerformanceEventType, ). Builder().ResourceOwner(m.ResourceOwner) } @@ -71,12 +75,8 @@ func (m *InstanceFeaturesReadModel) reduceReset() { if m.populateFromSystem() { return } - m.instance.LoginDefaultOrg = FeatureSource[bool]{} - m.instance.TriggerIntrospectionProjections = FeatureSource[bool]{} - m.instance.LegacyIntrospection = FeatureSource[bool]{} - m.instance.UserSchema = FeatureSource[bool]{} - m.instance.TokenExchange = FeatureSource[bool]{} - m.instance.Actions = FeatureSource[bool]{} + m.instance = nil + m.instance = new(InstanceFeatures) } func (m *InstanceFeaturesReadModel) populateFromSystem() bool { @@ -89,35 +89,32 @@ func (m *InstanceFeaturesReadModel) populateFromSystem() bool { m.instance.UserSchema = m.system.UserSchema m.instance.TokenExchange = m.system.TokenExchange m.instance.Actions = m.system.Actions + m.instance.ImprovedPerformance = m.system.ImprovedPerformance return true } -func (m *InstanceFeaturesReadModel) reduceBoolFeature(event *feature_v2.SetEvent[bool]) error { +func reduceInstanceFeatureSet[T any](features *InstanceFeatures, event *feature_v2.SetEvent[T]) error { level, key, err := event.FeatureInfo() if err != nil { return err } - var dst *FeatureSource[bool] - switch key { case feature.KeyUnspecified: return nil case feature.KeyLoginDefaultOrg: - dst = &m.instance.LoginDefaultOrg + features.LoginDefaultOrg.set(level, event.Value) case feature.KeyTriggerIntrospectionProjections: - dst = &m.instance.TriggerIntrospectionProjections + features.TriggerIntrospectionProjections.set(level, event.Value) case feature.KeyLegacyIntrospection: - dst = &m.instance.LegacyIntrospection + features.LegacyIntrospection.set(level, event.Value) case feature.KeyUserSchema: - dst = &m.instance.UserSchema + features.UserSchema.set(level, event.Value) case feature.KeyTokenExchange: - dst = &m.instance.TokenExchange + features.TokenExchange.set(level, event.Value) case feature.KeyActions: - dst = &m.instance.Actions - } - *dst = FeatureSource[bool]{ - Level: level, - Value: event.Value, + features.Actions.set(level, event.Value) + case feature.KeyImprovedPerformance: + features.ImprovedPerformance.set(level, event.Value) } return nil } diff --git a/internal/query/org.go b/internal/query/org.go index 18715d015e..27bdfada76 100644 --- a/internal/query/org.go +++ b/internal/query/org.go @@ -13,8 +13,11 @@ import ( "github.com/zitadel/zitadel/internal/api/call" domain_pkg "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/eventstore/handler/v2" + "github.com/zitadel/zitadel/internal/feature" "github.com/zitadel/zitadel/internal/query/projection" "github.com/zitadel/zitadel/internal/telemetry/tracing" + "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/v2/readmodel" "github.com/zitadel/zitadel/internal/zerrors" ) @@ -92,7 +95,44 @@ func (q *OrgSearchQueries) toQuery(query sq.SelectBuilder) sq.SelectBuilder { return query } -func (q *Queries) OrgByID(ctx context.Context, shouldTriggerBulk bool, id string) (org *Org, err error) { +func (q *Queries) OrgByID(ctx context.Context, shouldTriggerBulk bool, id string) (_ *Org, err error) { + ctx, span := tracing.NewSpan(ctx) + defer func() { span.EndWithError(err) }() + + if !authz.GetInstance(ctx).Features().ShouldUseImprovedPerformance(feature.ImprovedPerformanceTypeOrgByID) { + return q.oldOrgByID(ctx, shouldTriggerBulk, id) + } + + foundOrg := readmodel.NewOrg(id) + eventCount, err := q.eventStoreV4.Query( + ctx, + eventstore.NewQuery( + authz.GetInstance(ctx).InstanceID(), + foundOrg, + eventstore.AppendFilters(foundOrg.Filter()...), + ), + ) + if err != nil { + return nil, zerrors.ThrowInternal(err, "QUERY-AWx52", "Errors.Query.SQLStatement") + } + + if eventCount == 0 { + return nil, zerrors.ThrowNotFound(nil, "QUERY-leq5Q", "Errors.Org.NotFound") + } + + return &Org{ + ID: foundOrg.ID, + CreationDate: foundOrg.CreationDate, + ChangeDate: foundOrg.ChangeDate, + ResourceOwner: foundOrg.Owner, + State: domain_pkg.OrgState(foundOrg.State.State), + Sequence: uint64(foundOrg.Sequence), + Name: foundOrg.Name, + Domain: foundOrg.PrimaryDomain.Domain, + }, nil +} + +func (q *Queries) oldOrgByID(ctx context.Context, shouldTriggerBulk bool, id string) (org *Org, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() diff --git a/internal/query/projection/instance_features.go b/internal/query/projection/instance_features.go index ebb388da89..06090a2f5d 100644 --- a/internal/query/projection/instance_features.go +++ b/internal/query/projection/instance_features.go @@ -6,6 +6,7 @@ import ( "github.com/zitadel/zitadel/internal/eventstore" old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" "github.com/zitadel/zitadel/internal/eventstore/handler/v2" + "github.com/zitadel/zitadel/internal/feature" feature_v1 "github.com/zitadel/zitadel/internal/repository/feature" "github.com/zitadel/zitadel/internal/repository/feature/feature_v2" "github.com/zitadel/zitadel/internal/repository/instance" @@ -83,6 +84,10 @@ func (*instanceFeatureProjection) Reducers() []handler.AggregateReducer { Event: feature_v2.InstanceActionsEventType, Reduce: reduceInstanceSetFeature[bool], }, + { + Event: feature_v2.InstanceImprovedPerformanceEventType, + Reduce: reduceInstanceSetFeature[[]feature.ImprovedPerformanceType], + }, { Event: instance.InstanceRemovedEventType, Reduce: reduceInstanceRemovedHelper(InstanceDomainInstanceIDCol), diff --git a/internal/query/projection/system_features.go b/internal/query/projection/system_features.go index 94c6282c49..158da7a616 100644 --- a/internal/query/projection/system_features.go +++ b/internal/query/projection/system_features.go @@ -6,6 +6,7 @@ import ( "github.com/zitadel/zitadel/internal/eventstore" old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" "github.com/zitadel/zitadel/internal/eventstore/handler/v2" + "github.com/zitadel/zitadel/internal/feature" "github.com/zitadel/zitadel/internal/repository/feature/feature_v2" "github.com/zitadel/zitadel/internal/zerrors" ) @@ -75,6 +76,10 @@ func (*systemFeatureProjection) Reducers() []handler.AggregateReducer { Event: feature_v2.SystemActionsEventType, Reduce: reduceSystemSetFeature[bool], }, + { + Event: feature_v2.SystemImprovedPerformanceEventType, + Reduce: reduceSystemSetFeature[[]feature.ImprovedPerformanceType], + }, }, }} } diff --git a/internal/query/query.go b/internal/query/query.go index 6bae5f924e..8b8313625e 100644 --- a/internal/query/query.go +++ b/internal/query/query.go @@ -19,11 +19,14 @@ import ( "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/query/projection" "github.com/zitadel/zitadel/internal/telemetry/tracing" + es_v4 "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/v2/eventstore/postgres" ) type Queries struct { - eventstore *eventstore.Eventstore - client *database.DB + eventstore *eventstore.Eventstore + eventStoreV4 es_v4.Querier + client *database.DB keyEncryptionAlgorithm crypto.EncryptionAlgorithm idpConfigEncryption crypto.EncryptionAlgorithm @@ -56,6 +59,7 @@ func StartQueries( ) (repo *Queries, err error) { repo = &Queries{ eventstore: es, + eventStoreV4: postgres.New(querySqlClient), client: querySqlClient, DefaultLanguage: language.Und, LoginTranslationFileContents: make(map[string][]byte), diff --git a/internal/query/system_features.go b/internal/query/system_features.go index 65087e92dc..33e5f14b3a 100644 --- a/internal/query/system_features.go +++ b/internal/query/system_features.go @@ -12,6 +12,11 @@ type FeatureSource[T any] struct { Value T } +func (f *FeatureSource[T]) set(level feature.Level, value any) { + f.Level = level + f.Value = value.(T) +} + type SystemFeatures struct { Details *domain.ObjectDetails @@ -21,6 +26,7 @@ type SystemFeatures struct { UserSchema FeatureSource[bool] TokenExchange FeatureSource[bool] Actions FeatureSource[bool] + ImprovedPerformance FeatureSource[[]feature.ImprovedPerformanceType] } func (q *Queries) GetSystemFeatures(ctx context.Context) (_ *SystemFeatures, err error) { diff --git a/internal/query/system_features_model.go b/internal/query/system_features_model.go index 243dc4e6c4..9b185c3e8f 100644 --- a/internal/query/system_features_model.go +++ b/internal/query/system_features_model.go @@ -28,7 +28,12 @@ func (m *SystemFeaturesReadModel) Reduce() error { case *feature_v2.ResetEvent: m.reduceReset() case *feature_v2.SetEvent[bool]: - err := m.reduceBoolFeature(e) + err := reduceSystemFeatureSet(m.system, e) + if err != nil { + return err + } + case *feature_v2.SetEvent[[]feature.ImprovedPerformanceType]: + err := reduceSystemFeatureSet(m.system, e) if err != nil { return err } @@ -51,41 +56,38 @@ func (m *SystemFeaturesReadModel) Query() *eventstore.SearchQueryBuilder { feature_v2.SystemUserSchemaEventType, feature_v2.SystemTokenExchangeEventType, feature_v2.SystemActionsEventType, + feature_v2.SystemImprovedPerformanceEventType, ). Builder().ResourceOwner(m.ResourceOwner) } func (m *SystemFeaturesReadModel) reduceReset() { + m.system = nil m.system = new(SystemFeatures) } -func (m *SystemFeaturesReadModel) reduceBoolFeature(event *feature_v2.SetEvent[bool]) error { +func reduceSystemFeatureSet[T any](features *SystemFeatures, event *feature_v2.SetEvent[T]) error { level, key, err := event.FeatureInfo() if err != nil { return err } - var dst *FeatureSource[bool] - switch key { case feature.KeyUnspecified: return nil case feature.KeyLoginDefaultOrg: - dst = &m.system.LoginDefaultOrg + features.LoginDefaultOrg.set(level, event.Value) case feature.KeyTriggerIntrospectionProjections: - dst = &m.system.TriggerIntrospectionProjections + features.TriggerIntrospectionProjections.set(level, event.Value) case feature.KeyLegacyIntrospection: - dst = &m.system.LegacyIntrospection + features.LegacyIntrospection.set(level, event.Value) case feature.KeyUserSchema: - dst = &m.system.UserSchema + features.UserSchema.set(level, event.Value) case feature.KeyTokenExchange: - dst = &m.system.TokenExchange + features.TokenExchange.set(level, event.Value) case feature.KeyActions: - dst = &m.system.Actions - } - - *dst = FeatureSource[bool]{ - Level: level, - Value: event.Value, + features.Actions.set(level, event.Value) + case feature.KeyImprovedPerformance: + features.ImprovedPerformance.set(level, event.Value) } return nil } diff --git a/internal/repository/feature/feature_v2/eventstore.go b/internal/repository/feature/feature_v2/eventstore.go index 26f33661fe..bd8b22eec8 100644 --- a/internal/repository/feature/feature_v2/eventstore.go +++ b/internal/repository/feature/feature_v2/eventstore.go @@ -2,6 +2,7 @@ package feature_v2 import ( "github.com/zitadel/zitadel/internal/eventstore" + "github.com/zitadel/zitadel/internal/feature" ) func init() { @@ -12,6 +13,8 @@ func init() { eventstore.RegisterFilterEventMapper(AggregateType, SystemUserSchemaEventType, eventstore.GenericEventMapper[SetEvent[bool]]) eventstore.RegisterFilterEventMapper(AggregateType, SystemTokenExchangeEventType, eventstore.GenericEventMapper[SetEvent[bool]]) eventstore.RegisterFilterEventMapper(AggregateType, SystemActionsEventType, eventstore.GenericEventMapper[SetEvent[bool]]) + eventstore.RegisterFilterEventMapper(AggregateType, SystemImprovedPerformanceEventType, eventstore.GenericEventMapper[SetEvent[[]feature.ImprovedPerformanceType]]) + eventstore.RegisterFilterEventMapper(AggregateType, InstanceResetEventType, eventstore.GenericEventMapper[ResetEvent]) eventstore.RegisterFilterEventMapper(AggregateType, InstanceLoginDefaultOrgEventType, eventstore.GenericEventMapper[SetEvent[bool]]) eventstore.RegisterFilterEventMapper(AggregateType, InstanceTriggerIntrospectionProjectionsEventType, eventstore.GenericEventMapper[SetEvent[bool]]) @@ -19,4 +22,5 @@ func init() { eventstore.RegisterFilterEventMapper(AggregateType, InstanceUserSchemaEventType, eventstore.GenericEventMapper[SetEvent[bool]]) eventstore.RegisterFilterEventMapper(AggregateType, InstanceTokenExchangeEventType, eventstore.GenericEventMapper[SetEvent[bool]]) eventstore.RegisterFilterEventMapper(AggregateType, InstanceActionsEventType, eventstore.GenericEventMapper[SetEvent[bool]]) + eventstore.RegisterFilterEventMapper(AggregateType, InstanceImprovedPerformanceEventType, eventstore.GenericEventMapper[SetEvent[[]feature.ImprovedPerformanceType]]) } diff --git a/internal/repository/feature/feature_v2/feature.go b/internal/repository/feature/feature_v2/feature.go index c1f9add2d7..d5beea8bf4 100644 --- a/internal/repository/feature/feature_v2/feature.go +++ b/internal/repository/feature/feature_v2/feature.go @@ -18,6 +18,7 @@ var ( SystemUserSchemaEventType = setEventTypeFromFeature(feature.LevelSystem, feature.KeyUserSchema) SystemTokenExchangeEventType = setEventTypeFromFeature(feature.LevelSystem, feature.KeyTokenExchange) SystemActionsEventType = setEventTypeFromFeature(feature.LevelSystem, feature.KeyActions) + SystemImprovedPerformanceEventType = setEventTypeFromFeature(feature.LevelSystem, feature.KeyImprovedPerformance) InstanceResetEventType = resetEventTypeFromFeature(feature.LevelInstance) InstanceLoginDefaultOrgEventType = setEventTypeFromFeature(feature.LevelInstance, feature.KeyLoginDefaultOrg) @@ -26,6 +27,7 @@ var ( InstanceUserSchemaEventType = setEventTypeFromFeature(feature.LevelInstance, feature.KeyUserSchema) InstanceTokenExchangeEventType = setEventTypeFromFeature(feature.LevelInstance, feature.KeyTokenExchange) InstanceActionsEventType = setEventTypeFromFeature(feature.LevelInstance, feature.KeyActions) + InstanceImprovedPerformanceEventType = setEventTypeFromFeature(feature.LevelInstance, feature.KeyImprovedPerformance) ) const ( diff --git a/internal/v2/projection/org_primary_domain.go b/internal/v2/projection/org_primary_domain.go new file mode 100644 index 0000000000..3c83cd3152 --- /dev/null +++ b/internal/v2/projection/org_primary_domain.go @@ -0,0 +1,57 @@ +package projection + +import ( + "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/v2/org" +) + +type OrgPrimaryDomain struct { + projection + + id string + + Domain string +} + +func NewOrgPrimaryDomain(id string) *OrgPrimaryDomain { + return &OrgPrimaryDomain{ + id: id, + } +} + +func (p *OrgPrimaryDomain) Filter() []*eventstore.Filter { + return []*eventstore.Filter{ + eventstore.NewFilter( + eventstore.FilterPagination( + eventstore.GlobalPositionGreater(&p.position), + ), + eventstore.AppendAggregateFilter( + org.AggregateType, + eventstore.AggregateIDs(p.id), + eventstore.AppendEvent( + eventstore.SetEventTypes(org.DomainPrimarySetType), + ), + ), + ), + } +} + +func (p *OrgPrimaryDomain) Reduce(events ...*eventstore.StorageEvent) error { + for _, event := range events { + if !p.shouldReduce(event) { + continue + } + if event.Type != org.DomainPrimarySetType { + continue + } + e, err := org.DomainPrimarySetEventFromStorage(event) + if err != nil { + return err + } + + p.Domain = e.Payload.Name + p.projection.reduce(event) + } + + return nil +} diff --git a/internal/v2/projection/org_state.go b/internal/v2/projection/org_state.go new file mode 100644 index 0000000000..f67b51060a --- /dev/null +++ b/internal/v2/projection/org_state.go @@ -0,0 +1,67 @@ +package projection + +import ( + "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/v2/org" +) + +type OrgState struct { + projection + + id string + + org.State +} + +func NewStateProjection(id string) *OrgState { + // TODO: check buffer for id and return from buffer if exists + return &OrgState{ + id: id, + } +} + +func (p *OrgState) Filter() []*eventstore.Filter { + return []*eventstore.Filter{ + eventstore.NewFilter( + eventstore.FilterPagination( + eventstore.Descending(), + eventstore.GlobalPositionGreater(&p.position), + ), + eventstore.AppendAggregateFilter( + org.AggregateType, + eventstore.AggregateIDs(p.id), + eventstore.AppendEvent( + eventstore.SetEventTypes( + org.AddedType, + org.DeactivatedType, + org.ReactivatedType, + org.RemovedType, + ), + ), + ), + ), + } +} + +func (p *OrgState) Reduce(events ...*eventstore.StorageEvent) error { + for _, event := range events { + if !p.shouldReduce(event) { + continue + } + + switch event.Type { + case org.AddedType: + p.State = org.ActiveState + case org.DeactivatedType: + p.State = org.InactiveState + case org.ReactivatedType: + p.State = org.ActiveState + case org.RemovedType: + p.State = org.RemovedState + default: + continue + } + p.position = event.Position + } + return nil +} diff --git a/internal/v2/projection/projection.go b/internal/v2/projection/projection.go new file mode 100644 index 0000000000..980eaecc74 --- /dev/null +++ b/internal/v2/projection/projection.go @@ -0,0 +1,20 @@ +package projection + +import "github.com/zitadel/zitadel/internal/v2/eventstore" + +type projection struct { + instance string + position eventstore.GlobalPosition +} + +func (p *projection) reduce(event *eventstore.StorageEvent) { + if p.instance == "" { + p.instance = event.Aggregate.Instance + } + p.position = event.Position +} + +func (p *projection) shouldReduce(event *eventstore.StorageEvent) bool { + shouldReduce := p.instance == "" || p.instance == event.Aggregate.Instance + return shouldReduce && p.position.IsLess(event.Position) +} diff --git a/internal/v2/readmodel/org.go b/internal/v2/readmodel/org.go new file mode 100644 index 0000000000..94bcb21537 --- /dev/null +++ b/internal/v2/readmodel/org.go @@ -0,0 +1,68 @@ +package readmodel + +import ( + "time" + + "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/v2/org" + "github.com/zitadel/zitadel/internal/v2/projection" +) + +type Org struct { + ID string + Name string + PrimaryDomain *projection.OrgPrimaryDomain + State *projection.OrgState + + Sequence uint32 + CreationDate time.Time + ChangeDate time.Time + Owner string +} + +func NewOrg(id string) *Org { + return &Org{ + ID: id, + State: projection.NewStateProjection(id), + PrimaryDomain: projection.NewOrgPrimaryDomain(id), + } +} + +func (rm *Org) Filter() []*eventstore.Filter { + return []*eventstore.Filter{ + // we don't need the filters of the projections as we filter all events of the read model + eventstore.NewFilter( + eventstore.AppendAggregateFilter( + org.AggregateType, + eventstore.SetAggregateID(rm.ID), + ), + ), + } +} + +func (rm *Org) Reduce(events ...*eventstore.StorageEvent) error { + for _, event := range events { + switch event.Type { + case org.AddedType: + added, err := org.AddedEventFromStorage(event) + if err != nil { + return err + } + rm.Name = added.Payload.Name + rm.Owner = event.Aggregate.Owner + rm.CreationDate = event.CreatedAt + case org.ChangedType: + changed, err := org.ChangedEventFromStorage(event) + if err != nil { + return err + } + rm.Name = changed.Payload.Name + } + rm.Sequence = event.Sequence + rm.ChangeDate = event.CreatedAt + } + if err := rm.State.Reduce(events...); err != nil { + return err + } + return rm.PrimaryDomain.Reduce(events...) +} diff --git a/internal/v2/readmodel/query.go b/internal/v2/readmodel/query.go new file mode 100644 index 0000000000..73ca7cd415 --- /dev/null +++ b/internal/v2/readmodel/query.go @@ -0,0 +1,15 @@ +package readmodel + +import ( + "database/sql" + + "github.com/zitadel/zitadel/internal/v2/eventstore" +) + +type QueryOpt func(opts []eventstore.QueryOpt) []eventstore.QueryOpt + +func WithTx(tx *sql.Tx) QueryOpt { + return func(opts []eventstore.QueryOpt) []eventstore.QueryOpt { + return append(opts, eventstore.SetQueryTx(tx)) + } +} diff --git a/proto/zitadel/feature/v2beta/feature.proto b/proto/zitadel/feature/v2beta/feature.proto index 484bb667fc..082159b95b 100644 --- a/proto/zitadel/feature/v2beta/feature.proto +++ b/proto/zitadel/feature/v2beta/feature.proto @@ -33,3 +33,25 @@ message FeatureFlag { } ]; } + +message ImprovedPerformanceFeatureFlag { + repeated ImprovedPerformance execution_paths = 1 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "[1]"; + description: "Which of the performance improvements is enabled"; + } + ]; + + Source source = 2 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + description: "The source where the setting of the feature was defined. The source may be the resource itself or a resource owner through inheritance."; + } + ]; +} + +enum ImprovedPerformance { + IMPROVED_PERFORMANCE_UNSPECIFIED = 0; + // Uses the eventstore to query the org by id + // instead of the sql table. + IMPROVED_PERFORMANCE_ORG_BY_ID = 1; +} \ No newline at end of file diff --git a/proto/zitadel/feature/v2beta/instance.proto b/proto/zitadel/feature/v2beta/instance.proto index 3cfc2c4506..292fcc5101 100644 --- a/proto/zitadel/feature/v2beta/instance.proto +++ b/proto/zitadel/feature/v2beta/instance.proto @@ -49,6 +49,15 @@ message SetInstanceFeaturesRequest{ description: "Actions allow to manage data executions and targets. If the flag is enabled, you'll be able to use the new API and its features. Note that it is still in an early stage."; } ]; + + repeated ImprovedPerformance improved_performance = 7 [ + (validate.rules).repeated.unique = true, + (validate.rules).repeated.items.enum = {defined_only: true, not_in: [0]}, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "[1]"; + description: "Improves performance of specified execution paths."; + } + ]; } message SetInstanceFeaturesResponse { @@ -113,4 +122,11 @@ message GetInstanceFeaturesResponse { description: "Actions v2 allow to manage data executions and targets. If the flag is enabled, you'll be able to use the new API and its features. Note that it is still in an early stage."; } ]; + + ImprovedPerformanceFeatureFlag improved_performance = 8 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "[1]"; + description: "Improves performance of specified execution paths."; + } + ]; } diff --git a/proto/zitadel/feature/v2beta/system.proto b/proto/zitadel/feature/v2beta/system.proto index 8f98eb0625..048f25391f 100644 --- a/proto/zitadel/feature/v2beta/system.proto +++ b/proto/zitadel/feature/v2beta/system.proto @@ -46,13 +46,21 @@ message SetSystemFeaturesRequest{ } ]; - optional bool actions = 6 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { example: "true"; description: "Actions allow to manage data executions and targets. If the flag is enabled, you'll be able to use the new API and its features. Note that it is still in an early stage."; } ]; + + repeated ImprovedPerformance improved_performance = 7 [ + (validate.rules).repeated.unique = true, + (validate.rules).repeated.items.enum = {defined_only: true, not_in: [0]}, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "[1]"; + description: "Improves performance of specified execution paths."; + } + ]; } message SetSystemFeaturesResponse { @@ -110,4 +118,11 @@ message GetSystemFeaturesResponse { description: "Actions v2 allow to manage data executions and targets. If the flag is enabled, you'll be able to use the new API and its features. Note that it is still in an early stage."; } ]; + + ImprovedPerformanceFeatureFlag improved_performance = 8 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "[1]"; + description: "Improves performance of specified execution paths."; + } + ]; }