diff --git a/internal/v2/avatar/added.go b/internal/v2/avatar/added.go new file mode 100644 index 0000000000..5a083a10e3 --- /dev/null +++ b/internal/v2/avatar/added.go @@ -0,0 +1,7 @@ +package avatar + +const AvatarAddedTypeSuffix = ".avatar.added" + +type AddedPayload struct { + StoreKey string `json:"storeKey"` +} diff --git a/internal/v2/avatar/removed.go b/internal/v2/avatar/removed.go new file mode 100644 index 0000000000..44cd5b63db --- /dev/null +++ b/internal/v2/avatar/removed.go @@ -0,0 +1,7 @@ +package avatar + +const AvatarRemovedTypeSuffix = ".avatar.removed" + +type RemovedPayload struct { + StoreKey string `json:"storeKey"` +} diff --git a/internal/v2/database/mock/sql_mock.go b/internal/v2/database/mock/sql_mock.go index c693671d6f..6fe282d1c4 100644 --- a/internal/v2/database/mock/sql_mock.go +++ b/internal/v2/database/mock/sql_mock.go @@ -137,3 +137,11 @@ type AnyType[T interface{}] struct{} func (a AnyType[T]) Match(v driver.Value) bool { return reflect.TypeOf(new(T)).Elem().Kind().String() == reflect.TypeOf(v).Kind().String() } + +var NilArg nilArgument + +type nilArgument struct{} + +func (a nilArgument) Match(v driver.Value) bool { + return reflect.ValueOf(v).IsNil() +} diff --git a/internal/v2/domain/added.go b/internal/v2/domain/added.go new file mode 100644 index 0000000000..4d6f873869 --- /dev/null +++ b/internal/v2/domain/added.go @@ -0,0 +1,7 @@ +package domain + +const AddedTypeSuffix = "domain.added" + +type AddedPayload struct { + Name string `json:"domain"` +} diff --git a/internal/v2/domain/primary_set.go b/internal/v2/domain/primary_set.go new file mode 100644 index 0000000000..4155656b35 --- /dev/null +++ b/internal/v2/domain/primary_set.go @@ -0,0 +1,7 @@ +package domain + +const PrimarySetTypeSuffix = "domain.primary.set" + +type PrimarySetPayload struct { + Name string `json:"domain"` +} diff --git a/internal/v2/domain/removed.go b/internal/v2/domain/removed.go new file mode 100644 index 0000000000..8041abb6df --- /dev/null +++ b/internal/v2/domain/removed.go @@ -0,0 +1,7 @@ +package domain + +const RemovedTypeSuffix = "domain.removed" + +type RemovedPayload struct { + Name string `json:"domain"` +} diff --git a/internal/v2/domain/verfied.go b/internal/v2/domain/verfied.go new file mode 100644 index 0000000000..835274bf9a --- /dev/null +++ b/internal/v2/domain/verfied.go @@ -0,0 +1,7 @@ +package domain + +const VerifiedTypeSuffix = "domain.verified" + +type VerifiedPayload struct { + Name string `json:"domain"` +} diff --git a/internal/v2/eventstore/event.go b/internal/v2/eventstore/event.go index b452093305..2117177587 100644 --- a/internal/v2/eventstore/event.go +++ b/internal/v2/eventstore/event.go @@ -1,36 +1,66 @@ package eventstore -import "time" +import ( + "time" +) + +type Unmarshal func(ptr any) error + +type Payload interface { + Unmarshal | any +} + +type Action[P Payload] struct { + Creator string + Type string + Revision uint16 + Payload P +} + +type Command struct { + Action[any] + UniqueConstraints []*UniqueConstraint +} + +type StorageEvent struct { + Action[Unmarshal] -type Event[P any] struct { Aggregate Aggregate CreatedAt time.Time - Creator string Position GlobalPosition - Revision uint16 Sequence uint32 - Type string - Payload P } -type StoragePayload interface { - Unmarshal(ptr any) error +type Event[P any] struct { + *StorageEvent + Payload P } -func EventFromStorage[E Event[P], P any](event *Event[StoragePayload]) (*E, error) { +func UnmarshalPayload[P any](unmarshal Unmarshal) (P, error) { var payload P - - if err := event.Payload.Unmarshal(&payload); err != nil { - return nil, err - } - return &E{ - Aggregate: event.Aggregate, - CreatedAt: event.CreatedAt, - Creator: event.Creator, - Position: event.Position, - Revision: event.Revision, - Sequence: event.Sequence, - Type: event.Type, - Payload: payload, - }, nil + err := unmarshal(&payload) + return payload, err +} + +type EmptyPayload struct{} + +type TypeChecker interface { + ActionType() string +} + +func Type[T TypeChecker]() string { + var t T + return t.ActionType() +} + +func IsType[T TypeChecker](types ...string) bool { + gotten := Type[T]() + + for _, typ := range types { + if gotten == typ { + return true + } + } + + return false } diff --git a/internal/v2/eventstore/event_store.go b/internal/v2/eventstore/event_store.go index fe70bb36a3..cc447c5e15 100644 --- a/internal/v2/eventstore/event_store.go +++ b/internal/v2/eventstore/event_store.go @@ -34,8 +34,12 @@ type GlobalPosition struct { InPositionOrder uint32 } -type Reducer interface { - Reduce(events ...*Event[StoragePayload]) error +func (gp GlobalPosition) IsLess(other GlobalPosition) bool { + return gp.Position < other.Position || (gp.Position == other.Position && gp.InPositionOrder < other.InPositionOrder) } -type Reduce func(events ...*Event[StoragePayload]) error +type Reducer interface { + Reduce(events ...*StorageEvent) error +} + +type Reduce func(events ...*StorageEvent) error diff --git a/internal/v2/eventstore/postgres/event.go b/internal/v2/eventstore/postgres/event.go index 9970dd14ea..18d86c5751 100644 --- a/internal/v2/eventstore/postgres/event.go +++ b/internal/v2/eventstore/postgres/event.go @@ -2,8 +2,8 @@ package postgres import ( "encoding/json" - - "github.com/zitadel/logging" + "reflect" + "time" "github.com/zitadel/zitadel/internal/v2/eventstore" "github.com/zitadel/zitadel/internal/zerrors" @@ -13,52 +13,52 @@ func intentToCommands(intent *intent) (commands []*command, err error) { commands = make([]*command, len(intent.Commands())) for i, cmd := range intent.Commands() { - var payload unmarshalPayload - if cmd.Payload() != nil { - payload, err = json.Marshal(cmd.Payload()) - if err != nil { - logging.WithError(err).Warn("marshal payload failed") - return nil, zerrors.ThrowInternal(err, "POSTG-MInPK", "Errors.Internal") - } + payload, err := marshalPayload(cmd.Payload) + if err != nil { + return nil, zerrors.ThrowInternal(err, "POSTG-MInPK", "Errors.Internal") } - commands[i] = &command{ - Event: &eventstore.Event[eventstore.StoragePayload]{ - Aggregate: *intent.Aggregate(), - Creator: cmd.Creator(), - Revision: cmd.Revision(), - Type: cmd.Type(), - // always add at least 1 to the currently stored sequence - Sequence: intent.sequence + uint32(i) + 1, - Payload: payload, - }, - intent: intent, - uniqueConstraints: cmd.UniqueConstraints(), + Command: cmd, + intent: intent, + sequence: intent.nextSequence(), + payload: payload, } } return commands, nil } +func marshalPayload(payload any) ([]byte, error) { + if reflect.ValueOf(payload).IsZero() { + return nil, nil + } + return json.Marshal(payload) +} + type command struct { - *eventstore.Event[eventstore.StoragePayload] + eventstore.Command - intent *intent - uniqueConstraints []*eventstore.UniqueConstraint + intent *intent + + payload []byte + position eventstore.GlobalPosition + createdAt time.Time + sequence uint32 } -var _ eventstore.StoragePayload = (unmarshalPayload)(nil) - -type unmarshalPayload []byte - -// Unmarshal implements eventstore.StoragePayload. -func (p unmarshalPayload) Unmarshal(ptr any) error { - if len(p) == 0 { - return nil +func (cmd *command) toEvent() *eventstore.StorageEvent { + return &eventstore.StorageEvent{ + Action: eventstore.Action[eventstore.Unmarshal]{ + Creator: cmd.Creator, + Type: cmd.Type, + Revision: cmd.Revision, + Payload: func(ptr any) error { + return json.Unmarshal(cmd.payload, ptr) + }, + }, + Aggregate: *cmd.intent.Aggregate(), + Sequence: cmd.intent.sequence, + Position: cmd.position, + CreatedAt: cmd.createdAt, } - if err := json.Unmarshal(p, ptr); err != nil { - return zerrors.ThrowInternal(err, "POSTG-u8qVo", "Errors.Internal") - } - - return nil } diff --git a/internal/v2/eventstore/postgres/intent.go b/internal/v2/eventstore/postgres/intent.go index 9ab259ada8..34b7860e35 100644 --- a/internal/v2/eventstore/postgres/intent.go +++ b/internal/v2/eventstore/postgres/intent.go @@ -12,6 +12,11 @@ type intent struct { sequence uint32 } +func (i *intent) nextSequence() uint32 { + i.sequence++ + return i.sequence +} + func makeIntents(pushIntent *eventstore.PushIntent) []*intent { res := make([]*intent, len(pushIntent.Aggregates())) diff --git a/internal/v2/eventstore/postgres/push.go b/internal/v2/eventstore/postgres/push.go index 7ae64fd41d..269d22cdef 100644 --- a/internal/v2/eventstore/postgres/push.go +++ b/internal/v2/eventstore/postgres/push.go @@ -140,19 +140,19 @@ func push(ctx context.Context, tx *sql.Tx, reducer eventstore.Reducer, commands stmt.WriteString(", ") } - cmd.Position.InPositionOrder = uint32(i) + cmd.position.InPositionOrder = uint32(i) stmt.WriteString(`(`) stmt.WriteArgs( - cmd.Aggregate.Instance, - cmd.Aggregate.Owner, - cmd.Aggregate.Type, - cmd.Aggregate.ID, + cmd.intent.Aggregate().Instance, + cmd.intent.Aggregate().Owner, + cmd.intent.Aggregate().Type, + cmd.intent.Aggregate().ID, cmd.Revision, cmd.Creator, cmd.Type, - cmd.Payload, - cmd.Sequence, - i, + cmd.payload, + cmd.sequence, + cmd.position.InPositionOrder, ) stmt.WriteString(", statement_timestamp(), EXTRACT(EPOCH FROM clock_timestamp())") stmt.WriteString(`)`) @@ -171,13 +171,13 @@ func push(ctx context.Context, tx *sql.Tx, reducer eventstore.Reducer, commands defer func() { i++ }() err := scan( - &commands[i].CreatedAt, - &commands[i].Position.Position, + &commands[i].createdAt, + &commands[i].position.Position, ) if err != nil { return err } - return reducer.Reduce(commands[i].Event) + return reducer.Reduce(commands[i].toEvent()) }) } @@ -188,12 +188,13 @@ func uniqueConstraints(ctx context.Context, tx *sql.Tx, commands []*command) (er var stmt database.Statement for _, cmd := range commands { - if len(cmd.uniqueConstraints) == 0 { + if len(cmd.UniqueConstraints) == 0 { continue } - for _, constraint := range cmd.uniqueConstraints { + for _, constraint := range cmd.UniqueConstraints { stmt.Reset() - instance := cmd.Aggregate.Instance + + instance := cmd.intent.PushAggregate.Aggregate().Instance if constraint.IsGlobal { instance = "" } diff --git a/internal/v2/eventstore/postgres/push_test.go b/internal/v2/eventstore/postgres/push_test.go index 819add334f..b81b3a1517 100644 --- a/internal/v2/eventstore/postgres/push_test.go +++ b/internal/v2/eventstore/postgres/push_test.go @@ -47,13 +47,16 @@ func Test_uniqueConstraints(t *testing.T) { args: args{ commands: []*command{ { - Event: &eventstore.Event[eventstore.StoragePayload]{ - Aggregate: eventstore.Aggregate{ - Instance: "instance", - }, + intent: &intent{ + PushAggregate: eventstore.NewPushIntent( + "instance", + eventstore.AppendAggregate("", "", ""), + ).Aggregates()[0], }, - uniqueConstraints: []*eventstore.UniqueConstraint{ - eventstore.NewAddEventUniqueConstraint("test", "id", "error"), + Command: eventstore.Command{ + UniqueConstraints: []*eventstore.UniqueConstraint{ + eventstore.NewAddEventUniqueConstraint("test", "id", "error"), + }, }, }, }, @@ -72,13 +75,16 @@ func Test_uniqueConstraints(t *testing.T) { args: args{ commands: []*command{ { - Event: &eventstore.Event[eventstore.StoragePayload]{ - Aggregate: eventstore.Aggregate{ - Instance: "instance", - }, + intent: &intent{ + PushAggregate: eventstore.NewPushIntent( + "instance", + eventstore.AppendAggregate("", "", ""), + ).Aggregates()[0], }, - uniqueConstraints: []*eventstore.UniqueConstraint{ - eventstore.NewAddGlobalUniqueConstraint("test", "id", "error"), + Command: eventstore.Command{ + UniqueConstraints: []*eventstore.UniqueConstraint{ + eventstore.NewAddGlobalUniqueConstraint("test", "id", "error"), + }, }, }, }, @@ -97,14 +103,17 @@ func Test_uniqueConstraints(t *testing.T) { args: args{ commands: []*command{ { - Event: &eventstore.Event[eventstore.StoragePayload]{ - Aggregate: eventstore.Aggregate{ - Instance: "instance", - }, + intent: &intent{ + PushAggregate: eventstore.NewPushIntent( + "instance", + eventstore.AppendAggregate("", "", ""), + ).Aggregates()[0], }, - uniqueConstraints: []*eventstore.UniqueConstraint{ - eventstore.NewAddEventUniqueConstraint("test", "id", "error"), - eventstore.NewAddEventUniqueConstraint("test", "id2", "error"), + Command: eventstore.Command{ + UniqueConstraints: []*eventstore.UniqueConstraint{ + eventstore.NewAddEventUniqueConstraint("test", "id", "error"), + eventstore.NewAddEventUniqueConstraint("test", "id2", "error"), + }, }, }, }, @@ -128,23 +137,29 @@ func Test_uniqueConstraints(t *testing.T) { args: args{ commands: []*command{ { - Event: &eventstore.Event[eventstore.StoragePayload]{ - Aggregate: eventstore.Aggregate{ - Instance: "instance", - }, + intent: &intent{ + PushAggregate: eventstore.NewPushIntent( + "instance", + eventstore.AppendAggregate("", "", ""), + ).Aggregates()[0], }, - uniqueConstraints: []*eventstore.UniqueConstraint{ - eventstore.NewAddEventUniqueConstraint("test", "id", "error"), + Command: eventstore.Command{ + UniqueConstraints: []*eventstore.UniqueConstraint{ + eventstore.NewAddEventUniqueConstraint("test", "id", "error"), + }, }, }, { - Event: &eventstore.Event[eventstore.StoragePayload]{ - Aggregate: eventstore.Aggregate{ - Instance: "instance", - }, + intent: &intent{ + PushAggregate: eventstore.NewPushIntent( + "instance", + eventstore.AppendAggregate("", "", ""), + ).Aggregates()[0], }, - uniqueConstraints: []*eventstore.UniqueConstraint{ - eventstore.NewAddEventUniqueConstraint("test", "id2", "error"), + Command: eventstore.Command{ + UniqueConstraints: []*eventstore.UniqueConstraint{ + eventstore.NewAddEventUniqueConstraint("test", "id2", "error"), + }, }, }, }, @@ -168,13 +183,16 @@ func Test_uniqueConstraints(t *testing.T) { args: args{ commands: []*command{ { - Event: &eventstore.Event[eventstore.StoragePayload]{ - Aggregate: eventstore.Aggregate{ - Instance: "instance", - }, + intent: &intent{ + PushAggregate: eventstore.NewPushIntent( + "instance", + eventstore.AppendAggregate("", "", ""), + ).Aggregates()[0], }, - uniqueConstraints: []*eventstore.UniqueConstraint{ - eventstore.NewRemoveInstanceUniqueConstraints(), + Command: eventstore.Command{ + UniqueConstraints: []*eventstore.UniqueConstraint{ + eventstore.NewRemoveInstanceUniqueConstraints(), + }, }, }, }, @@ -193,23 +211,29 @@ func Test_uniqueConstraints(t *testing.T) { args: args{ commands: []*command{ { - Event: &eventstore.Event[eventstore.StoragePayload]{ - Aggregate: eventstore.Aggregate{ - Instance: "instance", - }, + intent: &intent{ + PushAggregate: eventstore.NewPushIntent( + "instance", + eventstore.AppendAggregate("", "", ""), + ).Aggregates()[0], }, - uniqueConstraints: []*eventstore.UniqueConstraint{ - eventstore.NewRemoveInstanceUniqueConstraints(), + Command: eventstore.Command{ + UniqueConstraints: []*eventstore.UniqueConstraint{ + eventstore.NewRemoveInstanceUniqueConstraints(), + }, }, }, { - Event: &eventstore.Event[eventstore.StoragePayload]{ - Aggregate: eventstore.Aggregate{ - Instance: "instance", - }, + intent: &intent{ + PushAggregate: eventstore.NewPushIntent( + "instance", + eventstore.AppendAggregate("", "", ""), + ).Aggregates()[0], }, - uniqueConstraints: []*eventstore.UniqueConstraint{ - eventstore.NewRemoveInstanceUniqueConstraints(), + Command: eventstore.Command{ + UniqueConstraints: []*eventstore.UniqueConstraint{ + eventstore.NewRemoveInstanceUniqueConstraints(), + }, }, }, }, @@ -233,13 +257,16 @@ func Test_uniqueConstraints(t *testing.T) { args: args{ commands: []*command{ { - Event: &eventstore.Event[eventstore.StoragePayload]{ - Aggregate: eventstore.Aggregate{ - Instance: "instance", - }, + intent: &intent{ + PushAggregate: eventstore.NewPushIntent( + "instance", + eventstore.AppendAggregate("", "", ""), + ).Aggregates()[0], }, - uniqueConstraints: []*eventstore.UniqueConstraint{ - eventstore.NewRemoveUniqueConstraint("test", "id"), + Command: eventstore.Command{ + UniqueConstraints: []*eventstore.UniqueConstraint{ + eventstore.NewRemoveUniqueConstraint("test", "id"), + }, }, }, }, @@ -258,13 +285,16 @@ func Test_uniqueConstraints(t *testing.T) { args: args{ commands: []*command{ { - Event: &eventstore.Event[eventstore.StoragePayload]{ - Aggregate: eventstore.Aggregate{ - Instance: "instance", - }, + intent: &intent{ + PushAggregate: eventstore.NewPushIntent( + "instance", + eventstore.AppendAggregate("", "", ""), + ).Aggregates()[0], }, - uniqueConstraints: []*eventstore.UniqueConstraint{ - eventstore.NewRemoveGlobalUniqueConstraint("test", "id"), + Command: eventstore.Command{ + UniqueConstraints: []*eventstore.UniqueConstraint{ + eventstore.NewRemoveGlobalUniqueConstraint("test", "id"), + }, }, }, }, @@ -283,14 +313,17 @@ func Test_uniqueConstraints(t *testing.T) { args: args{ commands: []*command{ { - Event: &eventstore.Event[eventstore.StoragePayload]{ - Aggregate: eventstore.Aggregate{ - Instance: "instance", - }, + intent: &intent{ + PushAggregate: eventstore.NewPushIntent( + "instance", + eventstore.AppendAggregate("", "", ""), + ).Aggregates()[0], }, - uniqueConstraints: []*eventstore.UniqueConstraint{ - eventstore.NewRemoveUniqueConstraint("test", "id"), - eventstore.NewRemoveUniqueConstraint("test", "id2"), + Command: eventstore.Command{ + UniqueConstraints: []*eventstore.UniqueConstraint{ + eventstore.NewRemoveUniqueConstraint("test", "id"), + eventstore.NewRemoveUniqueConstraint("test", "id2"), + }, }, }, }, @@ -314,23 +347,29 @@ func Test_uniqueConstraints(t *testing.T) { args: args{ commands: []*command{ { - Event: &eventstore.Event[eventstore.StoragePayload]{ - Aggregate: eventstore.Aggregate{ - Instance: "instance", - }, + intent: &intent{ + PushAggregate: eventstore.NewPushIntent( + "instance", + eventstore.AppendAggregate("", "", ""), + ).Aggregates()[0], }, - uniqueConstraints: []*eventstore.UniqueConstraint{ - eventstore.NewRemoveUniqueConstraint("test", "id"), + Command: eventstore.Command{ + UniqueConstraints: []*eventstore.UniqueConstraint{ + eventstore.NewRemoveUniqueConstraint("test", "id"), + }, }, }, { - Event: &eventstore.Event[eventstore.StoragePayload]{ - Aggregate: eventstore.Aggregate{ - Instance: "instance", - }, + intent: &intent{ + PushAggregate: eventstore.NewPushIntent( + "instance", + eventstore.AppendAggregate("", "", ""), + ).Aggregates()[0], }, - uniqueConstraints: []*eventstore.UniqueConstraint{ - eventstore.NewRemoveUniqueConstraint("test", "id2"), + Command: eventstore.Command{ + UniqueConstraints: []*eventstore.UniqueConstraint{ + eventstore.NewRemoveUniqueConstraint("test", "id2"), + }, }, }, }, @@ -354,13 +393,16 @@ func Test_uniqueConstraints(t *testing.T) { args: args{ commands: []*command{ { - Event: &eventstore.Event[eventstore.StoragePayload]{ - Aggregate: eventstore.Aggregate{ - Instance: "instance", - }, + intent: &intent{ + PushAggregate: eventstore.NewPushIntent( + "instance", + eventstore.AppendAggregate("", "", ""), + ).Aggregates()[0], }, - uniqueConstraints: []*eventstore.UniqueConstraint{ - eventstore.NewAddEventUniqueConstraint("test", "id", ""), + Command: eventstore.Command{ + UniqueConstraints: []*eventstore.UniqueConstraint{ + eventstore.NewAddEventUniqueConstraint("test", "id", ""), + }, }, }, }, @@ -385,13 +427,16 @@ func Test_uniqueConstraints(t *testing.T) { args: args{ commands: []*command{ { - Event: &eventstore.Event[eventstore.StoragePayload]{ - Aggregate: eventstore.Aggregate{ - Instance: "instance", - }, + intent: &intent{ + PushAggregate: eventstore.NewPushIntent( + "instance", + eventstore.AppendAggregate("", "", ""), + ).Aggregates()[0], }, - uniqueConstraints: []*eventstore.UniqueConstraint{ - eventstore.NewAddEventUniqueConstraint("test", "id", "My.Error"), + Command: eventstore.Command{ + UniqueConstraints: []*eventstore.UniqueConstraint{ + eventstore.NewAddEventUniqueConstraint("test", "id", "My.Error"), + }, }, }, }, @@ -741,16 +786,14 @@ func Test_push(t *testing.T) { eventstore.AppendAggregate("owner", "testType", "testID"), ).Aggregates()[0], }, - Event: &eventstore.Event[eventstore.StoragePayload]{ - Aggregate: *eventstore.NewPushIntent( - "instance", - eventstore.AppendAggregate("owner", "testType", "testID"), - ).Aggregates()[0].Aggregate(), - Creator: "gigi", - Revision: 1, - Type: "test.type", - Sequence: 1, + Command: eventstore.Command{ + Action: eventstore.Action[any]{ + Creator: "gigi", + Revision: 1, + Type: "test.type", + }, }, + sequence: 1, }, }, expectations: []mock.Expectation{ @@ -764,9 +807,9 @@ func Test_push(t *testing.T) { uint16(1), "gigi", "test.type", - nil, + mock.NilArg, uint32(1), - 0, + uint32(0), ), mock.WithQueryResult( []string{"created_at", "position"}, @@ -798,16 +841,14 @@ func Test_push(t *testing.T) { eventstore.AppendAggregate("owner", "testType", "testID"), ).Aggregates()[0], }, - Event: &eventstore.Event[eventstore.StoragePayload]{ - Aggregate: *eventstore.NewPushIntent( - "instance", - eventstore.AppendAggregate("owner", "testType", "testID"), - ).Aggregates()[0].Aggregate(), - Creator: "gigi", - Revision: 1, - Type: "test.type", - Sequence: 1, + Command: eventstore.Command{ + Action: eventstore.Action[any]{ + Creator: "gigi", + Revision: 1, + Type: "test.type", + }, }, + sequence: 1, }, { intent: &intent{ @@ -816,16 +857,14 @@ func Test_push(t *testing.T) { eventstore.AppendAggregate("owner", "testType", "testID"), ).Aggregates()[0], }, - Event: &eventstore.Event[eventstore.StoragePayload]{ - Aggregate: *eventstore.NewPushIntent( - "instance", - eventstore.AppendAggregate("owner", "testType", "testID"), - ).Aggregates()[0].Aggregate(), - Creator: "gigi", - Revision: 1, - Type: "test.type2", - Sequence: 2, + Command: eventstore.Command{ + Action: eventstore.Action[any]{ + Creator: "gigi", + Revision: 1, + Type: "test.type2", + }, }, + sequence: 2, }, }, expectations: []mock.Expectation{ @@ -839,9 +878,9 @@ func Test_push(t *testing.T) { uint16(1), "gigi", "test.type", - nil, + mock.NilArg, uint32(1), - 0, + uint32(0), "instance", "owner", "testType", @@ -849,9 +888,9 @@ func Test_push(t *testing.T) { uint16(1), "gigi", "test.type2", - nil, + mock.NilArg, uint32(2), - 1, + uint32(1), ), mock.WithQueryResult( []string{"created_at", "position"}, @@ -887,36 +926,30 @@ func Test_push(t *testing.T) { eventstore.AppendAggregate("owner", "testType", "testID"), ).Aggregates()[0], }, - Event: &eventstore.Event[eventstore.StoragePayload]{ - Aggregate: *eventstore.NewPushIntent( - "instance", - eventstore.AppendAggregate("owner", "testType", "testID"), - ).Aggregates()[0].Aggregate(), - Creator: "gigi", - Revision: 1, - Type: "test.type", - Sequence: 1, + Command: eventstore.Command{ + Action: eventstore.Action[any]{ + Creator: "gigi", + Revision: 1, + Type: "test.type", + }, }, + sequence: 1, }, { intent: &intent{ PushAggregate: eventstore.NewPushIntent( "instance", - eventstore.AppendAggregate("owner", "testType", "testID"), + eventstore.AppendAggregate("owner", "type2", "id2"), ).Aggregates()[0], }, - Event: &eventstore.Event[eventstore.StoragePayload]{ - Aggregate: eventstore.Aggregate{ - ID: "id2", - Type: "type2", - Instance: "instance", - Owner: "owner", + Command: eventstore.Command{ + Action: eventstore.Action[any]{ + Creator: "gigi", + Revision: 1, + Type: "test.type2", }, - Creator: "gigi", - Revision: 1, - Type: "test.type2", - Sequence: 10, }, + sequence: 10, }, }, expectations: []mock.Expectation{ @@ -930,9 +963,9 @@ func Test_push(t *testing.T) { uint16(1), "gigi", "test.type", - nil, + mock.NilArg, uint32(1), - 0, + uint32(0), "instance", "owner", "type2", @@ -940,9 +973,9 @@ func Test_push(t *testing.T) { uint16(1), "gigi", "test.type2", - nil, + mock.NilArg, uint32(10), - 1, + uint32(1), ), mock.WithQueryResult( []string{"created_at", "position"}, @@ -978,17 +1011,15 @@ func Test_push(t *testing.T) { eventstore.AppendAggregate("owner", "testType", "testID"), ).Aggregates()[0], }, - Event: &eventstore.Event[eventstore.StoragePayload]{ - Aggregate: *eventstore.NewPushIntent( - "instance", - eventstore.AppendAggregate("owner", "testType", "testID"), - ).Aggregates()[0].Aggregate(), - Creator: "gigi", - Revision: 1, - Type: "test.type", - Sequence: 1, - Payload: unmarshalPayload(`{"name": "gigi"}`), + Command: eventstore.Command{ + Action: eventstore.Action[any]{ + Creator: "gigi", + Revision: 1, + Type: "test.type", + }, }, + sequence: 1, + payload: []byte(`{"name": "gigi"}`), }, }, expectations: []mock.Expectation{ @@ -1002,9 +1033,9 @@ func Test_push(t *testing.T) { uint16(1), "gigi", "test.type", - unmarshalPayload(`{"name": "gigi"}`), + []byte(`{"name": "gigi"}`), uint32(1), - 0, + uint32(0), ), mock.WithQueryResult( []string{"created_at", "position"}, @@ -1036,16 +1067,14 @@ func Test_push(t *testing.T) { eventstore.AppendAggregate("owner", "testType", "testID"), ).Aggregates()[0], }, - Event: &eventstore.Event[eventstore.StoragePayload]{ - Aggregate: *eventstore.NewPushIntent( - "instance", - eventstore.AppendAggregate("owner", "testType", "testID"), - ).Aggregates()[0].Aggregate(), - Creator: "gigi", - Revision: 1, - Type: "test.type", - Sequence: 1, + Command: eventstore.Command{ + Action: eventstore.Action[any]{ + Creator: "gigi", + Revision: 1, + Type: "test.type", + }, }, + sequence: 1, }, }, expectations: []mock.Expectation{ @@ -1059,9 +1088,9 @@ func Test_push(t *testing.T) { uint16(1), "gigi", "test.type", - nil, + mock.NilArg, uint32(1), - 0, + uint32(0), ), mock.WithQueryResult( []string{"created_at", "position"}, @@ -1094,16 +1123,14 @@ func Test_push(t *testing.T) { eventstore.AppendAggregate("owner", "testType", "testID"), ).Aggregates()[0], }, - Event: &eventstore.Event[eventstore.StoragePayload]{ - Aggregate: *eventstore.NewPushIntent( - "instance", - eventstore.AppendAggregate("owner", "testType", "testID"), - ).Aggregates()[0].Aggregate(), - Creator: "gigi", - Revision: 1, - Type: "test.type", - Sequence: 1, + Command: eventstore.Command{ + Action: eventstore.Action[any]{ + Creator: "gigi", + Revision: 1, + Type: "test.type", + }, }, + sequence: 1, }, { intent: &intent{ @@ -1112,16 +1139,14 @@ func Test_push(t *testing.T) { eventstore.AppendAggregate("owner", "testType", "testID"), ).Aggregates()[0], }, - Event: &eventstore.Event[eventstore.StoragePayload]{ - Aggregate: *eventstore.NewPushIntent( - "instance", - eventstore.AppendAggregate("owner", "testType", "testID"), - ).Aggregates()[0].Aggregate(), - Creator: "gigi", - Revision: 1, - Type: "test.type2", - Sequence: 2, + Command: eventstore.Command{ + Action: eventstore.Action[any]{ + Creator: "gigi", + Revision: 1, + Type: "test.type2", + }, }, + sequence: 2, }, }, expectations: []mock.Expectation{ @@ -1135,9 +1160,9 @@ func Test_push(t *testing.T) { uint16(1), "gigi", "test.type", - nil, + mock.NilArg, uint32(1), - 0, + uint32(0), "instance", "owner", "testType", @@ -1145,9 +1170,9 @@ func Test_push(t *testing.T) { uint16(1), "gigi", "test.type2", - nil, + mock.NilArg, uint32(2), - 1, + uint32(1), ), mock.WithQueryResult( []string{"created_at", "position"}, @@ -1189,16 +1214,14 @@ func Test_push(t *testing.T) { eventstore.AppendAggregate("owner", "testType", "testID"), ).Aggregates()[0], }, - Event: &eventstore.Event[eventstore.StoragePayload]{ - Aggregate: *eventstore.NewPushIntent( - "instance", - eventstore.AppendAggregate("owner", "testType", "testID"), - ).Aggregates()[0].Aggregate(), - Creator: "gigi", - Revision: 1, - Type: "test.type", - Sequence: 1, + Command: eventstore.Command{ + Action: eventstore.Action[any]{ + Creator: "gigi", + Revision: 1, + Type: "test.type", + }, }, + sequence: 1, }, { intent: &intent{ @@ -1207,16 +1230,14 @@ func Test_push(t *testing.T) { eventstore.AppendAggregate("owner", "testType", "testID"), ).Aggregates()[0], }, - Event: &eventstore.Event[eventstore.StoragePayload]{ - Aggregate: *eventstore.NewPushIntent( - "instance", - eventstore.AppendAggregate("owner", "testType", "testID"), - ).Aggregates()[0].Aggregate(), - Creator: "gigi", - Revision: 1, - Type: "test.type2", - Sequence: 2, + Command: eventstore.Command{ + Action: eventstore.Action[any]{ + Creator: "gigi", + Revision: 1, + Type: "test.type2", + }, }, + sequence: 2, }, }, expectations: []mock.Expectation{ @@ -1230,9 +1251,9 @@ func Test_push(t *testing.T) { uint16(1), "gigi", "test.type", - nil, + mock.NilArg, uint32(1), - 0, + uint32(0), "instance", "owner", "testType", @@ -1240,9 +1261,9 @@ func Test_push(t *testing.T) { uint16(1), "gigi", "test.type2", - nil, + mock.NilArg, uint32(2), - 1, + uint32(1), ), mock.WithQueryResult( []string{"created_at", "position"}, diff --git a/internal/v2/eventstore/postgres/query.go b/internal/v2/eventstore/postgres/query.go index 608b31e533..ca7a081c75 100644 --- a/internal/v2/eventstore/postgres/query.go +++ b/internal/v2/eventstore/postgres/query.go @@ -3,6 +3,7 @@ package postgres import ( "context" "database/sql" + "encoding/json" "slices" "github.com/zitadel/logging" @@ -38,7 +39,7 @@ func executeQuery(ctx context.Context, tx database.Querier, stmt *database.State } err = database.MapRowsToObject(rows, func(scan func(dest ...any) error) error { - e := new(eventstore.Event[eventstore.StoragePayload]) + e := new(eventstore.StorageEvent) var payload sql.Null[[]byte] @@ -59,7 +60,12 @@ func executeQuery(ctx context.Context, tx database.Querier, stmt *database.State if err != nil { return err } - e.Payload = unmarshalPayload(payload.V) + e.Payload = func(ptr any) error { + if len(payload.V) == 0 { + return nil + } + return json.Unmarshal(payload.V, ptr) + } eventCount++ return reducer.Reduce(e) diff --git a/internal/v2/eventstore/postgres/query_test.go b/internal/v2/eventstore/postgres/query_test.go index c6e2c6f8a3..f73d7341f2 100644 --- a/internal/v2/eventstore/postgres/query_test.go +++ b/internal/v2/eventstore/postgres/query_test.go @@ -1143,7 +1143,7 @@ type testReducer struct { } // Reduce implements eventstore.Reducer. -func (r *testReducer) Reduce(events ...*eventstore.Event[eventstore.StoragePayload]) error { +func (r *testReducer) Reduce(events ...*eventstore.StorageEvent) error { if r == nil { return nil } diff --git a/internal/v2/eventstore/push.go b/internal/v2/eventstore/push.go index 6260315b82..b426078b8e 100644 --- a/internal/v2/eventstore/push.go +++ b/internal/v2/eventstore/push.go @@ -36,7 +36,7 @@ func (pi *PushIntent) Instance() string { return pi.instance } -func (pi *PushIntent) Reduce(events ...*Event[StoragePayload]) error { +func (pi *PushIntent) Reduce(events ...*StorageEvent) error { if pi.reducer == nil { return nil } @@ -170,21 +170,3 @@ func AppendCommands(commands ...Command) PushAggregateOpt { pa.commands = append(pa.commands, commands...) } } - -type Command interface { - // Creator is the id of the user which created the action - Creator() string - // Type describes the action it's in the past (e.g. user.created) - Type() string - // Revision of the action - Revision() uint16 - // Payload returns the payload of the event. It represent the changed fields by the event - // valid types are: - // * nil: no payload - // * struct: which can be marshalled to json - // * pointer to struct: which can be marshalled to json - // * []byte: json marshalled data - Payload() any - // UniqueConstraints should be added for unique attributes of an event, if nil constraints will not be checked - UniqueConstraints() []*UniqueConstraint -} diff --git a/internal/v2/eventstore/query.go b/internal/v2/eventstore/query.go index 0dd23ea898..eddb1aedde 100644 --- a/internal/v2/eventstore/query.go +++ b/internal/v2/eventstore/query.go @@ -41,7 +41,7 @@ func (q *Query) Pagination() *Pagination { return q.pagination } -func (q *Query) Reduce(events ...*Event[StoragePayload]) error { +func (q *Query) Reduce(events ...*StorageEvent) error { return q.reducer.Reduce(events...) } diff --git a/internal/v2/instance/aggregate.go b/internal/v2/instance/aggregate.go new file mode 100644 index 0000000000..aa4182b244 --- /dev/null +++ b/internal/v2/instance/aggregate.go @@ -0,0 +1,8 @@ +package instance + +import "github.com/zitadel/zitadel/internal/repository/instance" + +const ( + AggregateType = string(instance.AggregateType) + eventTypePrefix = AggregateType + "." +) diff --git a/internal/v2/instance/domain_policy.go b/internal/v2/instance/domain_policy.go new file mode 100644 index 0000000000..8746328ee1 --- /dev/null +++ b/internal/v2/instance/domain_policy.go @@ -0,0 +1,65 @@ +package instance + +import ( + "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/v2/policy" + "github.com/zitadel/zitadel/internal/zerrors" +) + +const DomainPolicyAddedType = eventTypePrefix + policy.DomainPolicyAddedTypeSuffix + +type DomainPolicyAddedPayload policy.DomainPolicyAddedPayload + +type DomainPolicyAddedEvent eventstore.Event[DomainPolicyAddedPayload] + +var _ eventstore.TypeChecker = (*DomainPolicyAddedEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *DomainPolicyAddedEvent) ActionType() string { + return DomainPolicyAddedType +} + +func DomainPolicyAddedEventFromStorage(event *eventstore.StorageEvent) (e *DomainPolicyAddedEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "INSTA-z1a7D", "Errors.Invalid.Event.Type") + } + + payload, err := eventstore.UnmarshalPayload[DomainPolicyAddedPayload](event.Payload) + if err != nil { + return nil, err + } + + return &DomainPolicyAddedEvent{ + StorageEvent: event, + Payload: payload, + }, nil +} + +const DomainPolicyChangedType = eventTypePrefix + policy.DomainPolicyChangedTypeSuffix + +type DomainPolicyChangedPayload policy.DomainPolicyChangedPayload + +type DomainPolicyChangedEvent eventstore.Event[DomainPolicyChangedPayload] + +var _ eventstore.TypeChecker = (*DomainPolicyChangedEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *DomainPolicyChangedEvent) ActionType() string { + return DomainPolicyChangedType +} + +func DomainPolicyChangedEventFromStorage(event *eventstore.StorageEvent) (e *DomainPolicyChangedEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "INSTA-BTLhd", "Errors.Invalid.Event.Type") + } + + payload, err := eventstore.UnmarshalPayload[DomainPolicyChangedPayload](event.Payload) + if err != nil { + return nil, err + } + + return &DomainPolicyChangedEvent{ + StorageEvent: event, + Payload: payload, + }, nil +} diff --git a/internal/v2/instance/removed.go b/internal/v2/instance/removed.go new file mode 100644 index 0000000000..fe2e814893 --- /dev/null +++ b/internal/v2/instance/removed.go @@ -0,0 +1,27 @@ +package instance + +import ( + "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/zerrors" +) + +const RemovedType = eventTypePrefix + "removed" + +type RemovedEvent eventstore.Event[eventstore.EmptyPayload] + +var _ eventstore.TypeChecker = (*RemovedEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *RemovedEvent) ActionType() string { + return RemovedType +} + +func RemovedEventFromStorage(event *eventstore.StorageEvent) (e *RemovedEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "INSTA-xppIg", "Errors.Invalid.Event.Type") + } + + return &RemovedEvent{ + StorageEvent: event, + }, nil +} diff --git a/internal/v2/org/added.go b/internal/v2/org/added.go new file mode 100644 index 0000000000..3d8cb62078 --- /dev/null +++ b/internal/v2/org/added.go @@ -0,0 +1,62 @@ +package org + +import ( + "context" + "strings" + + "github.com/zitadel/zitadel/internal/api/authz" + "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/zerrors" +) + +const AddedType = eventTypePrefix + "added" + +type addedPayload struct { + Name string `json:"name"` +} + +type AddedEvent eventstore.Event[addedPayload] + +var _ eventstore.TypeChecker = (*AddedEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *AddedEvent) ActionType() string { + return AddedType +} + +func AddedEventFromStorage(event *eventstore.StorageEvent) (e *AddedEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "ORG-Nf3tr", "Errors.Invalid.Event.Type") + } + + payload, err := eventstore.UnmarshalPayload[addedPayload](event.Payload) + if err != nil { + return nil, err + } + + return &AddedEvent{ + StorageEvent: event, + Payload: payload, + }, nil +} + +const uniqueOrgName = "org_name" + +func NewAddedCommand(ctx context.Context, name string) (*eventstore.Command, error) { + if name = strings.TrimSpace(name); name == "" { + return nil, zerrors.ThrowInvalidArgument(nil, "ORG-mruNY", "Errors.Invalid.Argument") + } + return &eventstore.Command{ + Action: eventstore.Action[any]{ + Creator: authz.GetCtxData(ctx).UserID, + Type: AddedType, + Revision: 1, + Payload: addedPayload{ + Name: name, + }, + }, + UniqueConstraints: []*eventstore.UniqueConstraint{ + eventstore.NewAddEventUniqueConstraint(uniqueOrgName, name, "Errors.Org.AlreadyExists"), + }, + }, nil +} diff --git a/internal/v2/org/aggregate.go b/internal/v2/org/aggregate.go new file mode 100644 index 0000000000..5ea881b755 --- /dev/null +++ b/internal/v2/org/aggregate.go @@ -0,0 +1,22 @@ +package org + +import ( + "context" + + "github.com/zitadel/zitadel/internal/api/authz" + "github.com/zitadel/zitadel/internal/v2/eventstore" +) + +const ( + AggregateType = "org" + eventTypePrefix = AggregateType + "." +) + +func NewAggregate(ctx context.Context, id string) *eventstore.Aggregate { + return &eventstore.Aggregate{ + ID: id, + Type: AggregateType, + Instance: authz.GetInstance(ctx).InstanceID(), + Owner: authz.GetCtxData(ctx).OrgID, + } +} diff --git a/internal/v2/org/changed.go b/internal/v2/org/changed.go new file mode 100644 index 0000000000..eb85fba9b0 --- /dev/null +++ b/internal/v2/org/changed.go @@ -0,0 +1,37 @@ +package org + +import ( + "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/zerrors" +) + +const ChangedType = eventTypePrefix + "changed" + +type changedPayload struct { + Name string `json:"name"` +} + +type ChangedEvent eventstore.Event[changedPayload] + +var _ eventstore.TypeChecker = (*ChangedEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *ChangedEvent) ActionType() string { + return ChangedType +} + +func ChangedEventFromStorage(event *eventstore.StorageEvent) (e *ChangedEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "ORG-pzOfP", "Errors.Invalid.Event.Type") + } + + payload, err := eventstore.UnmarshalPayload[changedPayload](event.Payload) + if err != nil { + return nil, err + } + + return &ChangedEvent{ + StorageEvent: event, + Payload: payload, + }, nil +} diff --git a/internal/v2/org/deactivated.go b/internal/v2/org/deactivated.go new file mode 100644 index 0000000000..454b8586fa --- /dev/null +++ b/internal/v2/org/deactivated.go @@ -0,0 +1,27 @@ +package org + +import ( + "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/zerrors" +) + +const DeactivatedType = eventTypePrefix + "deactivated" + +type DeactivatedEvent eventstore.Event[eventstore.EmptyPayload] + +var _ eventstore.TypeChecker = (*DeactivatedEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *DeactivatedEvent) ActionType() string { + return DeactivatedType +} + +func DeactivatedEventFromStorage(event *eventstore.StorageEvent) (e *DeactivatedEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "ORG-4zeWH", "Errors.Invalid.Event.Type") + } + + return &DeactivatedEvent{ + StorageEvent: event, + }, nil +} diff --git a/internal/v2/org/domain.go b/internal/v2/org/domain.go new file mode 100644 index 0000000000..b978997b49 --- /dev/null +++ b/internal/v2/org/domain.go @@ -0,0 +1,123 @@ +package org + +import ( + "github.com/zitadel/zitadel/internal/v2/domain" + "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/zerrors" +) + +const DomainAddedType = "org." + domain.AddedTypeSuffix + +type DomainAddedPayload domain.AddedPayload + +type DomainAddedEvent eventstore.Event[DomainAddedPayload] + +var _ eventstore.TypeChecker = (*DomainAddedEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *DomainAddedEvent) ActionType() string { + return DomainAddedType +} + +func DomainAddedEventFromStorage(event *eventstore.StorageEvent) (e *DomainAddedEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "ORG-CXVe3", "Errors.Invalid.Event.Type") + } + + payload, err := eventstore.UnmarshalPayload[DomainAddedPayload](event.Payload) + if err != nil { + return nil, err + } + + return &DomainAddedEvent{ + StorageEvent: event, + Payload: payload, + }, nil +} + +const DomainVerifiedType = "org." + domain.VerifiedTypeSuffix + +type DomainVerifiedPayload domain.VerifiedPayload + +type DomainVerifiedEvent eventstore.Event[DomainVerifiedPayload] + +var _ eventstore.TypeChecker = (*DomainVerifiedEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *DomainVerifiedEvent) ActionType() string { + return DomainVerifiedType +} + +func DomainVerifiedEventFromStorage(event *eventstore.StorageEvent) (e *DomainVerifiedEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "ORG-RAwdb", "Errors.Invalid.Event.Type") + } + + payload, err := eventstore.UnmarshalPayload[DomainVerifiedPayload](event.Payload) + if err != nil { + return nil, err + } + + return &DomainVerifiedEvent{ + StorageEvent: event, + Payload: payload, + }, nil +} + +const DomainPrimarySetType = "org." + domain.PrimarySetTypeSuffix + +type DomainPrimarySetPayload domain.PrimarySetPayload + +type DomainPrimarySetEvent eventstore.Event[DomainPrimarySetPayload] + +var _ eventstore.TypeChecker = (*DomainPrimarySetEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *DomainPrimarySetEvent) ActionType() string { + return DomainPrimarySetType +} + +func DomainPrimarySetEventFromStorage(event *eventstore.StorageEvent) (e *DomainPrimarySetEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "ORG-7P3Iz", "Errors.Invalid.Event.Type") + } + + payload, err := eventstore.UnmarshalPayload[DomainPrimarySetPayload](event.Payload) + if err != nil { + return nil, err + } + + return &DomainPrimarySetEvent{ + StorageEvent: event, + Payload: payload, + }, nil +} + +const DomainRemovedType = "org." + domain.RemovedTypeSuffix + +type DomainRemovedPayload domain.RemovedPayload + +type DomainRemovedEvent eventstore.Event[DomainRemovedPayload] + +var _ eventstore.TypeChecker = (*DomainRemovedEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *DomainRemovedEvent) ActionType() string { + return DomainRemovedType +} + +func DomainRemovedEventFromStorage(event *eventstore.StorageEvent) (e *DomainRemovedEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "ORG-ndpL2", "Errors.Invalid.Event.Type") + } + + payload, err := eventstore.UnmarshalPayload[DomainRemovedPayload](event.Payload) + if err != nil { + return nil, err + } + + return &DomainRemovedEvent{ + StorageEvent: event, + Payload: payload, + }, nil +} diff --git a/internal/v2/org/domain_policy.go b/internal/v2/org/domain_policy.go new file mode 100644 index 0000000000..ad37be0327 --- /dev/null +++ b/internal/v2/org/domain_policy.go @@ -0,0 +1,94 @@ +package org + +import ( + "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/v2/policy" + "github.com/zitadel/zitadel/internal/zerrors" +) + +const DomainPolicyAddedType = eventTypePrefix + policy.DomainPolicyAddedTypeSuffix + +type DomainPolicyAddedPayload policy.DomainPolicyAddedPayload + +type DomainPolicyAddedEvent eventstore.Event[DomainPolicyAddedPayload] + +var _ eventstore.TypeChecker = (*DomainPolicyAddedEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *DomainPolicyAddedEvent) ActionType() string { + return DomainPolicyAddedType +} + +func DomainPolicyAddedEventFromStorage(event *eventstore.StorageEvent) (e *DomainPolicyAddedEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "ORG-asiSN", "Errors.Invalid.Event.Type") + } + + payload, err := eventstore.UnmarshalPayload[DomainPolicyAddedPayload](event.Payload) + if err != nil { + return nil, err + } + + return &DomainPolicyAddedEvent{ + StorageEvent: event, + Payload: payload, + }, nil +} + +const DomainPolicyChangedType = eventTypePrefix + policy.DomainPolicyChangedTypeSuffix + +type DomainPolicyChangedPayload policy.DomainPolicyChangedPayload + +type DomainPolicyChangedEvent eventstore.Event[DomainPolicyChangedPayload] + +var _ eventstore.TypeChecker = (*DomainPolicyChangedEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *DomainPolicyChangedEvent) ActionType() string { + return DomainPolicyChangedType +} + +func DomainPolicyChangedEventFromStorage(event *eventstore.StorageEvent) (e *DomainPolicyChangedEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "ORG-BmN6K", "Errors.Invalid.Event.Type") + } + + payload, err := eventstore.UnmarshalPayload[DomainPolicyChangedPayload](event.Payload) + if err != nil { + return nil, err + } + + return &DomainPolicyChangedEvent{ + StorageEvent: event, + Payload: payload, + }, nil +} + +const DomainPolicyRemovedType = eventTypePrefix + policy.DomainPolicyRemovedTypeSuffix + +type DomainPolicyRemovedPayload policy.DomainPolicyRemovedPayload + +type DomainPolicyRemovedEvent eventstore.Event[DomainPolicyRemovedPayload] + +var _ eventstore.TypeChecker = (*DomainPolicyRemovedEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *DomainPolicyRemovedEvent) ActionType() string { + return DomainPolicyRemovedType +} + +func DomainPolicyRemovedEventFromStorage(event *eventstore.StorageEvent) (e *DomainPolicyRemovedEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "ORG-nHy4z", "Errors.Invalid.Event.Type") + } + + payload, err := eventstore.UnmarshalPayload[DomainPolicyRemovedPayload](event.Payload) + if err != nil { + return nil, err + } + + return &DomainPolicyRemovedEvent{ + StorageEvent: event, + Payload: payload, + }, nil +} diff --git a/internal/v2/org/reactivated.go b/internal/v2/org/reactivated.go new file mode 100644 index 0000000000..5b3da8fdd6 --- /dev/null +++ b/internal/v2/org/reactivated.go @@ -0,0 +1,27 @@ +package org + +import ( + "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/zerrors" +) + +const ReactivatedType = eventTypePrefix + "reactivated" + +type ReactivatedEvent eventstore.Event[eventstore.EmptyPayload] + +var _ eventstore.TypeChecker = (*ReactivatedEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *ReactivatedEvent) ActionType() string { + return ReactivatedType +} + +func ReactivatedEventFromStorage(event *eventstore.StorageEvent) (e *ReactivatedEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "ORG-cPWZw", "Errors.Invalid.Event.Type") + } + + return &ReactivatedEvent{ + StorageEvent: event, + }, nil +} diff --git a/internal/v2/org/removed.go b/internal/v2/org/removed.go new file mode 100644 index 0000000000..571c1c0113 --- /dev/null +++ b/internal/v2/org/removed.go @@ -0,0 +1,27 @@ +package org + +import ( + "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/zerrors" +) + +const RemovedType = eventTypePrefix + "removed" + +type RemovedEvent eventstore.Event[eventstore.EmptyPayload] + +var _ eventstore.TypeChecker = (*RemovedEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *RemovedEvent) ActionType() string { + return RemovedType +} + +func RemovedEventFromStorage(event *eventstore.StorageEvent) (e *RemovedEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "ORG-RSPYk", "Errors.Invalid.Event.Type") + } + + return &RemovedEvent{ + StorageEvent: event, + }, nil +} diff --git a/internal/v2/org/state.go b/internal/v2/org/state.go new file mode 100644 index 0000000000..428bdf82a5 --- /dev/null +++ b/internal/v2/org/state.go @@ -0,0 +1,36 @@ +package org + +type State uint8 + +const ( + UndefinedState State = iota + ActiveState + InactiveState + RemovedState + maxState +) + +func (s State) IsValid() bool { + return s != UndefinedState || + s < maxState +} + +func (s State) Is(state State) bool { + return s == state +} + +func (s State) IsValidState(state State) bool { + return s.IsValid() && s.Is(state) +} + +func (s State) IsValidStates(states ...State) bool { + if !s.IsValid() { + return false + } + for _, state := range states { + if s.Is(state) { + return true + } + } + return false +} diff --git a/internal/v2/policy/domain.go b/internal/v2/policy/domain.go new file mode 100644 index 0000000000..759caa254a --- /dev/null +++ b/internal/v2/policy/domain.go @@ -0,0 +1,23 @@ +package policy + +import "github.com/zitadel/zitadel/internal/v2/eventstore" + +const DomainPolicyAddedTypeSuffix = "policy.domain.added" + +type DomainPolicyAddedPayload struct { + UserLoginMustBeDomain bool `json:"userLoginMustBeDomain,omitempty"` + ValidateOrgDomains bool `json:"validateOrgDomains,omitempty"` + SMTPSenderAddressMatchesInstanceDomain bool `json:"smtpSenderAddressMatchesInstanceDomain,omitempty"` +} + +const DomainPolicyChangedTypeSuffix = "policy.domain.changed" + +type DomainPolicyChangedPayload struct { + UserLoginMustBeDomain *bool `json:"userLoginMustBeDomain,omitempty"` + ValidateOrgDomains *bool `json:"validateOrgDomains,omitempty"` + SMTPSenderAddressMatchesInstanceDomain *bool `json:"smtpSenderAddressMatchesInstanceDomain,omitempty"` +} + +const DomainPolicyRemovedTypeSuffix = "policy.domain.removed" + +type DomainPolicyRemovedPayload eventstore.EmptyPayload diff --git a/internal/v2/user/aggregate.go b/internal/v2/user/aggregate.go new file mode 100644 index 0000000000..291daa9af2 --- /dev/null +++ b/internal/v2/user/aggregate.go @@ -0,0 +1,23 @@ +package user + +import ( + "context" + + "github.com/zitadel/zitadel/internal/api/authz" + "github.com/zitadel/zitadel/internal/v2/eventstore" +) + +const ( + AggregateType = "user" + humanPrefix = AggregateType + ".human" + machinePrefix = AggregateType + ".machine" +) + +func NewAggregate(ctx context.Context, id string) *eventstore.Aggregate { + return &eventstore.Aggregate{ + ID: id, + Type: AggregateType, + Instance: authz.GetInstance(ctx).InstanceID(), + Owner: authz.GetCtxData(ctx).OrgID, + } +} diff --git a/internal/v2/user/domain_claimed.go b/internal/v2/user/domain_claimed.go new file mode 100644 index 0000000000..18960bb41f --- /dev/null +++ b/internal/v2/user/domain_claimed.go @@ -0,0 +1,38 @@ +package user + +import ( + "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/zerrors" +) + +type domainClaimedPayload struct { + Username string `json:"userName"` + TriggeredAtOrigin string `json:"triggerOrigin,omitempty"` +} + +type DomainClaimedEvent eventstore.Event[domainClaimedPayload] + +const DomainClaimedType = AggregateType + ".domain.claimed.sent" + +var _ eventstore.TypeChecker = (*DomainClaimedEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *DomainClaimedEvent) ActionType() string { + return DomainClaimedType +} + +func DomainClaimedEventFromStorage(event *eventstore.StorageEvent) (e *DomainClaimedEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "USER-x8O4o", "Errors.Invalid.Event.Type") + } + + payload, err := eventstore.UnmarshalPayload[domainClaimedPayload](event.Payload) + if err != nil { + return nil, err + } + + return &DomainClaimedEvent{ + StorageEvent: event, + Payload: payload, + }, nil +} diff --git a/internal/v2/user/human_added.go b/internal/v2/user/human_added.go new file mode 100644 index 0000000000..527e14f4db --- /dev/null +++ b/internal/v2/user/human_added.go @@ -0,0 +1,57 @@ +package user + +import ( + "golang.org/x/text/language" + + "github.com/zitadel/zitadel/internal/crypto" + "github.com/zitadel/zitadel/internal/domain" + "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/zerrors" +) + +const HumanAddedType = AggregateType + ".human.added" + +type humanAddedPayload struct { + Username string `json:"userName"` + + FirstName string `json:"firstName,omitempty"` + LastName string `json:"lastName,omitempty"` + NickName string `json:"nickName,omitempty"` + DisplayName string `json:"displayName,omitempty"` + PreferredLanguage language.Tag `json:"preferredLanguage,omitempty"` + Gender domain.Gender `json:"gender,omitempty"` + + EmailAddress domain.EmailAddress `json:"email,omitempty"` + PhoneNumber domain.PhoneNumber `json:"phone,omitempty"` + + // New events only use EncodedHash. However, the secret field + // is preserved to handle events older than the switch to Passwap. + Secret *crypto.CryptoValue `json:"secret,omitempty"` + EncodedHash string `json:"encodedHash,omitempty"` + PasswordChangeRequired bool `json:"changeRequired,omitempty"` +} + +type HumanAddedEvent eventstore.Event[humanAddedPayload] + +var _ eventstore.TypeChecker = (*HumanAddedEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *HumanAddedEvent) ActionType() string { + return HumanAddedType +} + +func HumanAddedEventFromStorage(event *eventstore.StorageEvent) (e *HumanAddedEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "USER-MRZ3p", "Errors.Invalid.Event.Type") + } + + payload, err := eventstore.UnmarshalPayload[humanAddedPayload](event.Payload) + if err != nil { + return nil, err + } + + return &HumanAddedEvent{ + StorageEvent: event, + Payload: payload, + }, nil +} diff --git a/internal/v2/user/human_avatar.go b/internal/v2/user/human_avatar.go new file mode 100644 index 0000000000..9b684ce02b --- /dev/null +++ b/internal/v2/user/human_avatar.go @@ -0,0 +1,61 @@ +package user + +import ( + "github.com/zitadel/zitadel/internal/v2/avatar" + "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/zerrors" +) + +type HumanAvatarAddedEvent eventstore.Event[avatar.AddedPayload] + +const HumanAvatarAddedType = humanPrefix + avatar.AvatarAddedTypeSuffix + +var _ eventstore.TypeChecker = (*HumanAvatarAddedEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *HumanAvatarAddedEvent) ActionType() string { + return HumanAvatarAddedType +} + +func HumanAvatarAddedEventFromStorage(event *eventstore.StorageEvent) (e *HumanAvatarAddedEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "USER-ddQaI", "Errors.Invalid.Event.Type") + } + + payload, err := eventstore.UnmarshalPayload[avatar.AddedPayload](event.Payload) + if err != nil { + return nil, err + } + + return &HumanAvatarAddedEvent{ + StorageEvent: event, + Payload: payload, + }, nil +} + +type HumanAvatarRemovedEvent eventstore.Event[avatar.RemovedPayload] + +const HumanAvatarRemovedType = humanPrefix + avatar.AvatarRemovedTypeSuffix + +var _ eventstore.TypeChecker = (*HumanAvatarRemovedEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *HumanAvatarRemovedEvent) ActionType() string { + return HumanAvatarRemovedType +} + +func HumanAvatarRemovedEventFromStorage(event *eventstore.StorageEvent) (e *HumanAvatarRemovedEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "USER-j2CkY", "Errors.Invalid.Event.Type") + } + + payload, err := eventstore.UnmarshalPayload[avatar.RemovedPayload](event.Payload) + if err != nil { + return nil, err + } + + return &HumanAvatarRemovedEvent{ + StorageEvent: event, + Payload: payload, + }, nil +} diff --git a/internal/v2/user/human_email_changed.go b/internal/v2/user/human_email_changed.go new file mode 100644 index 0000000000..88a7903afb --- /dev/null +++ b/internal/v2/user/human_email_changed.go @@ -0,0 +1,38 @@ +package user + +import ( + "github.com/zitadel/zitadel/internal/domain" + "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/zerrors" +) + +type humanEmailChangedPayload struct { + Address domain.EmailAddress `json:"email,omitempty"` +} + +type HumanEmailChangedEvent eventstore.Event[humanEmailChangedPayload] + +const HumanEmailChangedType = humanPrefix + ".email.changed" + +var _ eventstore.TypeChecker = (*HumanEmailChangedEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *HumanEmailChangedEvent) ActionType() string { + return HumanEmailChangedType +} + +func HumanEmailChangedEventFromStorage(event *eventstore.StorageEvent) (e *HumanEmailChangedEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "USER-Wr2lR", "Errors.Invalid.Event.Type") + } + + payload, err := eventstore.UnmarshalPayload[humanEmailChangedPayload](event.Payload) + if err != nil { + return nil, err + } + + return &HumanEmailChangedEvent{ + StorageEvent: event, + Payload: payload, + }, nil +} diff --git a/internal/v2/user/human_email_verified.go b/internal/v2/user/human_email_verified.go new file mode 100644 index 0000000000..65c4a9b391 --- /dev/null +++ b/internal/v2/user/human_email_verified.go @@ -0,0 +1,27 @@ +package user + +import ( + "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/zerrors" +) + +type HumanEmailVerifiedEvent eventstore.Event[eventstore.EmptyPayload] + +const HumanEmailVerifiedType = humanPrefix + ".email.verified" + +var _ eventstore.TypeChecker = (*HumanEmailVerifiedEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *HumanEmailVerifiedEvent) ActionType() string { + return HumanEmailVerifiedType +} + +func HumanEmailVerifiedEventFromStorage(event *eventstore.StorageEvent) (e *HumanEmailVerifiedEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "USER-X3esB", "Errors.Invalid.Event.Type") + } + + return &HumanEmailVerifiedEvent{ + StorageEvent: event, + }, nil +} diff --git a/internal/v2/user/human_init_code_added.go b/internal/v2/user/human_init_code_added.go new file mode 100644 index 0000000000..37938e000a --- /dev/null +++ b/internal/v2/user/human_init_code_added.go @@ -0,0 +1,43 @@ +package user + +import ( + "time" + + "github.com/zitadel/zitadel/internal/crypto" + "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/zerrors" +) + +type humanInitCodeAddedPayload struct { + Code *crypto.CryptoValue `json:"code,omitempty"` + Expiry time.Duration `json:"expiry,omitempty"` + TriggeredAtOrigin string `json:"triggerOrigin,omitempty"` + AuthRequestID string `json:"authRequestID,omitempty"` +} + +type HumanInitCodeAddedEvent eventstore.Event[humanInitCodeAddedPayload] + +const HumanInitCodeAddedType = humanPrefix + ".initialization.code.added" + +var _ eventstore.TypeChecker = (*HumanInitCodeAddedEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *HumanInitCodeAddedEvent) ActionType() string { + return HumanInitCodeAddedType +} + +func HumanInitCodeAddedEventFromStorage(event *eventstore.StorageEvent) (e *HumanInitCodeAddedEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "USER-XaGf6", "Errors.Invalid.Event.Type") + } + + payload, err := eventstore.UnmarshalPayload[humanInitCodeAddedPayload](event.Payload) + if err != nil { + return nil, err + } + + return &HumanInitCodeAddedEvent{ + StorageEvent: event, + Payload: payload, + }, nil +} diff --git a/internal/v2/user/human_init_code_succeeded.go b/internal/v2/user/human_init_code_succeeded.go new file mode 100644 index 0000000000..33e3d9a347 --- /dev/null +++ b/internal/v2/user/human_init_code_succeeded.go @@ -0,0 +1,27 @@ +package user + +import ( + "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/zerrors" +) + +type HumanInitCodeSucceededEvent eventstore.Event[eventstore.EmptyPayload] + +const HumanInitCodeSucceededType = humanPrefix + ".initialization.check.succeeded" + +var _ eventstore.TypeChecker = (*HumanInitCodeSucceededEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *HumanInitCodeSucceededEvent) ActionType() string { + return HumanInitCodeSucceededType +} + +func HumanInitCodeSucceededEventFromStorage(event *eventstore.StorageEvent) (e *HumanInitCodeSucceededEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "USER-12A5m", "Errors.Invalid.Event.Type") + } + + return &HumanInitCodeSucceededEvent{ + StorageEvent: event, + }, nil +} diff --git a/internal/v2/user/human_password_changed.go b/internal/v2/user/human_password_changed.go new file mode 100644 index 0000000000..fcdc589b31 --- /dev/null +++ b/internal/v2/user/human_password_changed.go @@ -0,0 +1,44 @@ +package user + +import ( + "github.com/zitadel/zitadel/internal/crypto" + "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/zerrors" +) + +type humanPasswordChangedPayload struct { + // New events only use EncodedHash. However, the secret field + // is preserved to handle events older than the switch to Passwap. + Secret *crypto.CryptoValue `json:"secret,omitempty"` + EncodedHash string `json:"encodedHash,omitempty"` + ChangeRequired bool `json:"changeRequired"` + UserAgentID string `json:"userAgentID,omitempty"` + TriggeredAtOrigin string `json:"triggerOrigin,omitempty"` +} + +type HumanPasswordChangedEvent eventstore.Event[humanPasswordChangedPayload] + +const HumanPasswordChangedType = humanPrefix + ".password.changed" + +var _ eventstore.TypeChecker = (*HumanPasswordChangedEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *HumanPasswordChangedEvent) ActionType() string { + return HumanPasswordChangedType +} + +func HumanPasswordChangedEventFromStorage(event *eventstore.StorageEvent) (e *HumanPasswordChangedEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "USER-Fx5tr", "Errors.Invalid.Event.Type") + } + + payload, err := eventstore.UnmarshalPayload[humanPasswordChangedPayload](event.Payload) + if err != nil { + return nil, err + } + + return &HumanPasswordChangedEvent{ + StorageEvent: event, + Payload: payload, + }, nil +} diff --git a/internal/v2/user/human_phone_changed.go b/internal/v2/user/human_phone_changed.go new file mode 100644 index 0000000000..964259e3bd --- /dev/null +++ b/internal/v2/user/human_phone_changed.go @@ -0,0 +1,38 @@ +package user + +import ( + "github.com/zitadel/zitadel/internal/domain" + "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/zerrors" +) + +type humanPhoneChangedPayload struct { + PhoneNumber domain.PhoneNumber `json:"phone,omitempty"` +} + +type HumanPhoneChangedEvent eventstore.Event[humanPhoneChangedPayload] + +const HumanPhoneChangedType = humanPrefix + ".phone.changed" + +var _ eventstore.TypeChecker = (*HumanPhoneChangedEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *HumanPhoneChangedEvent) ActionType() string { + return HumanPhoneChangedType +} + +func HumanPhoneChangedEventFromStorage(event *eventstore.StorageEvent) (e *HumanPhoneChangedEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "USER-d6hGS", "Errors.Invalid.Event.Type") + } + + payload, err := eventstore.UnmarshalPayload[humanPhoneChangedPayload](event.Payload) + if err != nil { + return nil, err + } + + return &HumanPhoneChangedEvent{ + StorageEvent: event, + Payload: payload, + }, nil +} diff --git a/internal/v2/user/human_phone_removed.go b/internal/v2/user/human_phone_removed.go new file mode 100644 index 0000000000..11d3dcbb0c --- /dev/null +++ b/internal/v2/user/human_phone_removed.go @@ -0,0 +1,27 @@ +package user + +import ( + "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/zerrors" +) + +type HumanPhoneRemovedEvent eventstore.Event[eventstore.EmptyPayload] + +const HumanPhoneRemovedType = humanPrefix + ".phone.removed" + +var _ eventstore.TypeChecker = (*HumanPhoneRemovedEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *HumanPhoneRemovedEvent) ActionType() string { + return HumanPhoneRemovedType +} + +func HumanPhoneRemovedEventFromStorage(event *eventstore.StorageEvent) (e *HumanPhoneRemovedEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "USER-vaD75", "Errors.Invalid.Event.Type") + } + + return &HumanPhoneRemovedEvent{ + StorageEvent: event, + }, nil +} diff --git a/internal/v2/user/human_phone_verified.go b/internal/v2/user/human_phone_verified.go new file mode 100644 index 0000000000..bfe7888425 --- /dev/null +++ b/internal/v2/user/human_phone_verified.go @@ -0,0 +1,27 @@ +package user + +import ( + "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/zerrors" +) + +type HumanPhoneVerifiedEvent eventstore.Event[eventstore.EmptyPayload] + +const HumanPhoneVerifiedType = humanPrefix + ".phone.removed" + +var _ eventstore.TypeChecker = (*HumanPhoneVerifiedEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *HumanPhoneVerifiedEvent) ActionType() string { + return HumanPhoneVerifiedType +} + +func HumanPhoneVerifiedEventFromStorage(event *eventstore.StorageEvent) (e *HumanPhoneVerifiedEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "USER-ycRBi", "Errors.Invalid.Event.Type") + } + + return &HumanPhoneVerifiedEvent{ + StorageEvent: event, + }, nil +} diff --git a/internal/v2/user/human_profile_changed.go b/internal/v2/user/human_profile_changed.go new file mode 100644 index 0000000000..8124d3b993 --- /dev/null +++ b/internal/v2/user/human_profile_changed.go @@ -0,0 +1,45 @@ +package user + +import ( + "golang.org/x/text/language" + + "github.com/zitadel/zitadel/internal/domain" + "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/zerrors" +) + +type humanProfileChangedPayload struct { + FirstName string `json:"firstName,omitempty"` + LastName string `json:"lastName,omitempty"` + NickName *string `json:"nickName,omitempty"` + DisplayName *string `json:"displayName,omitempty"` + PreferredLanguage *language.Tag `json:"preferredLanguage,omitempty"` + Gender *domain.Gender `json:"gender,omitempty"` +} + +type HumanProfileChangedEvent eventstore.Event[humanProfileChangedPayload] + +const HumanProfileChangedType = humanPrefix + ".profile.changed" + +var _ eventstore.TypeChecker = (*HumanProfileChangedEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *HumanProfileChangedEvent) ActionType() string { + return HumanProfileChangedType +} + +func HumanProfileChangedEventFromStorage(event *eventstore.StorageEvent) (e *HumanProfileChangedEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "USER-Z1aFH", "Errors.Invalid.Event.Type") + } + + payload, err := eventstore.UnmarshalPayload[humanProfileChangedPayload](event.Payload) + if err != nil { + return nil, err + } + + return &HumanProfileChangedEvent{ + StorageEvent: event, + Payload: payload, + }, nil +} diff --git a/internal/v2/user/human_registered.go b/internal/v2/user/human_registered.go new file mode 100644 index 0000000000..c1908da377 --- /dev/null +++ b/internal/v2/user/human_registered.go @@ -0,0 +1,55 @@ +package user + +import ( + "golang.org/x/text/language" + + "github.com/zitadel/zitadel/internal/crypto" + "github.com/zitadel/zitadel/internal/domain" + "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/zerrors" +) + +type humanRegisteredPayload struct { + Username string `json:"userName"` + FirstName string `json:"firstName,omitempty"` + LastName string `json:"lastName,omitempty"` + NickName string `json:"nickName,omitempty"` + DisplayName string `json:"displayName,omitempty"` + PreferredLanguage language.Tag `json:"preferredLanguage,omitempty"` + Gender domain.Gender `json:"gender,omitempty"` + EmailAddress domain.EmailAddress `json:"email,omitempty"` + PhoneNumber domain.PhoneNumber `json:"phone,omitempty"` + + // New events only use EncodedHash. However, the secret field + // is preserved to handle events older than the switch to Passwap. + Secret *crypto.CryptoValue `json:"secret,omitempty"` // legacy + EncodedHash string `json:"encodedHash,omitempty"` + PasswordChangeRequired bool `json:"changeRequired,omitempty"` +} + +type HumanRegisteredEvent eventstore.Event[humanRegisteredPayload] + +const HumanRegisteredType = humanPrefix + ".selfregistered" + +var _ eventstore.TypeChecker = (*HumanRegisteredEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *HumanRegisteredEvent) ActionType() string { + return HumanRegisteredType +} + +func HumanRegisteredEventFromStorage(event *eventstore.StorageEvent) (e *HumanRegisteredEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "USER-8HvGi", "Errors.Invalid.Event.Type") + } + + payload, err := eventstore.UnmarshalPayload[humanRegisteredPayload](event.Payload) + if err != nil { + return nil, err + } + + return &HumanRegisteredEvent{ + StorageEvent: event, + Payload: payload, + }, nil +} diff --git a/internal/v2/user/machine_added.go b/internal/v2/user/machine_added.go new file mode 100644 index 0000000000..57659db9a8 --- /dev/null +++ b/internal/v2/user/machine_added.go @@ -0,0 +1,41 @@ +package user + +import ( + "github.com/zitadel/zitadel/internal/domain" + "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/zerrors" +) + +type machineAddedPayload struct { + Username string `json:"userName"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + AccessTokenType domain.OIDCTokenType `json:"accessTokenType,omitempty"` +} + +type MachineAddedEvent eventstore.Event[machineAddedPayload] + +const MachineAddedType = machinePrefix + ".added" + +var _ eventstore.TypeChecker = (*MachineAddedEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *MachineAddedEvent) ActionType() string { + return MachineAddedType +} + +func MachineAddedEventFromStorage(event *eventstore.StorageEvent) (e *MachineAddedEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "USER-WLLoW", "Errors.Invalid.Event.Type") + } + + payload, err := eventstore.UnmarshalPayload[machineAddedPayload](event.Payload) + if err != nil { + return nil, err + } + + return &MachineAddedEvent{ + StorageEvent: event, + Payload: payload, + }, nil +} diff --git a/internal/v2/user/machine_changed.go b/internal/v2/user/machine_changed.go new file mode 100644 index 0000000000..bdd721b131 --- /dev/null +++ b/internal/v2/user/machine_changed.go @@ -0,0 +1,40 @@ +package user + +import ( + "github.com/zitadel/zitadel/internal/domain" + "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/zerrors" +) + +type machineChangedPayload struct { + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + AccessTokenType *domain.OIDCTokenType `json:"accessTokenType,omitempty"` +} + +type MachineChangedEvent eventstore.Event[machineChangedPayload] + +const MachineChangedType = machinePrefix + ".changed" + +var _ eventstore.TypeChecker = (*MachineChangedEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *MachineChangedEvent) ActionType() string { + return MachineChangedType +} + +func MachineChangedEventFromStorage(event *eventstore.StorageEvent) (e *MachineChangedEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "USER-JHwNs", "Errors.Invalid.Event.Type") + } + + payload, err := eventstore.UnmarshalPayload[machineChangedPayload](event.Payload) + if err != nil { + return nil, err + } + + return &MachineChangedEvent{ + StorageEvent: event, + Payload: payload, + }, nil +} diff --git a/internal/v2/user/machine_secret_hash_updated.go b/internal/v2/user/machine_secret_hash_updated.go new file mode 100644 index 0000000000..87d60a4305 --- /dev/null +++ b/internal/v2/user/machine_secret_hash_updated.go @@ -0,0 +1,37 @@ +package user + +import ( + "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/zerrors" +) + +type machineSecretHashUpdatedPayload struct { + HashedSecret string `json:"hashedSecret,omitempty"` +} + +type MachineSecretHashUpdatedEvent eventstore.Event[machineSecretHashUpdatedPayload] + +const MachineSecretHashUpdatedType = machinePrefix + ".secret.updated" + +var _ eventstore.TypeChecker = (*MachineSecretHashUpdatedEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *MachineSecretHashUpdatedEvent) ActionType() string { + return MachineSecretHashUpdatedType +} + +func MachineSecretHashUpdatedEventFromStorage(event *eventstore.StorageEvent) (e *MachineSecretHashUpdatedEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "USER-y41RK", "Errors.Invalid.Event.Type") + } + + payload, err := eventstore.UnmarshalPayload[machineSecretHashUpdatedPayload](event.Payload) + if err != nil { + return nil, err + } + + return &MachineSecretHashUpdatedEvent{ + StorageEvent: event, + Payload: payload, + }, nil +} diff --git a/internal/v2/user/machine_secret_removed.go b/internal/v2/user/machine_secret_removed.go new file mode 100644 index 0000000000..a05da5f796 --- /dev/null +++ b/internal/v2/user/machine_secret_removed.go @@ -0,0 +1,27 @@ +package user + +import ( + "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/zerrors" +) + +type MachineSecretRemovedEvent eventstore.Event[eventstore.EmptyPayload] + +const MachineSecretRemovedType = machinePrefix + ".secret.removed" + +var _ eventstore.TypeChecker = (*MachineSecretRemovedEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *MachineSecretRemovedEvent) ActionType() string { + return MachineSecretRemovedType +} + +func MachineSecretRemovedEventFromStorage(event *eventstore.StorageEvent) (e *MachineSecretRemovedEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "USER-SMtct", "Errors.Invalid.Event.Type") + } + + return &MachineSecretRemovedEvent{ + StorageEvent: event, + }, nil +} diff --git a/internal/v2/user/machine_secret_set.go b/internal/v2/user/machine_secret_set.go new file mode 100644 index 0000000000..a2a9ffa557 --- /dev/null +++ b/internal/v2/user/machine_secret_set.go @@ -0,0 +1,41 @@ +package user + +import ( + "github.com/zitadel/zitadel/internal/crypto" + "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/zerrors" +) + +type machineSecretSetPayload struct { + // New events only use EncodedHash. However, the ClientSecret field + // is preserved to handle events older than the switch to Passwap. + ClientSecret *crypto.CryptoValue `json:"clientSecret,omitempty"` + HashedSecret string `json:"hashedSecret,omitempty"` +} + +type MachineSecretHashSetEvent eventstore.Event[machineSecretSetPayload] + +const MachineSecretHashSetType = machinePrefix + ".secret.set" + +var _ eventstore.TypeChecker = (*MachineSecretHashSetEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *MachineSecretHashSetEvent) ActionType() string { + return MachineSecretHashSetType +} + +func MachineSecretHashSetEventFromStorage(event *eventstore.StorageEvent) (e *MachineSecretHashSetEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "USER-DzycT", "Errors.Invalid.Event.Type") + } + + payload, err := eventstore.UnmarshalPayload[machineSecretSetPayload](event.Payload) + if err != nil { + return nil, err + } + + return &MachineSecretHashSetEvent{ + StorageEvent: event, + Payload: payload, + }, nil +} diff --git a/internal/v2/user/token_added.go b/internal/v2/user/token_added.go new file mode 100644 index 0000000000..682dc219d3 --- /dev/null +++ b/internal/v2/user/token_added.go @@ -0,0 +1,51 @@ +package user + +import ( + "time" + + "github.com/zitadel/zitadel/internal/domain" + "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/zerrors" +) + +type tokenAddedPayload struct { + TokenID string `json:"tokenId,omitempty"` + ApplicationID string `json:"applicationId,omitempty"` + UserAgentID string `json:"userAgentId,omitempty"` + RefreshTokenID string `json:"refreshTokenID,omitempty"` + Audience []string `json:"audience,omitempty"` + Scopes []string `json:"scopes,omitempty"` + AuthMethodsReferences []string `json:"authMethodsReferences,omitempty"` + AuthTime time.Time `json:"authTime,omitempty"` + Expiration time.Time `json:"expiration,omitempty"` + PreferredLanguage string `json:"preferredLanguage,omitempty"` + Reason domain.TokenReason `json:"reason,omitempty"` + Actor *domain.TokenActor `json:"actor,omitempty"` +} + +type TokenAddedEvent eventstore.Event[tokenAddedPayload] + +const TokenAddedType = AggregateType + ".token.added" + +var _ eventstore.TypeChecker = (*TokenAddedEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *TokenAddedEvent) ActionType() string { + return TokenAddedType +} + +func TokenAddedEventFromStorage(event *eventstore.StorageEvent) (e *TokenAddedEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "USER-0YSt4", "Errors.Invalid.Event.Type") + } + + payload, err := eventstore.UnmarshalPayload[tokenAddedPayload](event.Payload) + if err != nil { + return nil, err + } + + return &TokenAddedEvent{ + StorageEvent: event, + Payload: payload, + }, nil +} diff --git a/internal/v2/user/user_deactivated.go b/internal/v2/user/user_deactivated.go new file mode 100644 index 0000000000..9d01a9e713 --- /dev/null +++ b/internal/v2/user/user_deactivated.go @@ -0,0 +1,27 @@ +package user + +import ( + "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/zerrors" +) + +type DeactivatedEvent eventstore.Event[eventstore.EmptyPayload] + +const DeactivatedType = AggregateType + ".deactivated" + +var _ eventstore.TypeChecker = (*DeactivatedEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *DeactivatedEvent) ActionType() string { + return DeactivatedType +} + +func DeactivatedEventFromStorage(event *eventstore.StorageEvent) (e *DeactivatedEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "USER-SBLu2", "Errors.Invalid.Event.Type") + } + + return &DeactivatedEvent{ + StorageEvent: event, + }, nil +} diff --git a/internal/v2/user/user_locked.go b/internal/v2/user/user_locked.go new file mode 100644 index 0000000000..a0702cdbc2 --- /dev/null +++ b/internal/v2/user/user_locked.go @@ -0,0 +1,27 @@ +package user + +import ( + "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/zerrors" +) + +type LockedEvent eventstore.Event[eventstore.EmptyPayload] + +const LockedType = AggregateType + ".locked" + +var _ eventstore.TypeChecker = (*LockedEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *LockedEvent) ActionType() string { + return LockedType +} + +func LockedEventFromStorage(event *eventstore.StorageEvent) (e *LockedEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "USER-48jjE", "Errors.Invalid.Event.Type") + } + + return &LockedEvent{ + StorageEvent: event, + }, nil +} diff --git a/internal/v2/user/user_reactivated.go b/internal/v2/user/user_reactivated.go new file mode 100644 index 0000000000..005392ce23 --- /dev/null +++ b/internal/v2/user/user_reactivated.go @@ -0,0 +1,27 @@ +package user + +import ( + "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/zerrors" +) + +type ReactivatedEvent eventstore.Event[eventstore.EmptyPayload] + +const ReactivatedType = AggregateType + ".reactivated" + +var _ eventstore.TypeChecker = (*ReactivatedEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *ReactivatedEvent) ActionType() string { + return ReactivatedType +} + +func ReactivatedEventFromStorage(event *eventstore.StorageEvent) (e *ReactivatedEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "USER-B3fcY", "Errors.Invalid.Event.Type") + } + + return &ReactivatedEvent{ + StorageEvent: event, + }, nil +} diff --git a/internal/v2/user/user_removed.go b/internal/v2/user/user_removed.go new file mode 100644 index 0000000000..b5ad827f05 --- /dev/null +++ b/internal/v2/user/user_removed.go @@ -0,0 +1,27 @@ +package user + +import ( + "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/zerrors" +) + +type RemovedEvent eventstore.Event[eventstore.EmptyPayload] + +const RemovedType = AggregateType + ".removed" + +var _ eventstore.TypeChecker = (*RemovedEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *RemovedEvent) ActionType() string { + return RemovedType +} + +func RemovedEventFromStorage(event *eventstore.StorageEvent) (e *RemovedEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "USER-UN6Xa", "Errors.Invalid.Event.Type") + } + + return &RemovedEvent{ + StorageEvent: event, + }, nil +} diff --git a/internal/v2/user/user_unlocked.go b/internal/v2/user/user_unlocked.go new file mode 100644 index 0000000000..97340619ce --- /dev/null +++ b/internal/v2/user/user_unlocked.go @@ -0,0 +1,27 @@ +package user + +import ( + "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/zerrors" +) + +type UnlockedEvent eventstore.Event[eventstore.EmptyPayload] + +const UnlockedType = AggregateType + ".unlocked" + +var _ eventstore.TypeChecker = (*UnlockedEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *UnlockedEvent) ActionType() string { + return UnlockedType +} + +func UnlockedEventFromStorage(event *eventstore.StorageEvent) (e *UnlockedEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "USER-HB0wi", "Errors.Invalid.Event.Type") + } + + return &UnlockedEvent{ + StorageEvent: event, + }, nil +} diff --git a/internal/v2/user/username_changed.go b/internal/v2/user/username_changed.go new file mode 100644 index 0000000000..5c0c8afdbc --- /dev/null +++ b/internal/v2/user/username_changed.go @@ -0,0 +1,37 @@ +package user + +import ( + "github.com/zitadel/zitadel/internal/v2/eventstore" + "github.com/zitadel/zitadel/internal/zerrors" +) + +type usernameChangedPayload struct { + Username string `json:"userName"` +} + +type UsernameChangedEvent eventstore.Event[usernameChangedPayload] + +const UsernameChangedType = AggregateType + ".username.changed" + +var _ eventstore.TypeChecker = (*UsernameChangedEvent)(nil) + +// ActionType implements eventstore.Typer. +func (c *UsernameChangedEvent) ActionType() string { + return UsernameChangedType +} + +func UsernameChangedEventFromStorage(event *eventstore.StorageEvent) (e *UsernameChangedEvent, _ error) { + if event.Type != e.ActionType() { + return nil, zerrors.ThrowInvalidArgument(nil, "USER-hCGsh", "Errors.Invalid.Event.Type") + } + + payload, err := eventstore.UnmarshalPayload[usernameChangedPayload](event.Payload) + if err != nil { + return nil, err + } + + return &UsernameChangedEvent{ + StorageEvent: event, + Payload: payload, + }, nil +}