Skip to content

Commit

Permalink
instance as projection
Browse files Browse the repository at this point in the history
  • Loading branch information
adlerhurst committed Dec 7, 2022
1 parent 28edddd commit e5cc00f
Show file tree
Hide file tree
Showing 3 changed files with 292 additions and 34 deletions.
160 changes: 160 additions & 0 deletions internal/projection/instance.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package projection

import (
"context"
"time"

"golang.org/x/text/language"

"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/repository/instance"
)

var _ Projection = (*Instance)(nil)

type Instance struct {
ID string

CreationDate time.Time
ChangeDate time.Time
Sequence uint64
Name string
Removed bool

DefaultOrgID string
IAMProjectID string
ConsoleID string
ConsoleAppID string
DefaultLang language.Tag
Domains []*InstanceDomain
}

func NewInstance(id string) *Instance {
return &Instance{
ID: id,
}
}

type InstanceDomain struct {
CreationDate time.Time
ChangeDate time.Time
Sequence uint64
Domain string
InstanceID string
IsGenerated bool
IsPrimary bool
}

func (i *Instance) Reduce(events []eventstore.Event) {
for _, event := range events {
i.ChangeDate = event.CreationDate()
i.Sequence = event.Sequence()

switch e := event.(type) {
case *instance.InstanceAddedEvent:
i.reduceInstanceAddedEvent(e)
case *instance.InstanceChangedEvent:
i.reduceInstanceChangedEvent(e)
case *instance.InstanceRemovedEvent:
i.reduceInstanceRemovedEvent(e)
case *instance.DefaultOrgSetEvent:
i.reduceDefaultOrgSetEvent(e)
case *instance.ProjectSetEvent:
i.reduceProjectSetEvent(e)
case *instance.ConsoleSetEvent:
i.reduceConsoleSetEvent(e)
case *instance.DefaultLanguageSetEvent:
i.reduceDefaultLanguageSetEvent(e)
case *instance.DomainAddedEvent:
i.reduceDomainAddedEvent(e)
case *instance.DomainPrimarySetEvent:
i.reduceDomainPrimarySetEvent(e)
case *instance.DomainRemovedEvent:
i.reduceDomainRemovedEvent(e)
}
}
}

func (i *Instance) SearchQuery(context.Context) *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
InstanceID(i.ID).
OrderAsc().
AddQuery().
AggregateIDs(i.ID).
AggregateTypes(instance.AggregateType).
EventTypes(
instance.InstanceAddedEventType,
instance.InstanceChangedEventType,
instance.InstanceRemovedEventType,

instance.DefaultOrgSetEventType,
instance.ProjectSetEventType,
instance.ConsoleSetEventType,
instance.DefaultLanguageSetEventType,

instance.InstanceDomainAddedEventType,
instance.InstanceDomainPrimarySetEventType,
instance.InstanceDomainRemovedEventType,
).
Builder()
}

func (i *Instance) reduceInstanceAddedEvent(event *instance.InstanceAddedEvent) {
i.ID = event.Aggregate().ID
i.CreationDate = event.CreationDate()
i.Name = event.Name
}

func (i *Instance) reduceInstanceChangedEvent(event *instance.InstanceChangedEvent) {
i.Name = event.Name
}

func (i *Instance) reduceInstanceRemovedEvent(event *instance.InstanceRemovedEvent) {
i.Removed = true
}

func (i *Instance) reduceDefaultOrgSetEvent(event *instance.DefaultOrgSetEvent) {
i.DefaultOrgID = event.OrgID
}

func (i *Instance) reduceProjectSetEvent(event *instance.ProjectSetEvent) {
i.IAMProjectID = event.ProjectID
}

func (i *Instance) reduceConsoleSetEvent(event *instance.ConsoleSetEvent) {
i.ConsoleAppID = event.AppID
i.ConsoleID = event.ClientID
}

func (i *Instance) reduceDefaultLanguageSetEvent(event *instance.DefaultLanguageSetEvent) {
i.DefaultLang = event.Language
}

