mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 20:37:30 +00:00
perf(import): optimize search for domains claimed by other organizations (#8200)
# Which Problems Are Solved Improve the performance of human imports by optimizing the query that finds domains claimed by other organizations. # How the Problems Are Solved Use the fields search table introduced in https://github.com/zitadel/zitadel/pull/8191 by storing each organization domain as Object ID and the verified status as field value. # Additional Changes - Feature flag for this optimization # Additional Context - Performance improvements for import are evaluated and acted upon internally at the moment --------- Co-authored-by: adlerhurst <silvan.reusser@gmail.com>
This commit is contained in:
42
cmd/setup/30.go
Normal file
42
cmd/setup/30.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package setup
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/api/authz"
|
||||||
|
"github.com/zitadel/zitadel/internal/eventstore"
|
||||||
|
"github.com/zitadel/zitadel/internal/query/projection"
|
||||||
|
"github.com/zitadel/zitadel/internal/repository/instance"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FillFieldsForOrgDomainVerified struct {
|
||||||
|
eventstore *eventstore.Eventstore
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mig *FillFieldsForOrgDomainVerified) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||||
|
instances, err := mig.eventstore.InstanceIDs(
|
||||||
|
ctx,
|
||||||
|
0,
|
||||||
|
true,
|
||||||
|
eventstore.NewSearchQueryBuilder(eventstore.ColumnsInstanceIDs).
|
||||||
|
OrderDesc().
|
||||||
|
AddQuery().
|
||||||
|
AggregateTypes("instance").
|
||||||
|
EventTypes(instance.InstanceAddedEventType).
|
||||||
|
Builder(),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, instance := range instances {
|
||||||
|
ctx := authz.WithInstanceID(ctx, instance)
|
||||||
|
if err := projection.OrgDomainVerifiedFields.Trigger(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mig *FillFieldsForOrgDomainVerified) String() string {
|
||||||
|
return "30_fill_fields_for_org_domain_verified"
|
||||||
|
}
|
@@ -113,6 +113,7 @@ type Steps struct {
|
|||||||
s27IDPTemplate6SAMLNameIDFormat *IDPTemplate6SAMLNameIDFormat
|
s27IDPTemplate6SAMLNameIDFormat *IDPTemplate6SAMLNameIDFormat
|
||||||
s28AddFieldTable *AddFieldTable
|
s28AddFieldTable *AddFieldTable
|
||||||
s29FillFieldsForProjectGrant *FillFieldsForProjectGrant
|
s29FillFieldsForProjectGrant *FillFieldsForProjectGrant
|
||||||
|
s30FillFieldsForOrgDomainVerified *FillFieldsForOrgDomainVerified
|
||||||
}
|
}
|
||||||
|
|
||||||
func MustNewSteps(v *viper.Viper) *Steps {
|
func MustNewSteps(v *viper.Viper) *Steps {
|
||||||
|
@@ -158,6 +158,7 @@ func Setup(ctx context.Context, config *Config, steps *Steps, masterKey string)
|
|||||||
steps.s27IDPTemplate6SAMLNameIDFormat = &IDPTemplate6SAMLNameIDFormat{dbClient: esPusherDBClient}
|
steps.s27IDPTemplate6SAMLNameIDFormat = &IDPTemplate6SAMLNameIDFormat{dbClient: esPusherDBClient}
|
||||||
steps.s28AddFieldTable = &AddFieldTable{dbClient: esPusherDBClient}
|
steps.s28AddFieldTable = &AddFieldTable{dbClient: esPusherDBClient}
|
||||||
steps.s29FillFieldsForProjectGrant = &FillFieldsForProjectGrant{eventstore: eventstoreClient}
|
steps.s29FillFieldsForProjectGrant = &FillFieldsForProjectGrant{eventstore: eventstoreClient}
|
||||||
|
steps.s30FillFieldsForOrgDomainVerified = &FillFieldsForOrgDomainVerified{eventstore: eventstoreClient}
|
||||||
|
|
||||||
err = projection.Create(ctx, projectionDBClient, eventstoreClient, config.Projections, nil, nil, nil)
|
err = projection.Create(ctx, projectionDBClient, eventstoreClient, config.Projections, nil, nil, nil)
|
||||||
logging.OnError(err).Fatal("unable to start projections")
|
logging.OnError(err).Fatal("unable to start projections")
|
||||||
@@ -198,6 +199,7 @@ func Setup(ctx context.Context, config *Config, steps *Steps, masterKey string)
|
|||||||
steps.s24AddActorToAuthTokens,
|
steps.s24AddActorToAuthTokens,
|
||||||
steps.s26AuthUsers3,
|
steps.s26AuthUsers3,
|
||||||
steps.s29FillFieldsForProjectGrant,
|
steps.s29FillFieldsForProjectGrant,
|
||||||
|
steps.s30FillFieldsForOrgDomainVerified,
|
||||||
} {
|
} {
|
||||||
mustExecuteMigration(ctx, eventstoreClient, step, "migration failed")
|
mustExecuteMigration(ctx, eventstoreClient, step, "migration failed")
|
||||||
}
|
}
|
||||||
|
@@ -115,6 +115,8 @@ func improvedPerformanceTypeToPb(typ feature.ImprovedPerformanceType) feature_pb
|
|||||||
return feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_PROJECT
|
return feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_PROJECT
|
||||||
case feature.ImprovedPerformanceTypeUserGrant:
|
case feature.ImprovedPerformanceTypeUserGrant:
|
||||||
return feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_USER_GRANT
|
return feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_USER_GRANT
|
||||||
|
case feature.ImprovedPerformanceTypeOrgDomainVerified:
|
||||||
|
return feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_ORG_DOMAIN_VERIFIED
|
||||||
default:
|
default:
|
||||||
return feature_pb.ImprovedPerformance(typ)
|
return feature_pb.ImprovedPerformance(typ)
|
||||||
}
|
}
|
||||||
@@ -145,6 +147,8 @@ func improvedPerformanceToDomain(typ feature_pb.ImprovedPerformance) feature.Imp
|
|||||||
return feature.ImprovedPerformanceTypeProject
|
return feature.ImprovedPerformanceTypeProject
|
||||||
case feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_USER_GRANT:
|
case feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_USER_GRANT:
|
||||||
return feature.ImprovedPerformanceTypeUserGrant
|
return feature.ImprovedPerformanceTypeUserGrant
|
||||||
|
case feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_ORG_DOMAIN_VERIFIED:
|
||||||
|
return feature.ImprovedPerformanceTypeOrgDomainVerified
|
||||||
default:
|
default:
|
||||||
return feature.ImprovedPerformanceTypeUnknown
|
return feature.ImprovedPerformanceTypeUnknown
|
||||||
}
|
}
|
||||||
|
@@ -13,6 +13,8 @@ import (
|
|||||||
"github.com/zitadel/zitadel/internal/crypto"
|
"github.com/zitadel/zitadel/internal/crypto"
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/eventstore"
|
"github.com/zitadel/zitadel/internal/eventstore"
|
||||||
|
"github.com/zitadel/zitadel/internal/feature"
|
||||||
|
"github.com/zitadel/zitadel/internal/query/projection"
|
||||||
"github.com/zitadel/zitadel/internal/repository/org"
|
"github.com/zitadel/zitadel/internal/repository/org"
|
||||||
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
||||||
"github.com/zitadel/zitadel/internal/zerrors"
|
"github.com/zitadel/zitadel/internal/zerrors"
|
||||||
@@ -390,3 +392,65 @@ func (c *Commands) getOrgDomainWriteModel(ctx context.Context, orgID, domain str
|
|||||||
}
|
}
|
||||||
return domainWriteModel, nil
|
return domainWriteModel, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type OrgDomainVerified struct {
|
||||||
|
OrgID string
|
||||||
|
Domain string
|
||||||
|
Verified bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Commands) searchOrgDomainVerifiedByDomain(ctx context.Context, domain string) (_ *OrgDomainVerified, err error) {
|
||||||
|
if !authz.GetFeatures(ctx).ShouldUseImprovedPerformance(feature.ImprovedPerformanceTypeOrgDomainVerified) {
|
||||||
|
return c.searchOrgDomainVerifiedByDomainOld(ctx, domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, span := tracing.NewSpan(ctx)
|
||||||
|
defer func() { span.EndWithError(err) }()
|
||||||
|
|
||||||
|
condition := map[eventstore.FieldType]any{
|
||||||
|
eventstore.FieldTypeAggregateType: org.AggregateType,
|
||||||
|
eventstore.FieldTypeObjectType: org.OrgDomainSearchType,
|
||||||
|
eventstore.FieldTypeObjectID: domain,
|
||||||
|
eventstore.FieldTypeObjectRevision: org.OrgDomainObjectRevision,
|
||||||
|
eventstore.FieldTypeFieldName: org.OrgDomainVerifiedSearchField,
|
||||||
|
}
|
||||||
|
|
||||||
|
results, err := c.eventstore.Search(ctx, condition)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(results) == 0 {
|
||||||
|
_ = projection.OrgDomainVerifiedFields.Trigger(ctx)
|
||||||
|
results, err = c.eventstore.Search(ctx, condition)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
orgDomain := new(OrgDomainVerified)
|
||||||
|
for _, result := range results {
|
||||||
|
orgDomain.OrgID = result.Aggregate.ID
|
||||||
|
if err = result.Value.Unmarshal(&orgDomain.Verified); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return orgDomain, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Commands) searchOrgDomainVerifiedByDomainOld(ctx context.Context, domain string) (_ *OrgDomainVerified, err error) {
|
||||||
|
ctx, span := tracing.NewSpan(ctx)
|
||||||
|
defer func() { span.EndWithError(err) }()
|
||||||
|
|
||||||
|
writeModel := NewOrgDomainVerifiedWriteModel(domain)
|
||||||
|
err = c.eventstore.FilterToQueryReducer(ctx, writeModel)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &OrgDomainVerified{
|
||||||
|
OrgID: writeModel.ResourceOwner,
|
||||||
|
Domain: writeModel.Domain,
|
||||||
|
Verified: writeModel.Verified,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
@@ -40,17 +40,8 @@ func (c *Commands) ChangeUsername(ctx context.Context, orgID, userID, userName s
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, zerrors.ThrowPreconditionFailed(err, "COMMAND-38fnu", "Errors.Org.DomainPolicy.NotExisting")
|
return nil, zerrors.ThrowPreconditionFailed(err, "COMMAND-38fnu", "Errors.Org.DomainPolicy.NotExisting")
|
||||||
}
|
}
|
||||||
if !domainPolicy.UserLoginMustBeDomain {
|
if err = c.userValidateDomain(ctx, orgID, userName, domainPolicy.UserLoginMustBeDomain); err != nil {
|
||||||
index := strings.LastIndex(userName, "@")
|
return nil, err
|
||||||
if index > 1 {
|
|
||||||
domainCheck := NewOrgDomainVerifiedWriteModel(userName[index+1:])
|
|
||||||
if err := c.eventstore.FilterToQueryReducer(ctx, domainCheck); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if domainCheck.Verified && domainCheck.ResourceOwner != orgID {
|
|
||||||
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-Di2ei", "Errors.User.DomainNotAllowedAsUsername")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
userAgg := UserAggregateFromWriteModel(&existingUser.WriteModel)
|
userAgg := UserAggregateFromWriteModel(&existingUser.WriteModel)
|
||||||
|
|
||||||
|
@@ -362,7 +362,7 @@ func addHumanCommandPassword(ctx context.Context, filter preparation.FilterToQue
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) userValidateDomain(ctx context.Context, resourceOwner string, username string, mustBeDomain bool) error {
|
func (c *Commands) userValidateDomain(ctx context.Context, resourceOwner string, username string, mustBeDomain bool) (err error) {
|
||||||
if mustBeDomain {
|
if mustBeDomain {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -372,12 +372,12 @@ func (c *Commands) userValidateDomain(ctx context.Context, resourceOwner string,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
domainCheck, err := c.orgDomainVerifiedWriteModel(ctx, username[index+1:])
|
domainCheck, err := c.searchOrgDomainVerifiedByDomain(ctx, username[index+1:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if domainCheck.Verified && domainCheck.ResourceOwner != resourceOwner {
|
if domainCheck.Verified && domainCheck.OrgID != resourceOwner {
|
||||||
return zerrors.ThrowInvalidArgument(nil, "COMMAND-SFd21", "Errors.User.DomainNotAllowedAsUsername")
|
return zerrors.ThrowInvalidArgument(nil, "COMMAND-SFd21", "Errors.User.DomainNotAllowedAsUsername")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -479,7 +479,7 @@ func (c *Commands) importHuman(ctx context.Context, orgID string, human *domain.
|
|||||||
if orgID == "" {
|
if orgID == "" {
|
||||||
return nil, nil, nil, "", zerrors.ThrowInvalidArgument(nil, "COMMAND-00p2b", "Errors.Org.Empty")
|
return nil, nil, nil, "", zerrors.ThrowInvalidArgument(nil, "COMMAND-00p2b", "Errors.Org.Empty")
|
||||||
}
|
}
|
||||||
if err := human.Normalize(); err != nil {
|
if err = human.Normalize(); err != nil {
|
||||||
return nil, nil, nil, "", err
|
return nil, nil, nil, "", err
|
||||||
}
|
}
|
||||||
events, humanWriteModel, err = c.createHuman(ctx, orgID, human, links, passwordless, domainPolicy, pwPolicy, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator)
|
events, humanWriteModel, err = c.createHuman(ctx, orgID, human, links, passwordless, domainPolicy, pwPolicy, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator)
|
||||||
@@ -497,24 +497,17 @@ func (c *Commands) importHuman(ctx context.Context, orgID string, human *domain.
|
|||||||
return events, humanWriteModel, passwordlessCodeWriteModel, code, nil
|
return events, humanWriteModel, passwordlessCodeWriteModel, code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:gocognit
|
|
||||||
func (c *Commands) createHuman(ctx context.Context, orgID string, human *domain.Human, links []*domain.UserIDPLink, passwordless bool, domainPolicy *domain.DomainPolicy, pwPolicy *domain.PasswordComplexityPolicy, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator crypto.Generator) (events []eventstore.Command, addedHuman *HumanWriteModel, err error) {
|
func (c *Commands) createHuman(ctx context.Context, orgID string, human *domain.Human, links []*domain.UserIDPLink, passwordless bool, domainPolicy *domain.DomainPolicy, pwPolicy *domain.PasswordComplexityPolicy, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator crypto.Generator) (events []eventstore.Command, addedHuman *HumanWriteModel, err error) {
|
||||||
if err := human.CheckDomainPolicy(domainPolicy); err != nil {
|
ctx, span := tracing.NewSpan(ctx)
|
||||||
|
defer func() { span.EndWithError(err) }()
|
||||||
|
|
||||||
|
if err = human.CheckDomainPolicy(domainPolicy); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
human.Username = strings.TrimSpace(human.Username)
|
human.Username = strings.TrimSpace(human.Username)
|
||||||
human.EmailAddress = human.EmailAddress.Normalize()
|
human.EmailAddress = human.EmailAddress.Normalize()
|
||||||
if !domainPolicy.UserLoginMustBeDomain {
|
if err = c.userValidateDomain(ctx, orgID, human.Username, domainPolicy.UserLoginMustBeDomain); err != nil {
|
||||||
index := strings.LastIndex(human.Username, "@")
|
return nil, nil, err
|
||||||
if index > 1 {
|
|
||||||
domainCheck := NewOrgDomainVerifiedWriteModel(human.Username[index+1:])
|
|
||||||
if err := c.eventstore.FilterToQueryReducer(ctx, domainCheck); err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
if domainCheck.Verified && domainCheck.ResourceOwner != orgID {
|
|
||||||
return nil, nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-SFd21", "Errors.User.DomainNotAllowedAsUsername")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if human.AggregateID == "" {
|
if human.AggregateID == "" {
|
||||||
|
@@ -434,15 +434,3 @@ func (c *Commands) userHumanWriteModel(ctx context.Context, userID string, profi
|
|||||||
}
|
}
|
||||||
return writeModel, nil
|
return writeModel, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) orgDomainVerifiedWriteModel(ctx context.Context, domain string) (writeModel *OrgDomainVerifiedWriteModel, err error) {
|
|
||||||
ctx, span := tracing.NewSpan(ctx)
|
|
||||||
defer func() { span.EndWithError(err) }()
|
|
||||||
|
|
||||||
writeModel = NewOrgDomainVerifiedWriteModel(domain)
|
|
||||||
err = c.eventstore.FilterToQueryReducer(ctx, writeModel)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return writeModel, nil
|
|
||||||
}
|
|
||||||
|
@@ -2,7 +2,6 @@ package command
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/eventstore"
|
"github.com/zitadel/zitadel/internal/eventstore"
|
||||||
"github.com/zitadel/zitadel/internal/repository/user"
|
"github.com/zitadel/zitadel/internal/repository/user"
|
||||||
@@ -19,17 +18,8 @@ func (c *Commands) changeUsername(ctx context.Context, cmds []eventstore.Command
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return cmds, zerrors.ThrowPreconditionFailed(err, "COMMAND-79pv6e1q62", "Errors.Org.DomainPolicy.NotExisting")
|
return cmds, zerrors.ThrowPreconditionFailed(err, "COMMAND-79pv6e1q62", "Errors.Org.DomainPolicy.NotExisting")
|
||||||
}
|
}
|
||||||
if !domainPolicy.UserLoginMustBeDomain {
|
if err = c.userValidateDomain(ctx, orgID, userName, domainPolicy.UserLoginMustBeDomain); err != nil {
|
||||||
index := strings.LastIndex(userName, "@")
|
return cmds, err
|
||||||
if index > 1 {
|
|
||||||
domainCheck := NewOrgDomainVerifiedWriteModel(userName[index+1:])
|
|
||||||
if err := c.eventstore.FilterToQueryReducer(ctx, domainCheck); err != nil {
|
|
||||||
return cmds, err
|
|
||||||
}
|
|
||||||
if domainCheck.Verified && domainCheck.ResourceOwner != orgID {
|
|
||||||
return cmds, zerrors.ThrowInvalidArgument(nil, "COMMAND-Di2ei", "Errors.User.DomainNotAllowedAsUsername")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return append(cmds,
|
return append(cmds,
|
||||||
user.NewUsernameChangedEvent(ctx, &wm.Aggregate().Aggregate, wm.UserName, userName, domainPolicy.UserLoginMustBeDomain),
|
user.NewUsernameChangedEvent(ctx, &wm.Aggregate().Aggregate, wm.UserName, userName, domainPolicy.UserLoginMustBeDomain),
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
package feature
|
package feature
|
||||||
|
|
||||||
|
import "slices"
|
||||||
|
|
||||||
//go:generate enumer -type Key -transform snake -trimprefix Key
|
//go:generate enumer -type Key -transform snake -trimprefix Key
|
||||||
type Key int
|
type Key int
|
||||||
|
|
||||||
@@ -45,13 +47,9 @@ const (
|
|||||||
ImprovedPerformanceTypeProjectGrant
|
ImprovedPerformanceTypeProjectGrant
|
||||||
ImprovedPerformanceTypeProject
|
ImprovedPerformanceTypeProject
|
||||||
ImprovedPerformanceTypeUserGrant
|
ImprovedPerformanceTypeUserGrant
|
||||||
|
ImprovedPerformanceTypeOrgDomainVerified
|
||||||
)
|
)
|
||||||
|
|
||||||
func (f Features) ShouldUseImprovedPerformance(typ ImprovedPerformanceType) bool {
|
func (f Features) ShouldUseImprovedPerformance(typ ImprovedPerformanceType) bool {
|
||||||
for _, improvedType := range f.ImprovedPerformance {
|
return slices.Contains(f.ImprovedPerformance, typ)
|
||||||
if improvedType == typ {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
@@ -7,13 +7,32 @@ import (
|
|||||||
"github.com/zitadel/zitadel/internal/repository/project"
|
"github.com/zitadel/zitadel/internal/repository/project"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
fieldsProjectGrant = "project_grant_fields"
|
||||||
|
fieldsOrgDomainVerified = "org_domain_verified_fields"
|
||||||
|
)
|
||||||
|
|
||||||
func newFillProjectGrantFields(config handler.Config) *handler.FieldHandler {
|
func newFillProjectGrantFields(config handler.Config) *handler.FieldHandler {
|
||||||
return handler.NewFieldHandler(
|
return handler.NewFieldHandler(
|
||||||
&config,
|
&config,
|
||||||
"project_grant_fields",
|
fieldsProjectGrant,
|
||||||
map[eventstore.AggregateType][]eventstore.EventType{
|
map[eventstore.AggregateType][]eventstore.EventType{
|
||||||
org.AggregateType: nil,
|
org.AggregateType: nil,
|
||||||
project.AggregateType: nil,
|
project.AggregateType: nil,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newFillOrgDomainVerifiedFields(config handler.Config) *handler.FieldHandler {
|
||||||
|
return handler.NewFieldHandler(
|
||||||
|
&config,
|
||||||
|
fieldsOrgDomainVerified,
|
||||||
|
map[eventstore.AggregateType][]eventstore.EventType{
|
||||||
|
org.AggregateType: {
|
||||||
|
org.OrgDomainAddedEventType,
|
||||||
|
org.OrgDomainVerifiedEventType,
|
||||||
|
org.OrgDomainRemovedEventType,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@@ -78,7 +78,8 @@ var (
|
|||||||
ExecutionProjection *handler.Handler
|
ExecutionProjection *handler.Handler
|
||||||
UserSchemaProjection *handler.Handler
|
UserSchemaProjection *handler.Handler
|
||||||
|
|
||||||
ProjectGrantFields *handler.FieldHandler
|
ProjectGrantFields *handler.FieldHandler
|
||||||
|
OrgDomainVerifiedFields *handler.FieldHandler
|
||||||
)
|
)
|
||||||
|
|
||||||
type projection interface {
|
type projection interface {
|
||||||
@@ -161,7 +162,8 @@ func Create(ctx context.Context, sqlClient *database.DB, es handler.EventStore,
|
|||||||
ExecutionProjection = newExecutionProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["executions"]))
|
ExecutionProjection = newExecutionProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["executions"]))
|
||||||
UserSchemaProjection = newUserSchemaProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["user_schemas"]))
|
UserSchemaProjection = newUserSchemaProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["user_schemas"]))
|
||||||
|
|
||||||
ProjectGrantFields = newFillProjectGrantFields(applyCustomConfig(projectionConfig, config.Customizations["project_grant_fields"]))
|
ProjectGrantFields = newFillProjectGrantFields(applyCustomConfig(projectionConfig, config.Customizations[fieldsProjectGrant]))
|
||||||
|
OrgDomainVerifiedFields = newFillOrgDomainVerifiedFields(applyCustomConfig(projectionConfig, config.Customizations[fieldsOrgDomainVerified]))
|
||||||
|
|
||||||
newProjectionsList()
|
newProjectionsList()
|
||||||
return nil
|
return nil
|
||||||
|
@@ -18,6 +18,10 @@ const (
|
|||||||
OrgDomainVerifiedEventType = domainEventPrefix + "verified"
|
OrgDomainVerifiedEventType = domainEventPrefix + "verified"
|
||||||
OrgDomainPrimarySetEventType = domainEventPrefix + "primary.set"
|
OrgDomainPrimarySetEventType = domainEventPrefix + "primary.set"
|
||||||
OrgDomainRemovedEventType = domainEventPrefix + "removed"
|
OrgDomainRemovedEventType = domainEventPrefix + "removed"
|
||||||
|
|
||||||
|
OrgDomainSearchType = "org_domain"
|
||||||
|
OrgDomainVerifiedSearchField = "verified"
|
||||||
|
OrgDomainObjectRevision = uint8(1)
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewAddOrgDomainUniqueConstraint(orgDomain string) *eventstore.UniqueConstraint {
|
func NewAddOrgDomainUniqueConstraint(orgDomain string) *eventstore.UniqueConstraint {
|
||||||
@@ -47,6 +51,28 @@ func (e *DomainAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *DomainAddedEvent) Fields() []*eventstore.FieldOperation {
|
||||||
|
return []*eventstore.FieldOperation{
|
||||||
|
eventstore.SetField(
|
||||||
|
e.Aggregate(),
|
||||||
|
domainSearchObject(e.Domain),
|
||||||
|
OrgDomainVerifiedSearchField,
|
||||||
|
&eventstore.Value{
|
||||||
|
Value: false,
|
||||||
|
ShouldIndex: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
eventstore.FieldTypeInstanceID,
|
||||||
|
eventstore.FieldTypeResourceOwner,
|
||||||
|
eventstore.FieldTypeAggregateType,
|
||||||
|
eventstore.FieldTypeAggregateID,
|
||||||
|
eventstore.FieldTypeObjectType,
|
||||||
|
eventstore.FieldTypeObjectID,
|
||||||
|
eventstore.FieldTypeFieldName,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func NewDomainAddedEvent(ctx context.Context, aggregate *eventstore.Aggregate, domain string) *DomainAddedEvent {
|
func NewDomainAddedEvent(ctx context.Context, aggregate *eventstore.Aggregate, domain string) *DomainAddedEvent {
|
||||||
return &DomainAddedEvent{
|
return &DomainAddedEvent{
|
||||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||||
@@ -167,6 +193,28 @@ func (e *DomainVerifiedEvent) UniqueConstraints() []*eventstore.UniqueConstraint
|
|||||||
return []*eventstore.UniqueConstraint{NewAddOrgDomainUniqueConstraint(e.Domain)}
|
return []*eventstore.UniqueConstraint{NewAddOrgDomainUniqueConstraint(e.Domain)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *DomainVerifiedEvent) Fields() []*eventstore.FieldOperation {
|
||||||
|
return []*eventstore.FieldOperation{
|
||||||
|
eventstore.SetField(
|
||||||
|
e.Aggregate(),
|
||||||
|
domainSearchObject(e.Domain),
|
||||||
|
OrgDomainVerifiedSearchField,
|
||||||
|
&eventstore.Value{
|
||||||
|
Value: true,
|
||||||
|
ShouldIndex: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
eventstore.FieldTypeInstanceID,
|
||||||
|
eventstore.FieldTypeResourceOwner,
|
||||||
|
eventstore.FieldTypeAggregateType,
|
||||||
|
eventstore.FieldTypeAggregateID,
|
||||||
|
eventstore.FieldTypeObjectType,
|
||||||
|
eventstore.FieldTypeObjectID,
|
||||||
|
eventstore.FieldTypeFieldName,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func NewDomainVerifiedEvent(ctx context.Context, aggregate *eventstore.Aggregate, domain string) *DomainVerifiedEvent {
|
func NewDomainVerifiedEvent(ctx context.Context, aggregate *eventstore.Aggregate, domain string) *DomainVerifiedEvent {
|
||||||
return &DomainVerifiedEvent{
|
return &DomainVerifiedEvent{
|
||||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||||
@@ -245,6 +293,28 @@ func (e *DomainRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint
|
|||||||
return []*eventstore.UniqueConstraint{NewRemoveOrgDomainUniqueConstraint(e.Domain)}
|
return []*eventstore.UniqueConstraint{NewRemoveOrgDomainUniqueConstraint(e.Domain)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *DomainRemovedEvent) Fields() []*eventstore.FieldOperation {
|
||||||
|
return []*eventstore.FieldOperation{
|
||||||
|
eventstore.SetField(
|
||||||
|
e.Aggregate(),
|
||||||
|
domainSearchObject(e.Domain),
|
||||||
|
OrgDomainVerifiedSearchField,
|
||||||
|
&eventstore.Value{
|
||||||
|
Value: false,
|
||||||
|
ShouldIndex: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
eventstore.FieldTypeInstanceID,
|
||||||
|
eventstore.FieldTypeResourceOwner,
|
||||||
|
eventstore.FieldTypeAggregateType,
|
||||||
|
eventstore.FieldTypeAggregateID,
|
||||||
|
eventstore.FieldTypeObjectType,
|
||||||
|
eventstore.FieldTypeObjectID,
|
||||||
|
eventstore.FieldTypeFieldName,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func NewDomainRemovedEvent(ctx context.Context, aggregate *eventstore.Aggregate, domain string, verified bool) *DomainRemovedEvent {
|
func NewDomainRemovedEvent(ctx context.Context, aggregate *eventstore.Aggregate, domain string, verified bool) *DomainRemovedEvent {
|
||||||
return &DomainRemovedEvent{
|
return &DomainRemovedEvent{
|
||||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||||
@@ -268,3 +338,11 @@ func DomainRemovedEventMapper(event eventstore.Event) (eventstore.Event, error)
|
|||||||
|
|
||||||
return orgDomainRemoved, nil
|
return orgDomainRemoved, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func domainSearchObject(domain string) eventstore.Object {
|
||||||
|
return eventstore.Object{
|
||||||
|
Type: OrgDomainSearchType,
|
||||||
|
ID: domain,
|
||||||
|
Revision: OrgDomainObjectRevision,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -60,4 +60,9 @@ enum ImprovedPerformance {
|
|||||||
IMPROVED_PERFORMANCE_PROJECT_GRANT = 2;
|
IMPROVED_PERFORMANCE_PROJECT_GRANT = 2;
|
||||||
IMPROVED_PERFORMANCE_PROJECT = 3;
|
IMPROVED_PERFORMANCE_PROJECT = 3;
|
||||||
IMPROVED_PERFORMANCE_USER_GRANT = 4;
|
IMPROVED_PERFORMANCE_USER_GRANT = 4;
|
||||||
|
|
||||||
|
// Improve performance on write side when
|
||||||
|
// users are checked against verified domains
|
||||||
|
// from other organizations.
|
||||||
|
IMPROVED_PERFORMANCE_ORG_DOMAIN_VERIFIED = 5;
|
||||||
}
|
}
|
Reference in New Issue
Block a user