mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 01:37:31 +00:00
add documentation
This commit is contained in:
@@ -7,15 +7,22 @@ import (
|
||||
"github.com/zitadel/zitadel/backend/v3/storage/database"
|
||||
)
|
||||
|
||||
// Commander is the all it needs to implement the command pattern.
|
||||
// It is the interface all manipulations need to implement.
|
||||
// If possible it should also be used for queries. We will find out if this is possible in the future.
|
||||
type Commander interface {
|
||||
Execute(ctx context.Context, opts *CommandOpts) (err error)
|
||||
fmt.Stringer
|
||||
}
|
||||
|
||||
// Invoker is part of the command pattern.
|
||||
// It is the interface that is used to execute commands.
|
||||
type Invoker interface {
|
||||
Invoke(ctx context.Context, command Commander, opts *CommandOpts) error
|
||||
}
|
||||
|
||||
// CommandOpts are passed to each command
|
||||
// the provide common fields used by commands like the database client.
|
||||
type CommandOpts struct {
|
||||
DB database.QueryExecutor
|
||||
Invoker Invoker
|
||||
@@ -95,6 +102,8 @@ func DefaultOpts(invoker Invoker) *CommandOpts {
|
||||
}
|
||||
}
|
||||
|
||||
// commandBatch is a batch of commands.
|
||||
// It uses the [Invoker] provided by the opts to execute each command.
|
||||
type commandBatch struct {
|
||||
Commands []Commander
|
||||
}
|
||||
|
@@ -6,6 +6,10 @@ import (
|
||||
"github.com/zitadel/zitadel/backend/v3/storage/eventstore"
|
||||
)
|
||||
|
||||
// CreateUserCommand adds a new user including the email verification for humans.
|
||||
// In the future it might make sense to separate the command into two commands:
|
||||
// - CreateHumanCommand: creates a new human user
|
||||
// - CreateMachineCommand: creates a new machine user
|
||||
type CreateUserCommand struct {
|
||||
user *User
|
||||
email *SetEmailCommand
|
||||
@@ -16,6 +20,7 @@ var (
|
||||
_ eventer = (*CreateUserCommand)(nil)
|
||||
)
|
||||
|
||||
// opts heavily reduces the complexity for email verification because each type of verification is a simple option which implements the [Commander] interface.
|
||||
func NewCreateHumanCommand(username string, opts ...CreateHumanOpt) *CreateUserCommand {
|
||||
cmd := &CreateUserCommand{
|
||||
user: &User{
|
||||
|
@@ -11,6 +11,10 @@ type generateCodeCommand struct {
|
||||
value *crypto.CryptoValue
|
||||
}
|
||||
|
||||
// I didn't update this repository to the solution proposed please view one of the following interfaces for correct usage:
|
||||
// - [UserRepository]
|
||||
// - [InstanceRepository]
|
||||
// - [OrgRepository]
|
||||
type CryptoRepository interface {
|
||||
GetEncryptionConfig(ctx context.Context) (*crypto.GeneratorConfig, error)
|
||||
}
|
||||
|
@@ -11,6 +11,8 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
)
|
||||
|
||||
// The variables could also be moved to a struct.
|
||||
// I just started with the singleton pattern and kept it like this.
|
||||
var (
|
||||
pool database.Pool
|
||||
userCodeAlgorithm crypto.EncryptionAlgorithm
|
||||
|
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/zitadel/zitadel/backend/v3/telemetry/tracing"
|
||||
)
|
||||
|
||||
// These tests give an overview of how to use the domain package.
|
||||
func TestExample(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
|
@@ -5,6 +5,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// EmailVerifiedCommand verifies an email address for a user.
|
||||
type EmailVerifiedCommand struct {
|
||||
UserID string `json:"userId"`
|
||||
Email *Email `json:"email"`
|
||||
@@ -42,6 +43,8 @@ func (cmd *EmailVerifiedCommand) applyOnSetEmail(setEmailCmd *SetEmailCommand) {
|
||||
setEmailCmd.verification = cmd
|
||||
}
|
||||
|
||||
// SendCodeCommand sends a verification code to the user's email address.
|
||||
// If the URLTemplate is not set it will use the default of the organization / instance.
|
||||
type SendCodeCommand struct {
|
||||
UserID string `json:"userId"`
|
||||
Email string `json:"email"`
|
||||
@@ -113,6 +116,8 @@ func (cmd *SendCodeCommand) applyOnSetEmail(setEmailCmd *SetEmailCommand) {
|
||||
setEmailCmd.verification = cmd
|
||||
}
|
||||
|
||||
// ReturnCodeCommand creates the code and returns it to the caller.
|
||||
// The caller gets the code by calling the Code field after the command got executed.
|
||||
type ReturnCodeCommand struct {
|
||||
UserID string `json:"userId"`
|
||||
Email string `json:"email"`
|
||||
|
@@ -33,6 +33,7 @@ func (i *Instance) Keys(index instanceCacheIndex) (key []string) {
|
||||
|
||||
var _ cache.Entry[instanceCacheIndex, string] = (*Instance)(nil)
|
||||
|
||||
// instanceColumns define all the columns of the instance table.
|
||||
type instanceColumns interface {
|
||||
// IDColumn returns the column for the id field.
|
||||
IDColumn() database.Column
|
||||
@@ -46,6 +47,7 @@ type instanceColumns interface {
|
||||
DeletedAtColumn() database.Column
|
||||
}
|
||||
|
||||
// instanceConditions define all the conditions for the instance table.
|
||||
type instanceConditions interface {
|
||||
// IDCondition returns an equal filter on the id field.
|
||||
IDCondition(instanceID string) database.Condition
|
||||
@@ -53,16 +55,19 @@ type instanceConditions interface {
|
||||
NameCondition(op database.TextOperation, name string) database.Condition
|
||||
}
|
||||
|
||||
// instanceChanges define all the changes for the instance table.
|
||||
type instanceChanges interface {
|
||||
// SetName sets the name column.
|
||||
SetName(name string) database.Change
|
||||
}
|
||||
|
||||
// InstanceRepository is the interface for the instance repository.
|
||||
type InstanceRepository interface {
|
||||
instanceColumns
|
||||
instanceConditions
|
||||
instanceChanges
|
||||
|
||||
// Member returns the member repository which is a sub repository of the instance repository.
|
||||
Member() MemberRepository
|
||||
|
||||
Get(ctx context.Context, opts ...database.QueryOption) (*Instance, error)
|
||||
|
@@ -7,6 +7,10 @@ import (
|
||||
"github.com/zitadel/zitadel/backend/v3/storage/eventstore"
|
||||
)
|
||||
|
||||
// Invoke provides a way to execute commands within the domain package.
|
||||
// It uses a chain of responsibility pattern to handle the command execution.
|
||||
// The default chain includes logging, tracing, and event publishing.
|
||||
// If you want to invoke multiple commands in a single transaction, you can use the [commandBatch].
|
||||
func Invoke(ctx context.Context, cmd Commander) error {
|
||||
invoker := newEventStoreInvoker(newLoggingInvoker(newTraceInvoker(nil)))
|
||||
opts := &CommandOpts{
|
||||
@@ -16,6 +20,8 @@ func Invoke(ctx context.Context, cmd Commander) error {
|
||||
return invoker.Invoke(ctx, cmd, opts)
|
||||
}
|
||||
|
||||
// eventStoreInvoker checks if the command implements the [eventer] interface.
|
||||
// If it does, it collects the events and publishes them to the event store.
|
||||
type eventStoreInvoker struct {
|
||||
collector *eventCollector
|
||||
}
|
||||
@@ -38,6 +44,7 @@ func (i *eventStoreInvoker) Invoke(ctx context.Context, command Commander, opts
|
||||
return nil
|
||||
}
|
||||
|
||||
// eventCollector collects events from all commands. The [eventStoreInvoker] pushes the collected events after all commands are executed.
|
||||
type eventCollector struct {
|
||||
next Invoker
|
||||
events []*eventstore.Event
|
||||
@@ -64,6 +71,7 @@ func (i *eventCollector) Invoke(ctx context.Context, command Commander, opts *Co
|
||||
return command.Execute(ctx, opts)
|
||||
}
|
||||
|
||||
// traceInvoker decorates each command with tracing.
|
||||
type traceInvoker struct {
|
||||
next Invoker
|
||||
}
|
||||
@@ -87,6 +95,8 @@ func (i *traceInvoker) Invoke(ctx context.Context, command Commander, opts *Comm
|
||||
return command.Execute(ctx, opts)
|
||||
}
|
||||
|
||||
// loggingInvoker decorates each command with logging.
|
||||
// It is an example implementation and logs the command name at the beginning and success or failure after the command got executed.
|
||||
type loggingInvoker struct {
|
||||
next Invoker
|
||||
}
|
||||
@@ -123,6 +133,10 @@ func (i *noopInvoker) Invoke(ctx context.Context, command Commander, opts *Comma
|
||||
return command.Execute(ctx, opts)
|
||||
}
|
||||
|
||||
// cacheInvoker could be used in the future to do the caching.
|
||||
// My goal would be to have two interfaces:
|
||||
// - cacheSetter: which caches an object
|
||||
// - cacheGetter: which gets an object from the cache, this should also skip the command execution
|
||||
type cacheInvoker struct {
|
||||
next Invoker
|
||||
}
|
||||
|
@@ -15,6 +15,7 @@ const (
|
||||
OrgStateInactive
|
||||
)
|
||||
|
||||
// Org is used by all other packages to represent an organization.
|
||||
type Org struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
@@ -42,6 +43,7 @@ func (o *Org) Keys(index orgCacheIndex) (key []string) {
|
||||
|
||||
var _ cache.Entry[orgCacheIndex, string] = (*Org)(nil)
|
||||
|
||||
// orgColumns define all the columns of the org table.
|
||||
type orgColumns interface {
|
||||
// InstanceIDColumn returns the column for the instance id field.
|
||||
InstanceIDColumn() database.Column
|
||||
@@ -59,6 +61,7 @@ type orgColumns interface {
|
||||
DeletedAtColumn() database.Column
|
||||
}
|
||||
|
||||
// orgConditions define all the conditions for the org table.
|
||||
type orgConditions interface {
|
||||
// InstanceIDCondition returns an equal filter on the instance id field.
|
||||
InstanceIDCondition(instanceID string) database.Condition
|
||||
@@ -70,6 +73,7 @@ type orgConditions interface {
|
||||
StateCondition(op database.NumberOperation, state OrgState) database.Condition
|
||||
}
|
||||
|
||||
// orgChanges define all the changes for the org table.
|
||||
type orgChanges interface {
|
||||
// SetName sets the name column.
|
||||
SetName(name string) database.Change
|
||||
@@ -77,12 +81,14 @@ type orgChanges interface {
|
||||
SetState(state OrgState) database.Change
|
||||
}
|
||||
|
||||
// OrgRepository is the interface for the org repository.
|
||||
// It is used to interact with the org table in the database.
|
||||
type OrgRepository interface {
|
||||
orgColumns
|
||||
orgConditions
|
||||
orgChanges
|
||||
|
||||
// Member returns the admin repository.
|
||||
// Member returns the member repository.
|
||||
Member() MemberRepository
|
||||
// Domain returns the domain repository.
|
||||
Domain() DomainRepository
|
||||
@@ -99,19 +105,14 @@ type OrgRepository interface {
|
||||
Update(ctx context.Context, condition database.Condition, changes ...database.Change) error
|
||||
}
|
||||
|
||||
type OrgOperation interface {
|
||||
MemberRepository
|
||||
DomainRepository
|
||||
Update(ctx context.Context, org *Org) error
|
||||
Delete(ctx context.Context) error
|
||||
}
|
||||
|
||||
// MemberRepository is a sub repository of the org repository and maybe the instance repository.
|
||||
type MemberRepository interface {
|
||||
AddMember(ctx context.Context, orgID, userID string, roles []string) error
|
||||
SetMemberRoles(ctx context.Context, orgID, userID string, roles []string) error
|
||||
RemoveMember(ctx context.Context, orgID, userID string) error
|
||||
}
|
||||
|
||||
// DomainRepository is a sub repository of the org repository and maybe the instance repository.
|
||||
type DomainRepository interface {
|
||||
AddDomain(ctx context.Context, domain string) error
|
||||
SetDomainVerified(ctx context.Context, domain string) error
|
||||
|
@@ -6,6 +6,8 @@ import (
|
||||
"github.com/zitadel/zitadel/backend/v3/storage/eventstore"
|
||||
)
|
||||
|
||||
// AddOrgCommand adds a new organization.
|
||||
// I'm unsure if we should add the Admins here or if this should be a separate command.
|
||||
type AddOrgCommand struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
@@ -86,6 +88,8 @@ func (cmd *AddOrgCommand) ensureID() (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
// AddMemberCommand adds a new member to an organization.
|
||||
// I'm not sure if we should make it more generic to also use it for instances.
|
||||
type AddMemberCommand struct {
|
||||
orgID string
|
||||
UserID string `json:"userId"`
|
||||
|
@@ -6,6 +6,10 @@ import (
|
||||
"github.com/zitadel/zitadel/backend/v3/storage/eventstore"
|
||||
)
|
||||
|
||||
// SetEmailCommand sets the email address of a user.
|
||||
// If allows verification as a sub command.
|
||||
// The verification command is executed after the email address is set.
|
||||
// The verification command is executed in the same transaction as the email address update.
|
||||
type SetEmailCommand struct {
|
||||
UserID string `json:"userId"`
|
||||
Email string `json:"email"`
|
||||
|
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/zitadel/zitadel/backend/v3/storage/database"
|
||||
)
|
||||
|
||||
// userColumns define all the columns of the user table.
|
||||
type userColumns interface {
|
||||
// InstanceIDColumn returns the column for the instance id field.
|
||||
InstanceIDColumn() database.Column
|
||||
@@ -24,6 +25,7 @@ type userColumns interface {
|
||||
DeletedAtColumn() database.Column
|
||||
}
|
||||
|
||||
// userConditions define all the conditions for the user table.
|
||||
type userConditions interface {
|
||||
// InstanceIDCondition returns an equal filter on the instance id field.
|
||||
InstanceIDCondition(instanceID string) database.Condition
|
||||
@@ -43,11 +45,13 @@ type userConditions interface {
|
||||
DeletedAtCondition(op database.NumberOperation, deletedAt time.Time) database.Condition
|
||||
}
|
||||
|
||||
// userChanges define all the changes for the user table.
|
||||
type userChanges interface {
|
||||
// SetUsername sets the username column.
|
||||
SetUsername(username string) database.Change
|
||||
}
|
||||
|
||||
// UserRepository is the interface for the user repository.
|
||||
type UserRepository interface {
|
||||
userColumns
|
||||
userConditions
|
||||
@@ -66,6 +70,7 @@ type UserRepository interface {
|
||||
Machine() MachineRepository
|
||||
}
|
||||
|
||||
// humanColumns define all the columns of the human table which inherits the user table.
|
||||
type humanColumns interface {
|
||||
userColumns
|
||||
// FirstNameColumn returns the column for the first name field.
|
||||
@@ -82,6 +87,7 @@ type humanColumns interface {
|
||||
PhoneVerifiedAtColumn() database.Column
|
||||
}
|
||||
|
||||
// humanConditions define all the conditions for the human table which inherits the user table.
|
||||
type humanConditions interface {
|
||||
userConditions
|
||||
// FirstNameCondition returns a filter on the first name field.
|
||||
@@ -103,6 +109,7 @@ type humanConditions interface {
|
||||
PhoneVerifiedAtCondition(op database.NumberOperation, phoneVerifiedAt time.Time) database.Condition
|
||||
}
|
||||
|
||||
// humanChanges define all the changes for the human table which inherits the user table.
|
||||
type humanChanges interface {
|
||||
userChanges
|
||||
// SetFirstName sets the first name field of the human.
|
||||
@@ -129,6 +136,7 @@ type humanChanges interface {
|
||||
SetPhoneVerifiedAt(at time.Time) database.Change
|
||||
}
|
||||
|
||||
// HumanRepository is the interface for the human repository it inherits the user repository.
|
||||
type HumanRepository interface {
|
||||
humanColumns
|
||||
humanConditions
|
||||
@@ -140,24 +148,28 @@ type HumanRepository interface {
|
||||
Update(ctx context.Context, condition database.Condition, changes ...database.Change) error
|
||||
}
|
||||
|
||||
// machineColumns define all the columns of the machine table which inherits the user table.
|
||||
type machineColumns interface {
|
||||
userColumns
|
||||
// DescriptionColumn returns the column for the description field.
|
||||
DescriptionColumn() database.Column
|
||||
}
|
||||
|
||||
// machineConditions define all the conditions for the machine table which inherits the user table.
|
||||
type machineConditions interface {
|
||||
userConditions
|
||||
// DescriptionCondition returns a filter on the description field.
|
||||
DescriptionCondition(op database.TextOperation, description string) database.Condition
|
||||
}
|
||||
|
||||
// machineChanges define all the changes for the machine table which inherits the user table.
|
||||
type machineChanges interface {
|
||||
userChanges
|
||||
// SetDescription sets the description field of the machine.
|
||||
SetDescription(description string) database.Change
|
||||
}
|
||||
|
||||
// MachineRepository is the interface for the machine repository it inherits the user repository.
|
||||
type MachineRepository interface {
|
||||
// Update updates machine users based on the given condition and changes.
|
||||
Update(ctx context.Context, condition database.Condition, changes ...database.Change) error
|
||||
@@ -167,6 +179,7 @@ type MachineRepository interface {
|
||||
machineChanges
|
||||
}
|
||||
|
||||
// UserTraits is implemented by [Human] and [Machine].
|
||||
type UserTraits interface {
|
||||
Type() UserType
|
||||
}
|
||||
|
Reference in New Issue
Block a user