func (i *Instance) reduceDomainAddedEvent(event *instance.DomainAddedEvent) {
i.Domains = append(i.Domains, &InstanceDomain{
CreationDate: event.CreationDate(),
ChangeDate: event.CreationDate(),
Sequence: event.Sequence(),
Domain: event.Domain,
InstanceID: i.ID,
IsGenerated: event.Generated,
})
}

func (i *Instance) reduceDomainPrimarySetEvent(event *instance.DomainPrimarySetEvent) {
for _, domain := range i.Domains {
domain.IsPrimary = domain.Domain == event.Domain
}
}

func (i *Instance) reduceDomainRemovedEvent(event *instance.DomainRemovedEvent) {
for idx, domain := range i.Domains {
if domain.Domain != event.Domain {
continue
}
i.Domains[idx] = i.Domains[len(i.Domains)-1]
i.Domains[len(i.Domains)-1] = nil
i.Domains = i.Domains[:len(i.Domains)-1]
return
}
}
65 changes: 65 additions & 0 deletions internal/projection/instance_domain.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package projection

import (
"context"

"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/repository/instance"
)

var _ Projection = (*SearchInstanceDomain)(nil)

type SearchInstanceDomain struct {
host string
removedInstances []string

InstanceID string
}

func NewSearchInstanceDomain(host string) *SearchInstanceDomain {
return &SearchInstanceDomain{
host: host,
}
}

func (domains *SearchInstanceDomain) Reduce(events []eventstore.Event) {
for _, event := range events {
switch e := event.(type) {
case *instance.DomainAddedEvent:
if ok := domains.reduceAdded(e); ok {
return
}
case *instance.DomainRemovedEvent:
domains.reduceRemoved(e)
}
}
}

func (domains *SearchInstanceDomain) SearchQuery(context.Context) *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
OrderDesc().
AddQuery().
AggregateTypes(instance.AggregateType).
EventTypes(
instance.InstanceDomainAddedEventType,
instance.InstanceDomainRemovedEventType,
).
EventData(map[string]interface{}{
"domain": domains.host,
}).
Builder()
}

func (domains *SearchInstanceDomain) reduceAdded(event *instance.DomainAddedEvent) (ok bool) {
for _, removed := range domains.removedInstances {
if removed == event.Domain {
return false
}
}
domains.InstanceID = event.Aggregate().ID
return true
}

func (domains *SearchInstanceDomain) reduceRemoved(event *instance.DomainRemovedEvent) {
domains.removedInstances = append(domains.removedInstances, event.Aggregate().ID)
}
101 changes: 67 additions & 34 deletions internal/query/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import (

"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/errors"
"github.com/zitadel/zitadel/internal/query/projection"
"github.com/zitadel/zitadel/internal/projection"
projection_old "github.com/zitadel/zitadel/internal/query/projection"
"github.com/zitadel/zitadel/internal/telemetry/tracing"
)

Expand All @@ -22,47 +23,47 @@ const (

var (
instanceTable = table{
name: projection.InstanceProjectionTable,
instanceIDCol: projection.InstanceColumnID,
name: projection_old.InstanceProjectionTable,
instanceIDCol: projection_old.InstanceColumnID,
}
InstanceColumnID = Column{
name: projection.InstanceColumnID,
name: projection_old.InstanceColumnID,
table: instanceTable,
}
InstanceColumnName = Column{
name: projection.InstanceColumnName,
name: projection_old.InstanceColumnName,
table: instanceTable,
}
InstanceColumnCreationDate = Column{
name: projection.InstanceColumnCreationDate,
name: projection_old.InstanceColumnCreationDate,
table: instanceTable,
}
InstanceColumnChangeDate = Column{
name: projection.InstanceColumnChangeDate,
name: projection_old.InstanceColumnChangeDate,
table: instanceTable,
}
InstanceColumnSequence = Column{
name: projection.InstanceColumnSequence,
name: projection_old.InstanceColumnSequence,
table: instanceTable,
}
InstanceColumnDefaultOrgID = Column{
name: projection.InstanceColumnDefaultOrgID,
name: projection_old.InstanceColumnDefaultOrgID,
table: instanceTable,
}
InstanceColumnProjectID = Column{
name: projection.InstanceColumnProjectID,
name: projection_old.InstanceColumnProjectID,
table: instanceTable,
}
InstanceColumnConsoleID = Column{
name: projection.InstanceColumnConsoleID,
name: projection_old.InstanceColumnConsoleID,
table: instanceTable,
}
InstanceColumnConsoleAppID = Column{
name: projection.InstanceColumnConsoleAppID,
name: projection_old.InstanceColumnConsoleAppID,
table: instanceTable,
}
InstanceColumnDefaultLanguage = Column{
name: projection.InstanceColumnDefaultLanguage,
name: projection_old.InstanceColumnDefaultLanguage,
table: instanceTable,
}
)
Expand Down Expand Up @@ -166,43 +167,75 @@ func (q *Queries) Instance(ctx context.Context, shouldTriggerBulk bool) (_ *Inst
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()

if shouldTriggerBulk {
projection.InstanceProjection.Trigger(ctx)
}

stmt, scan := prepareInstanceDomainQuery(authz.GetInstance(ctx).RequestedDomain())
query, args, err := stmt.Where(sq.Eq{
InstanceColumnID.identifier(): authz.GetInstance(ctx).InstanceID(),
}).ToSql()
if err != nil {
return nil, errors.ThrowInternal(err, "QUERY-d9ngs", "Errors.Query.SQLStatement")
}

row, err := q.client.QueryContext(ctx, query, args...)
instance := projection.NewInstance(authz.GetInstance(ctx).InstanceID())
events, err := q.eventstore.Filter(ctx, instance.SearchQuery(ctx))
if err != nil {
return nil, err
}
return scan(row)
instance.Reduce(events)

return mapInstance(instance), nil
}

func (q *Queries) InstanceByHost(ctx context.Context, host string) (_ authz.Instance, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()

stmt, scan := prepareInstanceDomainQuery(host)
host = strings.Split(host, ":")[0] //remove possible port
query, args, err := stmt.Where(sq.Eq{
InstanceDomainDomainCol.identifier(): host,
}).ToSql()

domainSearch := projection.NewSearchInstanceDomain(host)
events, err := q.eventstore.Filter(ctx, domainSearch.SearchQuery(ctx))
if err != nil {
return nil, errors.ThrowInternal(err, "QUERY-SAfg2", "Errors.Query.SQLStatement")
return nil, err
}
domainSearch.Reduce(events)
if domainSearch.InstanceID == "" {
return nil, errors.ThrowNotFound(nil, "QUERY-VZHH2", "Errors.NotFound")
}

row, err := q.client.QueryContext(ctx, query, args...)
instance := projection.NewInstance(domainSearch.InstanceID)
events, err = q.eventstore.Filter(ctx, instance.SearchQuery(ctx))
if err != nil {
return nil, err
}
return scan(row)
instance.Reduce(events)

return mapInstance(instance), nil
}

func mapInstance(instance *projection.Instance) *Instance {
return &Instance{
ID: instance.ID,
ChangeDate: instance.ChangeDate,
CreationDate: instance.CreationDate,
Sequence: instance.Sequence,
Name: instance.Name,

DefaultOrgID: instance.DefaultOrgID,
IAMProjectID: instance.IAMProjectID,
ConsoleID: instance.ConsoleID,
ConsoleAppID: instance.ConsoleAppID,
DefaultLang: instance.DefaultLang,
Domains: mapInstanceDomains(instance.Domains),
}
}

func mapInstanceDomains(domains []*projection.InstanceDomain) []*InstanceDomain {
instanceDomains := make([]*InstanceDomain, len(domains))

for i, domain := range domains {
instanceDomains[i] = &InstanceDomain{
CreationDate: domain.CreationDate,
ChangeDate: domain.ChangeDate,
Sequence: domain.Sequence,
Domain: domain.Domain,
InstanceID: domain.InstanceID,
IsGenerated: domain.IsGenerated,
IsPrimary: domain.IsPrimary,
}
}

return instanceDomains
}

func (q *Queries) GetDefaultLanguage(ctx context.Context) language.Tag {
Expand Down

0 comments on commit e5cc00f

Please sign in to comment.