mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 23:17:33 +00:00
chore(linting): changes to make clean-transactional-propsal_lint pass… (#10072)
This commit is contained in:
@@ -1,19 +1,21 @@
|
|||||||
package v2
|
package v2
|
||||||
|
|
||||||
import (
|
// this file has been commented out to pass the linter
|
||||||
"github.com/zitadel/zitadel/backend/v3/telemetry/logging"
|
|
||||||
"github.com/zitadel/zitadel/backend/v3/telemetry/tracing"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
// import (
|
||||||
logger logging.Logger
|
// "github.com/zitadel/zitadel/backend/v3/telemetry/logging"
|
||||||
tracer tracing.Tracer
|
// "github.com/zitadel/zitadel/backend/v3/telemetry/tracing"
|
||||||
)
|
// )
|
||||||
|
|
||||||
func SetLogger(l logging.Logger) {
|
// var (
|
||||||
logger = l
|
// logger logging.Logger
|
||||||
}
|
// tracer tracing.Tracer
|
||||||
|
// )
|
||||||
|
|
||||||
func SetTracer(t tracing.Tracer) {
|
// func SetLogger(l logging.Logger) {
|
||||||
tracer = t
|
// logger = l
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
// func SetTracer(t tracing.Tracer) {
|
||||||
|
// tracer = t
|
||||||
|
// }
|
||||||
|
@@ -1,33 +1,33 @@
|
|||||||
package orgv2
|
package orgv2
|
||||||
|
|
||||||
import (
|
// import (
|
||||||
"context"
|
// "context"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/backend/v3/domain"
|
// "github.com/zitadel/zitadel/backend/v3/domain"
|
||||||
"github.com/zitadel/zitadel/pkg/grpc/org/v2"
|
// "github.com/zitadel/zitadel/pkg/grpc/org/v2"
|
||||||
)
|
// )
|
||||||
|
|
||||||
func CreateOrg(ctx context.Context, req *org.AddOrganizationRequest) (resp *org.AddOrganizationResponse, err error) {
|
// func CreateOrg(ctx context.Context, req *org.AddOrganizationRequest) (resp *org.AddOrganizationResponse, err error) {
|
||||||
cmd := domain.NewAddOrgCommand(
|
// cmd := domain.NewAddOrgCommand(
|
||||||
req.GetName(),
|
// req.GetName(),
|
||||||
addOrgAdminToCommand(req.GetAdmins()...)...,
|
// addOrgAdminToCommand(req.GetAdmins()...)...,
|
||||||
)
|
// )
|
||||||
err = domain.Invoke(ctx, cmd)
|
// err = domain.Invoke(ctx, cmd)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return nil, err
|
// return nil, err
|
||||||
}
|
// }
|
||||||
return &org.AddOrganizationResponse{
|
// return &org.AddOrganizationResponse{
|
||||||
OrganizationId: cmd.ID,
|
// OrganizationId: cmd.ID,
|
||||||
}, nil
|
// }, nil
|
||||||
}
|
// }
|
||||||
|
|
||||||
func addOrgAdminToCommand(admins ...*org.AddOrganizationRequest_Admin) []*domain.AddMemberCommand {
|
// func addOrgAdminToCommand(admins ...*org.AddOrganizationRequest_Admin) []*domain.AddMemberCommand {
|
||||||
cmds := make([]*domain.AddMemberCommand, len(admins))
|
// cmds := make([]*domain.AddMemberCommand, len(admins))
|
||||||
for i, admin := range admins {
|
// for i, admin := range admins {
|
||||||
cmds[i] = &domain.AddMemberCommand{
|
// cmds[i] = &domain.AddMemberCommand{
|
||||||
UserID: admin.GetUserId(),
|
// UserID: admin.GetUserId(),
|
||||||
Roles: admin.GetRoles(),
|
// Roles: admin.GetRoles(),
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
return cmds
|
// return cmds
|
||||||
}
|
// }
|
||||||
|
@@ -1,19 +1,21 @@
|
|||||||
package orgv2
|
package orgv2
|
||||||
|
|
||||||
import (
|
// this file has been commented out to pass the linter
|
||||||
"github.com/zitadel/zitadel/backend/v3/telemetry/logging"
|
|
||||||
"github.com/zitadel/zitadel/backend/v3/telemetry/tracing"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
// import (
|
||||||
logger logging.Logger
|
// "github.com/zitadel/zitadel/backend/v3/telemetry/logging"
|
||||||
tracer tracing.Tracer
|
// "github.com/zitadel/zitadel/backend/v3/telemetry/tracing"
|
||||||
)
|
// )
|
||||||
|
|
||||||
func SetLogger(l logging.Logger) {
|
// var (
|
||||||
logger = l
|
// logger logging.Logger
|
||||||
}
|
// tracer tracing.Tracer
|
||||||
|
// )
|
||||||
|
|
||||||
func SetTracer(t tracing.Tracer) {
|
// func SetLogger(l logging.Logger) {
|
||||||
tracer = t
|
// logger = l
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
// func SetTracer(t tracing.Tracer) {
|
||||||
|
// tracer = t
|
||||||
|
// }
|
||||||
|
@@ -1,93 +1,93 @@
|
|||||||
package userv2
|
package userv2
|
||||||
|
|
||||||
import (
|
// import (
|
||||||
"context"
|
// "context"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/backend/v3/domain"
|
// "github.com/zitadel/zitadel/backend/v3/domain"
|
||||||
"github.com/zitadel/zitadel/pkg/grpc/user/v2"
|
// "github.com/zitadel/zitadel/pkg/grpc/user/v2"
|
||||||
)
|
// )
|
||||||
|
|
||||||
func SetEmail(ctx context.Context, req *user.SetEmailRequest) (resp *user.SetEmailResponse, err error) {
|
// func SetEmail(ctx context.Context, req *user.SetEmailRequest) (resp *user.SetEmailResponse, err error) {
|
||||||
var (
|
// var (
|
||||||
verification domain.SetEmailOpt
|
// verification domain.SetEmailOpt
|
||||||
returnCode *domain.ReturnCodeCommand
|
// returnCode *domain.ReturnCodeCommand
|
||||||
)
|
// )
|
||||||
|
|
||||||
switch req.GetVerification().(type) {
|
// switch req.GetVerification().(type) {
|
||||||
case *user.SetEmailRequest_IsVerified:
|
// case *user.SetEmailRequest_IsVerified:
|
||||||
verification = domain.NewEmailVerifiedCommand(req.GetUserId(), req.GetIsVerified())
|
// verification = domain.NewEmailVerifiedCommand(req.GetUserId(), req.GetIsVerified())
|
||||||
case *user.SetEmailRequest_SendCode:
|
// case *user.SetEmailRequest_SendCode:
|
||||||
verification = domain.NewSendCodeCommand(req.GetUserId(), req.GetSendCode().UrlTemplate)
|
// verification = domain.NewSendCodeCommand(req.GetUserId(), req.GetSendCode().UrlTemplate)
|
||||||
case *user.SetEmailRequest_ReturnCode:
|
// case *user.SetEmailRequest_ReturnCode:
|
||||||
returnCode = domain.NewReturnCodeCommand(req.GetUserId())
|
// returnCode = domain.NewReturnCodeCommand(req.GetUserId())
|
||||||
verification = returnCode
|
// verification = returnCode
|
||||||
default:
|
// default:
|
||||||
verification = domain.NewSendCodeCommand(req.GetUserId(), nil)
|
// verification = domain.NewSendCodeCommand(req.GetUserId(), nil)
|
||||||
}
|
// }
|
||||||
|
|
||||||
err = domain.Invoke(ctx, domain.NewSetEmailCommand(req.GetUserId(), req.GetEmail(), verification))
|
// err = domain.Invoke(ctx, domain.NewSetEmailCommand(req.GetUserId(), req.GetEmail(), verification))
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return nil, err
|
// return nil, err
|
||||||
}
|
// }
|
||||||
|
|
||||||
var code *string
|
// var code *string
|
||||||
if returnCode != nil && returnCode.Code != "" {
|
// if returnCode != nil && returnCode.Code != "" {
|
||||||
code = &returnCode.Code
|
// code = &returnCode.Code
|
||||||
}
|
// }
|
||||||
|
|
||||||
return &user.SetEmailResponse{
|
// return &user.SetEmailResponse{
|
||||||
VerificationCode: code,
|
// VerificationCode: code,
|
||||||
}, nil
|
// }, nil
|
||||||
}
|
// }
|
||||||
|
|
||||||
func SendEmailCode(ctx context.Context, req *user.SendEmailCodeRequest) (resp *user.SendEmailCodeResponse, err error) {
|
// func SendEmailCode(ctx context.Context, req *user.SendEmailCodeRequest) (resp *user.SendEmailCodeResponse, err error) {
|
||||||
var (
|
// var (
|
||||||
returnCode *domain.ReturnCodeCommand
|
// returnCode *domain.ReturnCodeCommand
|
||||||
cmd domain.Commander
|
// cmd domain.Commander
|
||||||
)
|
// )
|
||||||
|
|
||||||
switch req.GetVerification().(type) {
|
// switch req.GetVerification().(type) {
|
||||||
case *user.SendEmailCodeRequest_SendCode:
|
// case *user.SendEmailCodeRequest_SendCode:
|
||||||
cmd = domain.NewSendCodeCommand(req.GetUserId(), req.GetSendCode().UrlTemplate)
|
// cmd = domain.NewSendCodeCommand(req.GetUserId(), req.GetSendCode().UrlTemplate)
|
||||||
case *user.SendEmailCodeRequest_ReturnCode:
|
// case *user.SendEmailCodeRequest_ReturnCode:
|
||||||
returnCode = domain.NewReturnCodeCommand(req.GetUserId())
|
// returnCode = domain.NewReturnCodeCommand(req.GetUserId())
|
||||||
cmd = returnCode
|
// cmd = returnCode
|
||||||
default:
|
// default:
|
||||||
cmd = domain.NewSendCodeCommand(req.GetUserId(), req.GetSendCode().UrlTemplate)
|
// cmd = domain.NewSendCodeCommand(req.GetUserId(), req.GetSendCode().UrlTemplate)
|
||||||
}
|
// }
|
||||||
err = domain.Invoke(ctx, cmd)
|
// err = domain.Invoke(ctx, cmd)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return nil, err
|
// return nil, err
|
||||||
}
|
// }
|
||||||
resp = new(user.SendEmailCodeResponse)
|
// resp = new(user.SendEmailCodeResponse)
|
||||||
if returnCode != nil {
|
// if returnCode != nil {
|
||||||
resp.VerificationCode = &returnCode.Code
|
// resp.VerificationCode = &returnCode.Code
|
||||||
}
|
// }
|
||||||
return resp, nil
|
// return resp, nil
|
||||||
}
|
// }
|
||||||
|
|
||||||
func ResendEmailCode(ctx context.Context, req *user.ResendEmailCodeRequest) (resp *user.SendEmailCodeResponse, err error) {
|
// func ResendEmailCode(ctx context.Context, req *user.ResendEmailCodeRequest) (resp *user.SendEmailCodeResponse, err error) {
|
||||||
var (
|
// var (
|
||||||
returnCode *domain.ReturnCodeCommand
|
// returnCode *domain.ReturnCodeCommand
|
||||||
cmd domain.Commander
|
// cmd domain.Commander
|
||||||
)
|
// )
|
||||||
|
|
||||||
switch req.GetVerification().(type) {
|
// switch req.GetVerification().(type) {
|
||||||
case *user.ResendEmailCodeRequest_SendCode:
|
// case *user.ResendEmailCodeRequest_SendCode:
|
||||||
cmd = domain.NewSendCodeCommand(req.GetUserId(), req.GetSendCode().UrlTemplate)
|
// cmd = domain.NewSendCodeCommand(req.GetUserId(), req.GetSendCode().UrlTemplate)
|
||||||
case *user.ResendEmailCodeRequest_ReturnCode:
|
// case *user.ResendEmailCodeRequest_ReturnCode:
|
||||||
returnCode = domain.NewReturnCodeCommand(req.GetUserId())
|
// returnCode = domain.NewReturnCodeCommand(req.GetUserId())
|
||||||
cmd = returnCode
|
// cmd = returnCode
|
||||||
default:
|
// default:
|
||||||
cmd = domain.NewSendCodeCommand(req.GetUserId(), req.GetSendCode().UrlTemplate)
|
// cmd = domain.NewSendCodeCommand(req.GetUserId(), req.GetSendCode().UrlTemplate)
|
||||||
}
|
// }
|
||||||
err = domain.Invoke(ctx, cmd)
|
// err = domain.Invoke(ctx, cmd)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return nil, err
|
// return nil, err
|
||||||
}
|
// }
|
||||||
resp = new(user.SendEmailCodeResponse)
|
// resp = new(user.SendEmailCodeResponse)
|
||||||
if returnCode != nil {
|
// if returnCode != nil {
|
||||||
resp.VerificationCode = &returnCode.Code
|
// resp.VerificationCode = &returnCode.Code
|
||||||
}
|
// }
|
||||||
return resp, nil
|
// return resp, nil
|
||||||
}
|
// }
|
||||||
|
@@ -1,19 +1,19 @@
|
|||||||
package userv2
|
package userv2
|
||||||
|
|
||||||
import (
|
// this file has been commented out to pass the linter
|
||||||
"github.com/zitadel/zitadel/backend/v3/telemetry/logging"
|
|
||||||
"github.com/zitadel/zitadel/backend/v3/telemetry/tracing"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
// import (
|
||||||
logger logging.Logger
|
// "github.com/zitadel/zitadel/backend/v3/telemetry/logging"
|
||||||
tracer tracing.Tracer
|
// "github.com/zitadel/zitadel/backend/v3/telemetry/tracing"
|
||||||
)
|
// )
|
||||||
|
|
||||||
func SetLogger(l logging.Logger) {
|
// logger logging.Logger
|
||||||
logger = l
|
// var tracer tracing.Tracer
|
||||||
}
|
|
||||||
|
|
||||||
func SetTracer(t tracing.Tracer) {
|
// func SetLogger(l logging.Logger) {
|
||||||
tracer = t
|
// logger = l
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
// func SetTracer(t tracing.Tracer) {
|
||||||
|
// tracer = t
|
||||||
|
// }
|
||||||
|
@@ -1,131 +1,131 @@
|
|||||||
package domain
|
package domain
|
||||||
|
|
||||||
import (
|
// import (
|
||||||
"context"
|
// "context"
|
||||||
"fmt"
|
// "fmt"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/backend/v3/storage/database"
|
// "github.com/zitadel/zitadel/backend/v3/storage/database"
|
||||||
)
|
// )
|
||||||
|
|
||||||
// Commander is the all it needs to implement the command pattern.
|
// // Commander is the all it needs to implement the command pattern.
|
||||||
// It is the interface all manipulations need to implement.
|
// // 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.
|
// // If possible it should also be used for queries. We will find out if this is possible in the future.
|
||||||
type Commander interface {
|
// type Commander interface {
|
||||||
Execute(ctx context.Context, opts *CommandOpts) (err error)
|
// Execute(ctx context.Context, opts *CommandOpts) (err error)
|
||||||
fmt.Stringer
|
// fmt.Stringer
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Invoker is part of the command pattern.
|
// // Invoker is part of the command pattern.
|
||||||
// It is the interface that is used to execute commands.
|
// // It is the interface that is used to execute commands.
|
||||||
type Invoker interface {
|
// type Invoker interface {
|
||||||
Invoke(ctx context.Context, command Commander, opts *CommandOpts) error
|
// Invoke(ctx context.Context, command Commander, opts *CommandOpts) error
|
||||||
}
|
// }
|
||||||
|
|
||||||
// CommandOpts are passed to each command
|
// // CommandOpts are passed to each command
|
||||||
// the provide common fields used by commands like the database client.
|
// // the provide common fields used by commands like the database client.
|
||||||
type CommandOpts struct {
|
// type CommandOpts struct {
|
||||||
DB database.QueryExecutor
|
// DB database.QueryExecutor
|
||||||
Invoker Invoker
|
// Invoker Invoker
|
||||||
}
|
// }
|
||||||
|
|
||||||
type ensureTxOpts struct {
|
// type ensureTxOpts struct {
|
||||||
*database.TransactionOptions
|
// *database.TransactionOptions
|
||||||
}
|
// }
|
||||||
|
|
||||||
type EnsureTransactionOpt func(*ensureTxOpts)
|
// type EnsureTransactionOpt func(*ensureTxOpts)
|
||||||
|
|
||||||
// EnsureTx ensures that the DB is a transaction. If it is not, it will start a new transaction.
|
// // EnsureTx ensures that the DB is a transaction. If it is not, it will start a new transaction.
|
||||||
// The returned close function will end the transaction. If the DB is already a transaction, the close function
|
// // The returned close function will end the transaction. If the DB is already a transaction, the close function
|
||||||
// will do nothing because another [Commander] is already responsible for ending the transaction.
|
// // will do nothing because another [Commander] is already responsible for ending the transaction.
|
||||||
func (o *CommandOpts) EnsureTx(ctx context.Context, opts ...EnsureTransactionOpt) (close func(context.Context, error) error, err error) {
|
// func (o *CommandOpts) EnsureTx(ctx context.Context, opts ...EnsureTransactionOpt) (close func(context.Context, error) error, err error) {
|
||||||
beginner, ok := o.DB.(database.Beginner)
|
// beginner, ok := o.DB.(database.Beginner)
|
||||||
if !ok {
|
// if !ok {
|
||||||
// db is already a transaction
|
// // db is already a transaction
|
||||||
return func(_ context.Context, err error) error {
|
// return func(_ context.Context, err error) error {
|
||||||
return err
|
// return err
|
||||||
}, nil
|
// }, nil
|
||||||
}
|
// }
|
||||||
|
|
||||||
txOpts := &ensureTxOpts{
|
// txOpts := &ensureTxOpts{
|
||||||
TransactionOptions: new(database.TransactionOptions),
|
// TransactionOptions: new(database.TransactionOptions),
|
||||||
}
|
// }
|
||||||
for _, opt := range opts {
|
// for _, opt := range opts {
|
||||||
opt(txOpts)
|
// opt(txOpts)
|
||||||
}
|
// }
|
||||||
|
|
||||||
tx, err := beginner.Begin(ctx, txOpts.TransactionOptions)
|
// tx, err := beginner.Begin(ctx, txOpts.TransactionOptions)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return nil, err
|
// return nil, err
|
||||||
}
|
// }
|
||||||
o.DB = tx
|
// o.DB = tx
|
||||||
|
|
||||||
return func(ctx context.Context, err error) error {
|
// return func(ctx context.Context, err error) error {
|
||||||
return tx.End(ctx, err)
|
// return tx.End(ctx, err)
|
||||||
}, nil
|
// }, nil
|
||||||
}
|
// }
|
||||||
|
|
||||||
// EnsureClient ensures that the o.DB is a client. If it is not, it will get a new client from the [database.Pool].
|
// // EnsureClient ensures that the o.DB is a client. If it is not, it will get a new client from the [database.Pool].
|
||||||
// The returned close function will release the client. If the o.DB is already a client or transaction, the close function
|
// // The returned close function will release the client. If the o.DB is already a client or transaction, the close function
|
||||||
// will do nothing because another [Commander] is already responsible for releasing the client.
|
// // will do nothing because another [Commander] is already responsible for releasing the client.
|
||||||
func (o *CommandOpts) EnsureClient(ctx context.Context) (close func(_ context.Context) error, err error) {
|
// func (o *CommandOpts) EnsureClient(ctx context.Context) (close func(_ context.Context) error, err error) {
|
||||||
pool, ok := o.DB.(database.Pool)
|
// pool, ok := o.DB.(database.Pool)
|
||||||
if !ok {
|
// if !ok {
|
||||||
// o.DB is already a client
|
// // o.DB is already a client
|
||||||
return func(_ context.Context) error {
|
// return func(_ context.Context) error {
|
||||||
return nil
|
// return nil
|
||||||
}, nil
|
// }, nil
|
||||||
}
|
// }
|
||||||
client, err := pool.Acquire(ctx)
|
// client, err := pool.Acquire(ctx)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return nil, err
|
// return nil, err
|
||||||
}
|
// }
|
||||||
o.DB = client
|
// o.DB = client
|
||||||
return func(ctx context.Context) error {
|
// return func(ctx context.Context) error {
|
||||||
return client.Release(ctx)
|
// return client.Release(ctx)
|
||||||
}, nil
|
// }, nil
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (o *CommandOpts) Invoke(ctx context.Context, command Commander) error {
|
// func (o *CommandOpts) Invoke(ctx context.Context, command Commander) error {
|
||||||
if o.Invoker == nil {
|
// if o.Invoker == nil {
|
||||||
return command.Execute(ctx, o)
|
// return command.Execute(ctx, o)
|
||||||
}
|
// }
|
||||||
return o.Invoker.Invoke(ctx, command, o)
|
// return o.Invoker.Invoke(ctx, command, o)
|
||||||
}
|
// }
|
||||||
|
|
||||||
func DefaultOpts(invoker Invoker) *CommandOpts {
|
// func DefaultOpts(invoker Invoker) *CommandOpts {
|
||||||
if invoker == nil {
|
// if invoker == nil {
|
||||||
invoker = &noopInvoker{}
|
// invoker = &noopInvoker{}
|
||||||
}
|
// }
|
||||||
return &CommandOpts{
|
// return &CommandOpts{
|
||||||
DB: pool,
|
// DB: pool,
|
||||||
Invoker: invoker,
|
// Invoker: invoker,
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// commandBatch is a batch of commands.
|
// // commandBatch is a batch of commands.
|
||||||
// It uses the [Invoker] provided by the opts to execute each command.
|
// // It uses the [Invoker] provided by the opts to execute each command.
|
||||||
type commandBatch struct {
|
// type commandBatch struct {
|
||||||
Commands []Commander
|
// Commands []Commander
|
||||||
}
|
// }
|
||||||
|
|
||||||
func BatchCommands(cmds ...Commander) *commandBatch {
|
// func BatchCommands(cmds ...Commander) *commandBatch {
|
||||||
return &commandBatch{
|
// return &commandBatch{
|
||||||
Commands: cmds,
|
// Commands: cmds,
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// String implements [Commander].
|
// // String implements [Commander].
|
||||||
func (cmd *commandBatch) String() string {
|
// func (cmd *commandBatch) String() string {
|
||||||
return "commandBatch"
|
// return "commandBatch"
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (b *commandBatch) Execute(ctx context.Context, opts *CommandOpts) (err error) {
|
// func (b *commandBatch) Execute(ctx context.Context, opts *CommandOpts) (err error) {
|
||||||
for _, cmd := range b.Commands {
|
// for _, cmd := range b.Commands {
|
||||||
if err = opts.Invoke(ctx, cmd); err != nil {
|
// if err = opts.Invoke(ctx, cmd); err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
return nil
|
// return nil
|
||||||
}
|
// }
|
||||||
|
|
||||||
var _ Commander = (*commandBatch)(nil)
|
// var _ Commander = (*commandBatch)(nil)
|
||||||
|
@@ -1,90 +1,90 @@
|
|||||||
package domain
|
package domain
|
||||||
|
|
||||||
import (
|
// import (
|
||||||
"context"
|
// "context"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/backend/v3/storage/eventstore"
|
// "github.com/zitadel/zitadel/backend/v3/storage/eventstore"
|
||||||
)
|
// )
|
||||||
|
|
||||||
// CreateUserCommand adds a new user including the email verification for humans.
|
// // 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:
|
// // In the future it might make sense to separate the command into two commands:
|
||||||
// - CreateHumanCommand: creates a new human user
|
// // - CreateHumanCommand: creates a new human user
|
||||||
// - CreateMachineCommand: creates a new machine user
|
// // - CreateMachineCommand: creates a new machine user
|
||||||
type CreateUserCommand struct {
|
// type CreateUserCommand struct {
|
||||||
user *User
|
// user *User
|
||||||
email *SetEmailCommand
|
// email *SetEmailCommand
|
||||||
}
|
// }
|
||||||
|
|
||||||
var (
|
// var (
|
||||||
_ Commander = (*CreateUserCommand)(nil)
|
// _ Commander = (*CreateUserCommand)(nil)
|
||||||
_ eventer = (*CreateUserCommand)(nil)
|
// _ 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.
|
// // 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 {
|
// func NewCreateHumanCommand(username string, opts ...CreateHumanOpt) *CreateUserCommand {
|
||||||
cmd := &CreateUserCommand{
|
// cmd := &CreateUserCommand{
|
||||||
user: &User{
|
// user: &User{
|
||||||
Username: username,
|
// Username: username,
|
||||||
Traits: &Human{},
|
// Traits: &Human{},
|
||||||
},
|
// },
|
||||||
}
|
// }
|
||||||
|
|
||||||
for _, opt := range opts {
|
// for _, opt := range opts {
|
||||||
opt.applyOnCreateHuman(cmd)
|
// opt.applyOnCreateHuman(cmd)
|
||||||
}
|
// }
|
||||||
return cmd
|
// return cmd
|
||||||
}
|
// }
|
||||||
|
|
||||||
// String implements [Commander].
|
// // String implements [Commander].
|
||||||
func (cmd *CreateUserCommand) String() string {
|
// func (cmd *CreateUserCommand) String() string {
|
||||||
return "CreateUserCommand"
|
// return "CreateUserCommand"
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Events implements [eventer].
|
// // Events implements [eventer].
|
||||||
func (c *CreateUserCommand) Events() []*eventstore.Event {
|
// func (c *CreateUserCommand) Events() []*eventstore.Event {
|
||||||
return []*eventstore.Event{
|
// return []*eventstore.Event{
|
||||||
{
|
// {
|
||||||
AggregateType: "user",
|
// AggregateType: "user",
|
||||||
AggregateID: c.user.ID,
|
// AggregateID: c.user.ID,
|
||||||
Type: "user.added",
|
// Type: "user.added",
|
||||||
Payload: c.user,
|
// Payload: c.user,
|
||||||
},
|
// },
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Execute implements [Commander].
|
// // Execute implements [Commander].
|
||||||
func (c *CreateUserCommand) Execute(ctx context.Context, opts *CommandOpts) error {
|
// func (c *CreateUserCommand) Execute(ctx context.Context, opts *CommandOpts) error {
|
||||||
if err := c.ensureUserID(); err != nil {
|
// if err := c.ensureUserID(); err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
c.email.UserID = c.user.ID
|
// c.email.UserID = c.user.ID
|
||||||
if err := opts.Invoke(ctx, c.email); err != nil {
|
// if err := opts.Invoke(ctx, c.email); err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
return nil
|
// return nil
|
||||||
}
|
// }
|
||||||
|
|
||||||
type CreateHumanOpt interface {
|
// type CreateHumanOpt interface {
|
||||||
applyOnCreateHuman(*CreateUserCommand)
|
// applyOnCreateHuman(*CreateUserCommand)
|
||||||
}
|
// }
|
||||||
|
|
||||||
type createHumanIDOpt string
|
// type createHumanIDOpt string
|
||||||
|
|
||||||
// applyOnCreateHuman implements [CreateHumanOpt].
|
// // applyOnCreateHuman implements [CreateHumanOpt].
|
||||||
func (c createHumanIDOpt) applyOnCreateHuman(cmd *CreateUserCommand) {
|
// func (c createHumanIDOpt) applyOnCreateHuman(cmd *CreateUserCommand) {
|
||||||
cmd.user.ID = string(c)
|
// cmd.user.ID = string(c)
|
||||||
}
|
// }
|
||||||
|
|
||||||
var _ CreateHumanOpt = (*createHumanIDOpt)(nil)
|
// var _ CreateHumanOpt = (*createHumanIDOpt)(nil)
|
||||||
|
|
||||||
func CreateHumanWithID(id string) CreateHumanOpt {
|
// func CreateHumanWithID(id string) CreateHumanOpt {
|
||||||
return createHumanIDOpt(id)
|
// return createHumanIDOpt(id)
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (c *CreateUserCommand) ensureUserID() (err error) {
|
// func (c *CreateUserCommand) ensureUserID() (err error) {
|
||||||
if c.user.ID != "" {
|
// if c.user.ID != "" {
|
||||||
return nil
|
// return nil
|
||||||
}
|
// }
|
||||||
c.user.ID, err = generateID()
|
// c.user.ID, err = generateID()
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
|
@@ -1,37 +1,37 @@
|
|||||||
package domain
|
package domain
|
||||||
|
|
||||||
import (
|
// import (
|
||||||
"context"
|
// "context"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/crypto"
|
// "github.com/zitadel/zitadel/internal/crypto"
|
||||||
)
|
// )
|
||||||
|
|
||||||
type generateCodeCommand struct {
|
// type generateCodeCommand struct {
|
||||||
code string
|
// code string
|
||||||
value *crypto.CryptoValue
|
// value *crypto.CryptoValue
|
||||||
}
|
// }
|
||||||
|
|
||||||
// I didn't update this repository to the solution proposed please view one of the following interfaces for correct usage:
|
// // I didn't update this repository to the solution proposed please view one of the following interfaces for correct usage:
|
||||||
// - [UserRepository]
|
// // - [UserRepository]
|
||||||
// - [InstanceRepository]
|
// // - [InstanceRepository]
|
||||||
// - [OrgRepository]
|
// // - [OrgRepository]
|
||||||
type CryptoRepository interface {
|
// type CryptoRepository interface {
|
||||||
GetEncryptionConfig(ctx context.Context) (*crypto.GeneratorConfig, error)
|
// GetEncryptionConfig(ctx context.Context) (*crypto.GeneratorConfig, error)
|
||||||
}
|
// }
|
||||||
|
|
||||||
// String implements [Commander].
|
// // String implements [Commander].
|
||||||
func (cmd *generateCodeCommand) String() string {
|
// func (cmd *generateCodeCommand) String() string {
|
||||||
return "generateCodeCommand"
|
// return "generateCodeCommand"
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (cmd *generateCodeCommand) Execute(ctx context.Context, opts *CommandOpts) error {
|
// func (cmd *generateCodeCommand) Execute(ctx context.Context, opts *CommandOpts) error {
|
||||||
config, err := cryptoRepo(opts.DB).GetEncryptionConfig(ctx)
|
// config, err := cryptoRepo(opts.DB).GetEncryptionConfig(ctx)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
generator := crypto.NewEncryptionGenerator(*config, userCodeAlgorithm)
|
// generator := crypto.NewEncryptionGenerator(*config, userCodeAlgorithm)
|
||||||
cmd.value, cmd.code, err = crypto.NewCode(generator)
|
// cmd.value, cmd.code, err = crypto.NewCode(generator)
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
|
|
||||||
var _ Commander = (*generateCodeCommand)(nil)
|
// var _ Commander = (*generateCodeCommand)(nil)
|
||||||
|
@@ -1,65 +1,66 @@
|
|||||||
package domain
|
package domain
|
||||||
|
|
||||||
import (
|
// import (
|
||||||
"math/rand/v2"
|
// "math/rand/v2"
|
||||||
"strconv"
|
// "strconv"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/backend/v3/storage/cache"
|
// "github.com/zitadel/zitadel/backend/v3/storage/cache"
|
||||||
"github.com/zitadel/zitadel/backend/v3/storage/database"
|
// "github.com/zitadel/zitadel/backend/v3/storage/database"
|
||||||
"github.com/zitadel/zitadel/backend/v3/telemetry/logging"
|
|
||||||
"github.com/zitadel/zitadel/backend/v3/telemetry/tracing"
|
|
||||||
"github.com/zitadel/zitadel/internal/crypto"
|
|
||||||
)
|
|
||||||
|
|
||||||
// The variables could also be moved to a struct.
|
// // "github.com/zitadel/zitadel/backend/v3/telemetry/logging"
|
||||||
// I just started with the singleton pattern and kept it like this.
|
// "github.com/zitadel/zitadel/backend/v3/telemetry/tracing"
|
||||||
var (
|
// "github.com/zitadel/zitadel/internal/crypto"
|
||||||
pool database.Pool
|
// )
|
||||||
userCodeAlgorithm crypto.EncryptionAlgorithm
|
|
||||||
tracer tracing.Tracer
|
|
||||||
logger logging.Logger
|
|
||||||
|
|
||||||
userRepo func(database.QueryExecutor) UserRepository
|
// // The variables could also be moved to a struct.
|
||||||
instanceRepo func(database.QueryExecutor) InstanceRepository
|
// // I just started with the singleton pattern and kept it like this.
|
||||||
cryptoRepo func(database.QueryExecutor) CryptoRepository
|
// var (
|
||||||
orgRepo func(database.QueryExecutor) OrgRepository
|
// pool database.Pool
|
||||||
|
// userCodeAlgorithm crypto.EncryptionAlgorithm
|
||||||
|
// tracer tracing.Tracer
|
||||||
|
// // logger logging.Logger
|
||||||
|
|
||||||
instanceCache cache.Cache[instanceCacheIndex, string, *Instance]
|
// userRepo func(database.QueryExecutor) UserRepository
|
||||||
orgCache cache.Cache[orgCacheIndex, string, *Org]
|
// // instanceRepo func(database.QueryExecutor) InstanceRepository
|
||||||
|
// cryptoRepo func(database.QueryExecutor) CryptoRepository
|
||||||
|
// orgRepo func(database.QueryExecutor) OrgRepository
|
||||||
|
|
||||||
generateID func() (string, error) = func() (string, error) {
|
// // instanceCache cache.Cache[instanceCacheIndex, string, *Instance]
|
||||||
return strconv.FormatUint(rand.Uint64(), 10), nil
|
// orgCache cache.Cache[orgCacheIndex, string, *Org]
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func SetPool(p database.Pool) {
|
// generateID func() (string, error) = func() (string, error) {
|
||||||
pool = p
|
// return strconv.FormatUint(rand.Uint64(), 10), nil
|
||||||
}
|
// }
|
||||||
|
// )
|
||||||
|
|
||||||
func SetUserCodeAlgorithm(algorithm crypto.EncryptionAlgorithm) {
|
// func SetPool(p database.Pool) {
|
||||||
userCodeAlgorithm = algorithm
|
// pool = p
|
||||||
}
|
// }
|
||||||
|
|
||||||
func SetTracer(t tracing.Tracer) {
|
// func SetUserCodeAlgorithm(algorithm crypto.EncryptionAlgorithm) {
|
||||||
tracer = t
|
// userCodeAlgorithm = algorithm
|
||||||
}
|
// }
|
||||||
|
|
||||||
func SetLogger(l logging.Logger) {
|
// func SetTracer(t tracing.Tracer) {
|
||||||
logger = l
|
// tracer = t
|
||||||
}
|
// }
|
||||||
|
|
||||||
func SetUserRepository(repo func(database.QueryExecutor) UserRepository) {
|
// // func SetLogger(l logging.Logger) {
|
||||||
userRepo = repo
|
// // logger = l
|
||||||
}
|
// // }
|
||||||
|
|
||||||
func SetOrgRepository(repo func(database.QueryExecutor) OrgRepository) {
|
// func SetUserRepository(repo func(database.QueryExecutor) UserRepository) {
|
||||||
orgRepo = repo
|
// userRepo = repo
|
||||||
}
|
// }
|
||||||
|
|
||||||
func SetInstanceRepository(repo func(database.QueryExecutor) InstanceRepository) {
|
// func SetOrgRepository(repo func(database.QueryExecutor) OrgRepository) {
|
||||||
instanceRepo = repo
|
// orgRepo = repo
|
||||||
}
|
// }
|
||||||
|
|
||||||
func SetCryptoRepository(repo func(database.QueryExecutor) CryptoRepository) {
|
// // func SetInstanceRepository(repo func(database.QueryExecutor) InstanceRepository) {
|
||||||
cryptoRepo = repo
|
// // instanceRepo = repo
|
||||||
}
|
// // }
|
||||||
|
|
||||||
|
// func SetCryptoRepository(repo func(database.QueryExecutor) CryptoRepository) {
|
||||||
|
// cryptoRepo = repo
|
||||||
|
// }
|
||||||
|
@@ -1,67 +1,67 @@
|
|||||||
package domain_test
|
package domain_test
|
||||||
|
|
||||||
import (
|
// import (
|
||||||
"context"
|
// "context"
|
||||||
"log/slog"
|
// "log/slog"
|
||||||
"testing"
|
// "testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
// "github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
// "github.com/stretchr/testify/require"
|
||||||
"go.opentelemetry.io/otel"
|
// "go.opentelemetry.io/otel"
|
||||||
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
|
// "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
|
||||||
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
// sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
||||||
"go.uber.org/mock/gomock"
|
// "go.uber.org/mock/gomock"
|
||||||
|
|
||||||
. "github.com/zitadel/zitadel/backend/v3/domain"
|
// . "github.com/zitadel/zitadel/backend/v3/domain"
|
||||||
"github.com/zitadel/zitadel/backend/v3/storage/database/dbmock"
|
// "github.com/zitadel/zitadel/backend/v3/storage/database/dbmock"
|
||||||
"github.com/zitadel/zitadel/backend/v3/storage/database/repository"
|
// "github.com/zitadel/zitadel/backend/v3/storage/database/repository"
|
||||||
"github.com/zitadel/zitadel/backend/v3/telemetry/logging"
|
// "github.com/zitadel/zitadel/backend/v3/telemetry/logging"
|
||||||
"github.com/zitadel/zitadel/backend/v3/telemetry/tracing"
|
// "github.com/zitadel/zitadel/backend/v3/telemetry/tracing"
|
||||||
)
|
// )
|
||||||
|
|
||||||
// These tests give an overview of how to use the domain package.
|
// These tests give an overview of how to use the domain package.
|
||||||
func TestExample(t *testing.T) {
|
// func TestExample(t *testing.T) {
|
||||||
t.Skip("skip example test because it is not a real test")
|
// t.Skip("skip example test because it is not a real test")
|
||||||
ctx := context.Background()
|
// ctx := context.Background()
|
||||||
|
|
||||||
ctrl := gomock.NewController(t)
|
// ctrl := gomock.NewController(t)
|
||||||
pool := dbmock.NewMockPool(ctrl)
|
// pool := dbmock.NewMockPool(ctrl)
|
||||||
tx := dbmock.NewMockTransaction(ctrl)
|
// tx := dbmock.NewMockTransaction(ctrl)
|
||||||
|
|
||||||
pool.EXPECT().Begin(gomock.Any(), gomock.Any()).Return(tx, nil)
|
// pool.EXPECT().Begin(gomock.Any(), gomock.Any()).Return(tx, nil)
|
||||||
tx.EXPECT().End(gomock.Any(), gomock.Any()).Return(nil)
|
// tx.EXPECT().End(gomock.Any(), gomock.Any()).Return(nil)
|
||||||
SetPool(pool)
|
// SetPool(pool)
|
||||||
|
|
||||||
exporter, err := stdouttrace.New(stdouttrace.WithPrettyPrint())
|
// exporter, err := stdouttrace.New(stdouttrace.WithPrettyPrint())
|
||||||
require.NoError(t, err)
|
// require.NoError(t, err)
|
||||||
tracerProvider := sdktrace.NewTracerProvider(
|
// tracerProvider := sdktrace.NewTracerProvider(
|
||||||
sdktrace.WithSyncer(exporter),
|
// sdktrace.WithSyncer(exporter),
|
||||||
)
|
// )
|
||||||
otel.SetTracerProvider(tracerProvider)
|
// otel.SetTracerProvider(tracerProvider)
|
||||||
SetTracer(tracing.Tracer{Tracer: tracerProvider.Tracer("test")})
|
// SetTracer(tracing.Tracer{Tracer: tracerProvider.Tracer("test")})
|
||||||
defer func() { assert.NoError(t, tracerProvider.Shutdown(ctx)) }()
|
// defer func() { assert.NoError(t, tracerProvider.Shutdown(ctx)) }()
|
||||||
|
|
||||||
SetLogger(logging.Logger{Logger: slog.Default()})
|
// SetLogger(logging.Logger{Logger: slog.Default()})
|
||||||
|
|
||||||
SetUserRepository(repository.UserRepository)
|
// SetUserRepository(repository.UserRepository)
|
||||||
SetOrgRepository(repository.OrgRepository)
|
// SetOrgRepository(repository.OrgRepository)
|
||||||
// SetInstanceRepository(repository.Instance)
|
// // SetInstanceRepository(repository.Instance)
|
||||||
// SetCryptoRepository(repository.Crypto)
|
// // SetCryptoRepository(repository.Crypto)
|
||||||
|
|
||||||
t.Run("create org", func(t *testing.T) {
|
// t.Run("create org", func(t *testing.T) {
|
||||||
org := NewAddOrgCommand("testorg", NewAddMemberCommand("testuser", "ORG_OWNER"))
|
// org := NewAddOrgCommand("testorg", NewAddMemberCommand("testuser", "ORG_OWNER"))
|
||||||
user := NewCreateHumanCommand("testuser")
|
// user := NewCreateHumanCommand("testuser")
|
||||||
err := Invoke(ctx, BatchCommands(org, user))
|
// err := Invoke(ctx, BatchCommands(org, user))
|
||||||
assert.NoError(t, err)
|
// assert.NoError(t, err)
|
||||||
})
|
// })
|
||||||
|
|
||||||
t.Run("verified email", func(t *testing.T) {
|
// t.Run("verified email", func(t *testing.T) {
|
||||||
err := Invoke(ctx, NewSetEmailCommand("u1", "test@example.com", NewEmailVerifiedCommand("u1", true)))
|
// err := Invoke(ctx, NewSetEmailCommand("u1", "test@example.com", NewEmailVerifiedCommand("u1", true)))
|
||||||
assert.NoError(t, err)
|
// assert.NoError(t, err)
|
||||||
})
|
// })
|
||||||
|
|
||||||
t.Run("unverified email", func(t *testing.T) {
|
// t.Run("unverified email", func(t *testing.T) {
|
||||||
err := Invoke(ctx, NewSetEmailCommand("u2", "test2@example.com", NewEmailVerifiedCommand("u2", false)))
|
// err := Invoke(ctx, NewSetEmailCommand("u2", "test2@example.com", NewEmailVerifiedCommand("u2", false)))
|
||||||
assert.NoError(t, err)
|
// assert.NoError(t, err)
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
|
@@ -1,175 +1,175 @@
|
|||||||
package domain
|
package domain
|
||||||
|
|
||||||
import (
|
// import (
|
||||||
"context"
|
// "context"
|
||||||
"time"
|
// "time"
|
||||||
)
|
// )
|
||||||
|
|
||||||
// EmailVerifiedCommand verifies an email address for a user.
|
// // EmailVerifiedCommand verifies an email address for a user.
|
||||||
type EmailVerifiedCommand struct {
|
// type EmailVerifiedCommand struct {
|
||||||
UserID string `json:"userId"`
|
// UserID string `json:"userId"`
|
||||||
Email *Email `json:"email"`
|
// Email *Email `json:"email"`
|
||||||
}
|
// }
|
||||||
|
|
||||||
func NewEmailVerifiedCommand(userID string, isVerified bool) *EmailVerifiedCommand {
|
// func NewEmailVerifiedCommand(userID string, isVerified bool) *EmailVerifiedCommand {
|
||||||
return &EmailVerifiedCommand{
|
// return &EmailVerifiedCommand{
|
||||||
UserID: userID,
|
// UserID: userID,
|
||||||
Email: &Email{
|
// Email: &Email{
|
||||||
VerifiedAt: time.Time{},
|
// VerifiedAt: time.Time{},
|
||||||
},
|
// },
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// String implements [Commander].
|
// // String implements [Commander].
|
||||||
func (cmd *EmailVerifiedCommand) String() string {
|
// func (cmd *EmailVerifiedCommand) String() string {
|
||||||
return "EmailVerifiedCommand"
|
// return "EmailVerifiedCommand"
|
||||||
}
|
// }
|
||||||
|
|
||||||
var (
|
// var (
|
||||||
_ Commander = (*EmailVerifiedCommand)(nil)
|
// _ Commander = (*EmailVerifiedCommand)(nil)
|
||||||
_ SetEmailOpt = (*EmailVerifiedCommand)(nil)
|
// _ SetEmailOpt = (*EmailVerifiedCommand)(nil)
|
||||||
)
|
// )
|
||||||
|
|
||||||
// Execute implements [Commander]
|
// // Execute implements [Commander]
|
||||||
func (cmd *EmailVerifiedCommand) Execute(ctx context.Context, opts *CommandOpts) error {
|
// func (cmd *EmailVerifiedCommand) Execute(ctx context.Context, opts *CommandOpts) error {
|
||||||
repo := userRepo(opts.DB).Human()
|
// repo := userRepo(opts.DB).Human()
|
||||||
return repo.Update(ctx, repo.IDCondition(cmd.UserID), repo.SetEmailVerifiedAt(time.Time{}))
|
// return repo.Update(ctx, repo.IDCondition(cmd.UserID), repo.SetEmailVerifiedAt(time.Time{}))
|
||||||
}
|
// }
|
||||||
|
|
||||||
// applyOnSetEmail implements [SetEmailOpt]
|
// // applyOnSetEmail implements [SetEmailOpt]
|
||||||
func (cmd *EmailVerifiedCommand) applyOnSetEmail(setEmailCmd *SetEmailCommand) {
|
// func (cmd *EmailVerifiedCommand) applyOnSetEmail(setEmailCmd *SetEmailCommand) {
|
||||||
cmd.UserID = setEmailCmd.UserID
|
// cmd.UserID = setEmailCmd.UserID
|
||||||
cmd.Email.Address = setEmailCmd.Email
|
// cmd.Email.Address = setEmailCmd.Email
|
||||||
setEmailCmd.verification = cmd
|
// setEmailCmd.verification = cmd
|
||||||
}
|
// }
|
||||||
|
|
||||||
// SendCodeCommand sends a verification code to the user's email address.
|
// // 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.
|
// // If the URLTemplate is not set it will use the default of the organization / instance.
|
||||||
type SendCodeCommand struct {
|
// type SendCodeCommand struct {
|
||||||
UserID string `json:"userId"`
|
// UserID string `json:"userId"`
|
||||||
Email string `json:"email"`
|
// Email string `json:"email"`
|
||||||
URLTemplate *string `json:"urlTemplate"`
|
// URLTemplate *string `json:"urlTemplate"`
|
||||||
generator *generateCodeCommand
|
// generator *generateCodeCommand
|
||||||
}
|
// }
|
||||||
|
|
||||||
var (
|
// var (
|
||||||
_ Commander = (*SendCodeCommand)(nil)
|
// _ Commander = (*SendCodeCommand)(nil)
|
||||||
_ SetEmailOpt = (*SendCodeCommand)(nil)
|
// _ SetEmailOpt = (*SendCodeCommand)(nil)
|
||||||
)
|
// )
|
||||||
|
|
||||||
func NewSendCodeCommand(userID string, urlTemplate *string) *SendCodeCommand {
|
// func NewSendCodeCommand(userID string, urlTemplate *string) *SendCodeCommand {
|
||||||
return &SendCodeCommand{
|
// return &SendCodeCommand{
|
||||||
UserID: userID,
|
// UserID: userID,
|
||||||
generator: &generateCodeCommand{},
|
// generator: &generateCodeCommand{},
|
||||||
URLTemplate: urlTemplate,
|
// URLTemplate: urlTemplate,
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// String implements [Commander].
|
// // String implements [Commander].
|
||||||
func (cmd *SendCodeCommand) String() string {
|
// func (cmd *SendCodeCommand) String() string {
|
||||||
return "SendCodeCommand"
|
// return "SendCodeCommand"
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Execute implements [Commander]
|
// // Execute implements [Commander]
|
||||||
func (cmd *SendCodeCommand) Execute(ctx context.Context, opts *CommandOpts) error {
|
// func (cmd *SendCodeCommand) Execute(ctx context.Context, opts *CommandOpts) error {
|
||||||
if err := cmd.ensureEmail(ctx, opts); err != nil {
|
// if err := cmd.ensureEmail(ctx, opts); err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
if err := cmd.ensureURL(ctx, opts); err != nil {
|
// if err := cmd.ensureURL(ctx, opts); err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
|
|
||||||
if err := opts.Invoker.Invoke(ctx, cmd.generator, opts); err != nil {
|
// if err := opts.Invoker.Invoke(ctx, cmd.generator, opts); err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
// TODO: queue notification
|
// // TODO: queue notification
|
||||||
|
|
||||||
return nil
|
// return nil
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (cmd *SendCodeCommand) ensureEmail(ctx context.Context, opts *CommandOpts) error {
|
// func (cmd *SendCodeCommand) ensureEmail(ctx context.Context, opts *CommandOpts) error {
|
||||||
if cmd.Email != "" {
|
// if cmd.Email != "" {
|
||||||
return nil
|
// return nil
|
||||||
}
|
// }
|
||||||
repo := userRepo(opts.DB).Human()
|
// repo := userRepo(opts.DB).Human()
|
||||||
email, err := repo.GetEmail(ctx, repo.IDCondition(cmd.UserID))
|
// email, err := repo.GetEmail(ctx, repo.IDCondition(cmd.UserID))
|
||||||
if err != nil || !email.VerifiedAt.IsZero() {
|
// if err != nil || !email.VerifiedAt.IsZero() {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
cmd.Email = email.Address
|
// cmd.Email = email.Address
|
||||||
return nil
|
// return nil
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (cmd *SendCodeCommand) ensureURL(ctx context.Context, opts *CommandOpts) error {
|
// func (cmd *SendCodeCommand) ensureURL(ctx context.Context, opts *CommandOpts) error {
|
||||||
if cmd.URLTemplate != nil && *cmd.URLTemplate != "" {
|
// if cmd.URLTemplate != nil && *cmd.URLTemplate != "" {
|
||||||
return nil
|
// return nil
|
||||||
}
|
// }
|
||||||
_, _ = ctx, opts
|
// _, _ = ctx, opts
|
||||||
// TODO: load default template
|
// // TODO: load default template
|
||||||
return nil
|
// return nil
|
||||||
}
|
// }
|
||||||
|
|
||||||
// applyOnSetEmail implements [SetEmailOpt]
|
// // applyOnSetEmail implements [SetEmailOpt]
|
||||||
func (cmd *SendCodeCommand) applyOnSetEmail(setEmailCmd *SetEmailCommand) {
|
// func (cmd *SendCodeCommand) applyOnSetEmail(setEmailCmd *SetEmailCommand) {
|
||||||
cmd.UserID = setEmailCmd.UserID
|
// cmd.UserID = setEmailCmd.UserID
|
||||||
cmd.Email = setEmailCmd.Email
|
// cmd.Email = setEmailCmd.Email
|
||||||
setEmailCmd.verification = cmd
|
// setEmailCmd.verification = cmd
|
||||||
}
|
// }
|
||||||
|
|
||||||
// ReturnCodeCommand creates the code and returns it to the caller.
|
// // 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.
|
// // The caller gets the code by calling the Code field after the command got executed.
|
||||||
type ReturnCodeCommand struct {
|
// type ReturnCodeCommand struct {
|
||||||
UserID string `json:"userId"`
|
// UserID string `json:"userId"`
|
||||||
Email string `json:"email"`
|
// Email string `json:"email"`
|
||||||
Code string `json:"code"`
|
// Code string `json:"code"`
|
||||||
generator *generateCodeCommand
|
// generator *generateCodeCommand
|
||||||
}
|
// }
|
||||||
|
|
||||||
var (
|
// var (
|
||||||
_ Commander = (*ReturnCodeCommand)(nil)
|
// _ Commander = (*ReturnCodeCommand)(nil)
|
||||||
_ SetEmailOpt = (*ReturnCodeCommand)(nil)
|
// _ SetEmailOpt = (*ReturnCodeCommand)(nil)
|
||||||
)
|
// )
|
||||||
|
|
||||||
func NewReturnCodeCommand(userID string) *ReturnCodeCommand {
|
// func NewReturnCodeCommand(userID string) *ReturnCodeCommand {
|
||||||
return &ReturnCodeCommand{
|
// return &ReturnCodeCommand{
|
||||||
UserID: userID,
|
// UserID: userID,
|
||||||
generator: &generateCodeCommand{},
|
// generator: &generateCodeCommand{},
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// String implements [Commander].
|
// // String implements [Commander].
|
||||||
func (cmd *ReturnCodeCommand) String() string {
|
// func (cmd *ReturnCodeCommand) String() string {
|
||||||
return "ReturnCodeCommand"
|
// return "ReturnCodeCommand"
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Execute implements [Commander]
|
// // Execute implements [Commander]
|
||||||
func (cmd *ReturnCodeCommand) Execute(ctx context.Context, opts *CommandOpts) error {
|
// func (cmd *ReturnCodeCommand) Execute(ctx context.Context, opts *CommandOpts) error {
|
||||||
if err := cmd.ensureEmail(ctx, opts); err != nil {
|
// if err := cmd.ensureEmail(ctx, opts); err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
if err := opts.Invoker.Invoke(ctx, cmd.generator, opts); err != nil {
|
// if err := opts.Invoker.Invoke(ctx, cmd.generator, opts); err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
cmd.Code = cmd.generator.code
|
// cmd.Code = cmd.generator.code
|
||||||
return nil
|
// return nil
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (cmd *ReturnCodeCommand) ensureEmail(ctx context.Context, opts *CommandOpts) error {
|
// func (cmd *ReturnCodeCommand) ensureEmail(ctx context.Context, opts *CommandOpts) error {
|
||||||
if cmd.Email != "" {
|
// if cmd.Email != "" {
|
||||||
return nil
|
// return nil
|
||||||
}
|
// }
|
||||||
repo := userRepo(opts.DB).Human()
|
// repo := userRepo(opts.DB).Human()
|
||||||
email, err := repo.GetEmail(ctx, repo.IDCondition(cmd.UserID))
|
// email, err := repo.GetEmail(ctx, repo.IDCondition(cmd.UserID))
|
||||||
if err != nil || !email.VerifiedAt.IsZero() {
|
// if err != nil || !email.VerifiedAt.IsZero() {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
cmd.Email = email.Address
|
// cmd.Email = email.Address
|
||||||
return nil
|
// return nil
|
||||||
}
|
// }
|
||||||
|
|
||||||
// applyOnSetEmail implements [SetEmailOpt]
|
// // applyOnSetEmail implements [SetEmailOpt]
|
||||||
func (cmd *ReturnCodeCommand) applyOnSetEmail(setEmailCmd *SetEmailCommand) {
|
// func (cmd *ReturnCodeCommand) applyOnSetEmail(setEmailCmd *SetEmailCommand) {
|
||||||
cmd.UserID = setEmailCmd.UserID
|
// cmd.UserID = setEmailCmd.UserID
|
||||||
cmd.Email = setEmailCmd.Email
|
// cmd.Email = setEmailCmd.Email
|
||||||
setEmailCmd.verification = cmd
|
// setEmailCmd.verification = cmd
|
||||||
}
|
// }
|
||||||
|
@@ -1,158 +1,158 @@
|
|||||||
package domain
|
package domain
|
||||||
|
|
||||||
import (
|
// import (
|
||||||
"context"
|
// "context"
|
||||||
"fmt"
|
// "fmt"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/backend/v3/storage/eventstore"
|
// "github.com/zitadel/zitadel/backend/v3/storage/eventstore"
|
||||||
)
|
// )
|
||||||
|
|
||||||
// Invoke provides a way to execute commands within the domain package.
|
// // Invoke provides a way to execute commands within the domain package.
|
||||||
// It uses a chain of responsibility pattern to handle the command execution.
|
// // It uses a chain of responsibility pattern to handle the command execution.
|
||||||
// The default chain includes logging, tracing, and event publishing.
|
// // 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].
|
// // If you want to invoke multiple commands in a single transaction, you can use the [commandBatch].
|
||||||
func Invoke(ctx context.Context, cmd Commander) error {
|
// func Invoke(ctx context.Context, cmd Commander) error {
|
||||||
invoker := newEventStoreInvoker(newLoggingInvoker(newTraceInvoker(nil)))
|
// invoker := newEventStoreInvoker(newLoggingInvoker(newTraceInvoker(nil)))
|
||||||
opts := &CommandOpts{
|
// opts := &CommandOpts{
|
||||||
Invoker: invoker.collector,
|
// Invoker: invoker.collector,
|
||||||
DB: pool,
|
// DB: pool,
|
||||||
}
|
// }
|
||||||
return invoker.Invoke(ctx, cmd, opts)
|
// return invoker.Invoke(ctx, cmd, opts)
|
||||||
}
|
// }
|
||||||
|
|
||||||
// eventStoreInvoker checks if the command implements the [eventer] interface.
|
// // eventStoreInvoker checks if the command implements the [eventer] interface.
|
||||||
// If it does, it collects the events and publishes them to the event store.
|
// // If it does, it collects the events and publishes them to the event store.
|
||||||
type eventStoreInvoker struct {
|
// type eventStoreInvoker struct {
|
||||||
collector *eventCollector
|
// collector *eventCollector
|
||||||
}
|
// }
|
||||||
|
|
||||||
func newEventStoreInvoker(next Invoker) *eventStoreInvoker {
|
// func newEventStoreInvoker(next Invoker) *eventStoreInvoker {
|
||||||
return &eventStoreInvoker{collector: &eventCollector{next: next}}
|
// return &eventStoreInvoker{collector: &eventCollector{next: next}}
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (i *eventStoreInvoker) Invoke(ctx context.Context, command Commander, opts *CommandOpts) (err error) {
|
// func (i *eventStoreInvoker) Invoke(ctx context.Context, command Commander, opts *CommandOpts) (err error) {
|
||||||
err = i.collector.Invoke(ctx, command, opts)
|
// err = i.collector.Invoke(ctx, command, opts)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
if len(i.collector.events) > 0 {
|
// if len(i.collector.events) > 0 {
|
||||||
err = eventstore.Publish(ctx, i.collector.events, opts.DB)
|
// err = eventstore.Publish(ctx, i.collector.events, opts.DB)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
return nil
|
// return nil
|
||||||
}
|
// }
|
||||||
|
|
||||||
// eventCollector collects events from all commands. The [eventStoreInvoker] pushes the collected events after all commands are executed.
|
// // eventCollector collects events from all commands. The [eventStoreInvoker] pushes the collected events after all commands are executed.
|
||||||
type eventCollector struct {
|
// type eventCollector struct {
|
||||||
next Invoker
|
// next Invoker
|
||||||
events []*eventstore.Event
|
// events []*eventstore.Event
|
||||||
}
|
// }
|
||||||
|
|
||||||
type eventer interface {
|
// type eventer interface {
|
||||||
Events() []*eventstore.Event
|
// Events() []*eventstore.Event
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (i *eventCollector) Invoke(ctx context.Context, command Commander, opts *CommandOpts) (err error) {
|
// func (i *eventCollector) Invoke(ctx context.Context, command Commander, opts *CommandOpts) (err error) {
|
||||||
if e, ok := command.(eventer); ok && len(e.Events()) > 0 {
|
// if e, ok := command.(eventer); ok && len(e.Events()) > 0 {
|
||||||
// we need to ensure all commands are executed in the same transaction
|
// // we need to ensure all commands are executed in the same transaction
|
||||||
close, err := opts.EnsureTx(ctx)
|
// close, err := opts.EnsureTx(ctx)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
defer func() { err = close(ctx, err) }()
|
// defer func() { err = close(ctx, err) }()
|
||||||
|
|
||||||
i.events = append(i.events, e.Events()...)
|
// i.events = append(i.events, e.Events()...)
|
||||||
}
|
// }
|
||||||
if i.next != nil {
|
// if i.next != nil {
|
||||||
return i.next.Invoke(ctx, command, opts)
|
// return i.next.Invoke(ctx, command, opts)
|
||||||
}
|
// }
|
||||||
return command.Execute(ctx, opts)
|
// return command.Execute(ctx, opts)
|
||||||
}
|
// }
|
||||||
|
|
||||||
// traceInvoker decorates each command with tracing.
|
// // traceInvoker decorates each command with tracing.
|
||||||
type traceInvoker struct {
|
// type traceInvoker struct {
|
||||||
next Invoker
|
// next Invoker
|
||||||
}
|
// }
|
||||||
|
|
||||||
func newTraceInvoker(next Invoker) *traceInvoker {
|
// func newTraceInvoker(next Invoker) *traceInvoker {
|
||||||
return &traceInvoker{next: next}
|
// return &traceInvoker{next: next}
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (i *traceInvoker) Invoke(ctx context.Context, command Commander, opts *CommandOpts) (err error) {
|
// func (i *traceInvoker) Invoke(ctx context.Context, command Commander, opts *CommandOpts) (err error) {
|
||||||
ctx, span := tracer.Start(ctx, fmt.Sprintf("%T", command))
|
// ctx, span := tracer.Start(ctx, fmt.Sprintf("%T", command))
|
||||||
defer func() {
|
// defer func() {
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
span.RecordError(err)
|
// span.RecordError(err)
|
||||||
}
|
// }
|
||||||
span.End()
|
// span.End()
|
||||||
}()
|
// }()
|
||||||
|
|
||||||
if i.next != nil {
|
// if i.next != nil {
|
||||||
return i.next.Invoke(ctx, command, opts)
|
// return i.next.Invoke(ctx, command, opts)
|
||||||
}
|
// }
|
||||||
return command.Execute(ctx, opts)
|
// return command.Execute(ctx, opts)
|
||||||
}
|
// }
|
||||||
|
|
||||||
// loggingInvoker decorates each command with logging.
|
// // 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.
|
// // 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 {
|
// type loggingInvoker struct {
|
||||||
next Invoker
|
// next Invoker
|
||||||
}
|
// }
|
||||||
|
|
||||||
func newLoggingInvoker(next Invoker) *loggingInvoker {
|
// func newLoggingInvoker(next Invoker) *loggingInvoker {
|
||||||
return &loggingInvoker{next: next}
|
// return &loggingInvoker{next: next}
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (i *loggingInvoker) Invoke(ctx context.Context, command Commander, opts *CommandOpts) (err error) {
|
// func (i *loggingInvoker) Invoke(ctx context.Context, command Commander, opts *CommandOpts) (err error) {
|
||||||
logger.InfoContext(ctx, "Invoking command", "command", command.String())
|
// logger.InfoContext(ctx, "Invoking command", "command", command.String())
|
||||||
|
|
||||||
if i.next != nil {
|
// if i.next != nil {
|
||||||
err = i.next.Invoke(ctx, command, opts)
|
// err = i.next.Invoke(ctx, command, opts)
|
||||||
} else {
|
// } else {
|
||||||
err = command.Execute(ctx, opts)
|
// err = command.Execute(ctx, opts)
|
||||||
}
|
// }
|
||||||
|
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
logger.ErrorContext(ctx, "Command invocation failed", "command", command.String(), "error", err)
|
// logger.ErrorContext(ctx, "Command invocation failed", "command", command.String(), "error", err)
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
logger.InfoContext(ctx, "Command invocation succeeded", "command", command.String())
|
// logger.InfoContext(ctx, "Command invocation succeeded", "command", command.String())
|
||||||
return nil
|
// return nil
|
||||||
}
|
// }
|
||||||
|
|
||||||
type noopInvoker struct {
|
// type noopInvoker struct {
|
||||||
next Invoker
|
// next Invoker
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (i *noopInvoker) Invoke(ctx context.Context, command Commander, opts *CommandOpts) error {
|
// func (i *noopInvoker) Invoke(ctx context.Context, command Commander, opts *CommandOpts) error {
|
||||||
if i.next != nil {
|
// if i.next != nil {
|
||||||
return i.next.Invoke(ctx, command, opts)
|
// return i.next.Invoke(ctx, command, opts)
|
||||||
}
|
// }
|
||||||
return command.Execute(ctx, opts)
|
// return command.Execute(ctx, opts)
|
||||||
}
|
// }
|
||||||
|
|
||||||
// cacheInvoker could be used in the future to do the caching.
|
// // cacheInvoker could be used in the future to do the caching.
|
||||||
// My goal would be to have two interfaces:
|
// // My goal would be to have two interfaces:
|
||||||
// - cacheSetter: which caches an object
|
// // - cacheSetter: which caches an object
|
||||||
// - cacheGetter: which gets an object from the cache, this should also skip the command execution
|
// // - cacheGetter: which gets an object from the cache, this should also skip the command execution
|
||||||
type cacheInvoker struct {
|
// type cacheInvoker struct {
|
||||||
next Invoker
|
// next Invoker
|
||||||
}
|
// }
|
||||||
|
|
||||||
type cacher interface {
|
// type cacher interface {
|
||||||
Cache(opts *CommandOpts)
|
// Cache(opts *CommandOpts)
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (i *cacheInvoker) Invoke(ctx context.Context, command Commander, opts *CommandOpts) (err error) {
|
// func (i *cacheInvoker) Invoke(ctx context.Context, command Commander, opts *CommandOpts) (err error) {
|
||||||
if c, ok := command.(cacher); ok {
|
// if c, ok := command.(cacher); ok {
|
||||||
c.Cache(opts)
|
// c.Cache(opts)
|
||||||
}
|
// }
|
||||||
if i.next != nil {
|
// if i.next != nil {
|
||||||
err = i.next.Invoke(ctx, command, opts)
|
// err = i.next.Invoke(ctx, command, opts)
|
||||||
} else {
|
// } else {
|
||||||
err = command.Execute(ctx, opts)
|
// err = command.Execute(ctx, opts)
|
||||||
}
|
// }
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
|
@@ -1,137 +1,137 @@
|
|||||||
package domain
|
package domain
|
||||||
|
|
||||||
import (
|
// import (
|
||||||
"context"
|
// "context"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/backend/v3/storage/eventstore"
|
// "github.com/zitadel/zitadel/backend/v3/storage/eventstore"
|
||||||
)
|
// )
|
||||||
|
|
||||||
// AddOrgCommand adds a new organization.
|
// // AddOrgCommand adds a new organization.
|
||||||
// I'm unsure if we should add the Admins here or if this should be a separate command.
|
// // I'm unsure if we should add the Admins here or if this should be a separate command.
|
||||||
type AddOrgCommand struct {
|
// type AddOrgCommand struct {
|
||||||
ID string `json:"id"`
|
// ID string `json:"id"`
|
||||||
Name string `json:"name"`
|
// Name string `json:"name"`
|
||||||
Admins []*AddMemberCommand `json:"admins"`
|
// Admins []*AddMemberCommand `json:"admins"`
|
||||||
}
|
// }
|
||||||
|
|
||||||
func NewAddOrgCommand(name string, admins ...*AddMemberCommand) *AddOrgCommand {
|
// func NewAddOrgCommand(name string, admins ...*AddMemberCommand) *AddOrgCommand {
|
||||||
return &AddOrgCommand{
|
// return &AddOrgCommand{
|
||||||
Name: name,
|
// Name: name,
|
||||||
Admins: admins,
|
// Admins: admins,
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// String implements [Commander].
|
// // String implements [Commander].
|
||||||
func (cmd *AddOrgCommand) String() string {
|
// func (cmd *AddOrgCommand) String() string {
|
||||||
return "AddOrgCommand"
|
// return "AddOrgCommand"
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Execute implements Commander.
|
// // Execute implements Commander.
|
||||||
func (cmd *AddOrgCommand) Execute(ctx context.Context, opts *CommandOpts) (err error) {
|
// func (cmd *AddOrgCommand) Execute(ctx context.Context, opts *CommandOpts) (err error) {
|
||||||
if len(cmd.Admins) == 0 {
|
// if len(cmd.Admins) == 0 {
|
||||||
return ErrNoAdminSpecified
|
// return ErrNoAdminSpecified
|
||||||
}
|
// }
|
||||||
if err = cmd.ensureID(); err != nil {
|
// if err = cmd.ensureID(); err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
|
|
||||||
close, err := opts.EnsureTx(ctx)
|
// close, err := opts.EnsureTx(ctx)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
defer func() { err = close(ctx, err) }()
|
// defer func() { err = close(ctx, err) }()
|
||||||
err = orgRepo(opts.DB).Create(ctx, &Org{
|
// err = orgRepo(opts.DB).Create(ctx, &Org{
|
||||||
ID: cmd.ID,
|
// ID: cmd.ID,
|
||||||
Name: cmd.Name,
|
// Name: cmd.Name,
|
||||||
})
|
// })
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
|
|
||||||
for _, admin := range cmd.Admins {
|
// for _, admin := range cmd.Admins {
|
||||||
admin.orgID = cmd.ID
|
// admin.orgID = cmd.ID
|
||||||
if err = opts.Invoke(ctx, admin); err != nil {
|
// if err = opts.Invoke(ctx, admin); err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
orgCache.Set(ctx, &Org{
|
// orgCache.Set(ctx, &Org{
|
||||||
ID: cmd.ID,
|
// ID: cmd.ID,
|
||||||
Name: cmd.Name,
|
// Name: cmd.Name,
|
||||||
})
|
// })
|
||||||
|
|
||||||
return nil
|
// return nil
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Events implements [eventer].
|
// // Events implements [eventer].
|
||||||
func (cmd *AddOrgCommand) Events() []*eventstore.Event {
|
// func (cmd *AddOrgCommand) Events() []*eventstore.Event {
|
||||||
return []*eventstore.Event{
|
// return []*eventstore.Event{
|
||||||
{
|
// {
|
||||||
AggregateType: "org",
|
// AggregateType: "org",
|
||||||
AggregateID: cmd.ID,
|
// AggregateID: cmd.ID,
|
||||||
Type: "org.added",
|
// Type: "org.added",
|
||||||
Payload: cmd,
|
// Payload: cmd,
|
||||||
},
|
// },
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
var (
|
// var (
|
||||||
_ Commander = (*AddOrgCommand)(nil)
|
// _ Commander = (*AddOrgCommand)(nil)
|
||||||
_ eventer = (*AddOrgCommand)(nil)
|
// _ eventer = (*AddOrgCommand)(nil)
|
||||||
)
|
// )
|
||||||
|
|
||||||
func (cmd *AddOrgCommand) ensureID() (err error) {
|
// func (cmd *AddOrgCommand) ensureID() (err error) {
|
||||||
if cmd.ID != "" {
|
// if cmd.ID != "" {
|
||||||
return nil
|
// return nil
|
||||||
}
|
// }
|
||||||
cmd.ID, err = generateID()
|
// cmd.ID, err = generateID()
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
|
|
||||||
// AddMemberCommand adds a new member to an organization.
|
// // 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.
|
// // I'm not sure if we should make it more generic to also use it for instances.
|
||||||
type AddMemberCommand struct {
|
// type AddMemberCommand struct {
|
||||||
orgID string
|
// orgID string
|
||||||
UserID string `json:"userId"`
|
// UserID string `json:"userId"`
|
||||||
Roles []string `json:"roles"`
|
// Roles []string `json:"roles"`
|
||||||
}
|
// }
|
||||||
|
|
||||||
func NewAddMemberCommand(userID string, roles ...string) *AddMemberCommand {
|
// func NewAddMemberCommand(userID string, roles ...string) *AddMemberCommand {
|
||||||
return &AddMemberCommand{
|
// return &AddMemberCommand{
|
||||||
UserID: userID,
|
// UserID: userID,
|
||||||
Roles: roles,
|
// Roles: roles,
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// String implements [Commander].
|
// // String implements [Commander].
|
||||||
func (cmd *AddMemberCommand) String() string {
|
// func (cmd *AddMemberCommand) String() string {
|
||||||
return "AddMemberCommand"
|
// return "AddMemberCommand"
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Execute implements Commander.
|
// // Execute implements Commander.
|
||||||
func (a *AddMemberCommand) Execute(ctx context.Context, opts *CommandOpts) (err error) {
|
// func (a *AddMemberCommand) Execute(ctx context.Context, opts *CommandOpts) (err error) {
|
||||||
close, err := opts.EnsureTx(ctx)
|
// close, err := opts.EnsureTx(ctx)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
defer func() { err = close(ctx, err) }()
|
// defer func() { err = close(ctx, err) }()
|
||||||
|
|
||||||
return orgRepo(opts.DB).Member().AddMember(ctx, a.orgID, a.UserID, a.Roles)
|
// return orgRepo(opts.DB).Member().AddMember(ctx, a.orgID, a.UserID, a.Roles)
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Events implements [eventer].
|
// // Events implements [eventer].
|
||||||
func (a *AddMemberCommand) Events() []*eventstore.Event {
|
// func (a *AddMemberCommand) Events() []*eventstore.Event {
|
||||||
return []*eventstore.Event{
|
// return []*eventstore.Event{
|
||||||
{
|
// {
|
||||||
AggregateType: "org",
|
// AggregateType: "org",
|
||||||
AggregateID: a.UserID,
|
// AggregateID: a.UserID,
|
||||||
Type: "member.added",
|
// Type: "member.added",
|
||||||
Payload: a,
|
// Payload: a,
|
||||||
},
|
// },
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
var (
|
// var (
|
||||||
_ Commander = (*AddMemberCommand)(nil)
|
// _ Commander = (*AddMemberCommand)(nil)
|
||||||
_ eventer = (*AddMemberCommand)(nil)
|
// _ eventer = (*AddMemberCommand)(nil)
|
||||||
)
|
// )
|
||||||
|
@@ -1,74 +1,74 @@
|
|||||||
package domain
|
package domain
|
||||||
|
|
||||||
import (
|
// import (
|
||||||
"context"
|
// "context"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/backend/v3/storage/eventstore"
|
// "github.com/zitadel/zitadel/backend/v3/storage/eventstore"
|
||||||
)
|
// )
|
||||||
|
|
||||||
// SetEmailCommand sets the email address of a user.
|
// // SetEmailCommand sets the email address of a user.
|
||||||
// If allows verification as a sub command.
|
// // If allows verification as a sub command.
|
||||||
// The verification command is executed after the email address is set.
|
// // 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.
|
// // The verification command is executed in the same transaction as the email address update.
|
||||||
type SetEmailCommand struct {
|
// type SetEmailCommand struct {
|
||||||
UserID string `json:"userId"`
|
// UserID string `json:"userId"`
|
||||||
Email string `json:"email"`
|
// Email string `json:"email"`
|
||||||
verification Commander
|
// verification Commander
|
||||||
}
|
// }
|
||||||
|
|
||||||
var (
|
// var (
|
||||||
_ Commander = (*SetEmailCommand)(nil)
|
// _ Commander = (*SetEmailCommand)(nil)
|
||||||
_ eventer = (*SetEmailCommand)(nil)
|
// _ eventer = (*SetEmailCommand)(nil)
|
||||||
_ CreateHumanOpt = (*SetEmailCommand)(nil)
|
// _ CreateHumanOpt = (*SetEmailCommand)(nil)
|
||||||
)
|
// )
|
||||||
|
|
||||||
type SetEmailOpt interface {
|
// type SetEmailOpt interface {
|
||||||
applyOnSetEmail(*SetEmailCommand)
|
// applyOnSetEmail(*SetEmailCommand)
|
||||||
}
|
// }
|
||||||
|
|
||||||
func NewSetEmailCommand(userID, email string, verificationType SetEmailOpt) *SetEmailCommand {
|
// func NewSetEmailCommand(userID, email string, verificationType SetEmailOpt) *SetEmailCommand {
|
||||||
cmd := &SetEmailCommand{
|
// cmd := &SetEmailCommand{
|
||||||
UserID: userID,
|
// UserID: userID,
|
||||||
Email: email,
|
// Email: email,
|
||||||
}
|
// }
|
||||||
verificationType.applyOnSetEmail(cmd)
|
// verificationType.applyOnSetEmail(cmd)
|
||||||
return cmd
|
// return cmd
|
||||||
}
|
// }
|
||||||
|
|
||||||
// String implements [Commander].
|
// // String implements [Commander].
|
||||||
func (cmd *SetEmailCommand) String() string {
|
// func (cmd *SetEmailCommand) String() string {
|
||||||
return "SetEmailCommand"
|
// return "SetEmailCommand"
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (cmd *SetEmailCommand) Execute(ctx context.Context, opts *CommandOpts) error {
|
// func (cmd *SetEmailCommand) Execute(ctx context.Context, opts *CommandOpts) error {
|
||||||
close, err := opts.EnsureTx(ctx)
|
// close, err := opts.EnsureTx(ctx)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
defer func() { err = close(ctx, err) }()
|
// defer func() { err = close(ctx, err) }()
|
||||||
// userStatement(opts.DB).Human().ByID(cmd.UserID).SetEmail(ctx, cmd.Email)
|
// // userStatement(opts.DB).Human().ByID(cmd.UserID).SetEmail(ctx, cmd.Email)
|
||||||
repo := userRepo(opts.DB).Human()
|
// repo := userRepo(opts.DB).Human()
|
||||||
err = repo.Update(ctx, repo.IDCondition(cmd.UserID), repo.SetEmailAddress(cmd.Email))
|
// err = repo.Update(ctx, repo.IDCondition(cmd.UserID), repo.SetEmailAddress(cmd.Email))
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
|
|
||||||
return opts.Invoke(ctx, cmd.verification)
|
// return opts.Invoke(ctx, cmd.verification)
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Events implements [eventer].
|
// // Events implements [eventer].
|
||||||
func (cmd *SetEmailCommand) Events() []*eventstore.Event {
|
// func (cmd *SetEmailCommand) Events() []*eventstore.Event {
|
||||||
return []*eventstore.Event{
|
// return []*eventstore.Event{
|
||||||
{
|
// {
|
||||||
AggregateType: "user",
|
// AggregateType: "user",
|
||||||
AggregateID: cmd.UserID,
|
// AggregateID: cmd.UserID,
|
||||||
Type: "user.email.set",
|
// Type: "user.email.set",
|
||||||
Payload: cmd,
|
// Payload: cmd,
|
||||||
},
|
// },
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// applyOnCreateHuman implements [CreateHumanOpt].
|
// // applyOnCreateHuman implements [CreateHumanOpt].
|
||||||
func (cmd *SetEmailCommand) applyOnCreateHuman(createUserCmd *CreateUserCommand) {
|
// func (cmd *SetEmailCommand) applyOnCreateHuman(createUserCmd *CreateUserCommand) {
|
||||||
createUserCmd.email = cmd
|
// createUserCmd.email = cmd
|
||||||
}
|
// }
|
||||||
|
@@ -20,7 +20,7 @@ func (a *and) Write(builder *StatementBuilder) {
|
|||||||
if i > 0 {
|
if i > 0 {
|
||||||
builder.WriteString(" AND ")
|
builder.WriteString(" AND ")
|
||||||
}
|
}
|
||||||
condition.(Condition).Write(builder)
|
condition.Write(builder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ func (o *or) Write(builder *StatementBuilder) {
|
|||||||
if i > 0 {
|
if i > 0 {
|
||||||
builder.WriteString(" OR ")
|
builder.WriteString(" OR ")
|
||||||
}
|
}
|
||||||
condition.(Condition).Write(builder)
|
condition.Write(builder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +85,7 @@ func (i *isNotNull) Write(builder *StatementBuilder) {
|
|||||||
|
|
||||||
// IsNotNull creates a condition that checks if a column is NOT NULL.
|
// IsNotNull creates a condition that checks if a column is NOT NULL.
|
||||||
func IsNotNull(column Column) *isNotNull {
|
func IsNotNull(column Column) *isNotNull {
|
||||||
return &isNotNull{column: column.(Column)}
|
return &isNotNull{column: column}
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Condition = (*isNotNull)(nil)
|
var _ Condition = (*isNotNull)(nil)
|
||||||
|
@@ -34,7 +34,7 @@ type Config struct {
|
|||||||
// // The value will be taken as is. Multiple options are space separated.
|
// // The value will be taken as is. Multiple options are space separated.
|
||||||
// Options string
|
// Options string
|
||||||
|
|
||||||
configuredFields []string
|
// configuredFields []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect implements [database.Connector].
|
// Connect implements [database.Connector].
|
||||||
|
@@ -2,6 +2,7 @@ package postgres
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
|
|
||||||
"github.com/jackc/pgx/v5"
|
"github.com/jackc/pgx/v5"
|
||||||
|
|
||||||
@@ -25,7 +26,10 @@ func (tx *pgxTx) Rollback(ctx context.Context) error {
|
|||||||
// End implements [database.Transaction].
|
// End implements [database.Transaction].
|
||||||
func (tx *pgxTx) End(ctx context.Context, err error) error {
|
func (tx *pgxTx) End(ctx context.Context, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tx.Rollback(ctx)
|
rollbackErr := tx.Rollback(ctx)
|
||||||
|
if rollbackErr != nil {
|
||||||
|
err = errors.Join(err, rollbackErr)
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return tx.Commit(ctx)
|
return tx.Commit(ctx)
|
||||||
|
@@ -46,7 +46,7 @@ func (opts *QueryOpts) WriteOrderBy(builder *StatementBuilder) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
builder.WriteString(" ORDER BY ")
|
builder.WriteString(" ORDER BY ")
|
||||||
Columns(opts.OrderBy).Write(builder)
|
opts.OrderBy.Write(builder)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (opts *QueryOpts) WriteLimit(builder *StatementBuilder) {
|
func (opts *QueryOpts) WriteLimit(builder *StatementBuilder) {
|
||||||
|
@@ -31,7 +31,6 @@ func (u *userHuman) GetEmail(ctx context.Context, condition database.Condition)
|
|||||||
&email.Address,
|
&email.Address,
|
||||||
&email.VerifiedAt,
|
&email.VerifiedAt,
|
||||||
)
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -189,18 +188,18 @@ func (h userHuman) PhoneVerifiedAtColumn() database.Column {
|
|||||||
return database.NewColumn("phone_verified_at")
|
return database.NewColumn("phone_verified_at")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h userHuman) columns() database.Columns {
|
// func (h userHuman) columns() database.Columns {
|
||||||
return append(h.user.columns(),
|
// return append(h.user.columns(),
|
||||||
h.FirstNameColumn(),
|
// h.FirstNameColumn(),
|
||||||
h.LastNameColumn(),
|
// h.LastNameColumn(),
|
||||||
h.EmailAddressColumn(),
|
// h.EmailAddressColumn(),
|
||||||
h.EmailVerifiedAtColumn(),
|
// h.EmailVerifiedAtColumn(),
|
||||||
h.PhoneNumberColumn(),
|
// h.PhoneNumberColumn(),
|
||||||
h.PhoneVerifiedAtColumn(),
|
// h.PhoneVerifiedAtColumn(),
|
||||||
)
|
// )
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (h userHuman) writeReturning(builder *database.StatementBuilder) {
|
// func (h userHuman) writeReturning(builder *database.StatementBuilder) {
|
||||||
builder.WriteString(" RETURNING ")
|
// builder.WriteString(" RETURNING ")
|
||||||
h.columns().Write(builder)
|
// h.columns().Write(builder)
|
||||||
}
|
// }
|
||||||
|
@@ -1,76 +1,76 @@
|
|||||||
package repository_test
|
package repository_test
|
||||||
|
|
||||||
import (
|
// import (
|
||||||
"context"
|
// "context"
|
||||||
"testing"
|
// "testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
// "github.com/stretchr/testify/assert"
|
||||||
"github.com/zitadel/zitadel/backend/v3/storage/database"
|
// "github.com/zitadel/zitadel/backend/v3/storage/database"
|
||||||
"github.com/zitadel/zitadel/backend/v3/storage/database/dbmock"
|
// "github.com/zitadel/zitadel/backend/v3/storage/database/dbmock"
|
||||||
"github.com/zitadel/zitadel/backend/v3/storage/database/repository"
|
// "github.com/zitadel/zitadel/backend/v3/storage/database/repository"
|
||||||
"go.uber.org/mock/gomock"
|
// "go.uber.org/mock/gomock"
|
||||||
)
|
// )
|
||||||
|
|
||||||
func TestQueryUser(t *testing.T) {
|
// func TestQueryUser(t *testing.T) {
|
||||||
t.Skip("tests are meant as examples and are not real tests")
|
// t.Skip("tests are meant as examples and are not real tests")
|
||||||
t.Run("User filters", func(t *testing.T) {
|
// t.Run("User filters", func(t *testing.T) {
|
||||||
client := dbmock.NewMockClient(gomock.NewController(t))
|
// client := dbmock.NewMockClient(gomock.NewController(t))
|
||||||
|
|
||||||
user := repository.UserRepository(client)
|
// user := repository.UserRepository(client)
|
||||||
u, err := user.Get(context.Background(),
|
// u, err := user.Get(context.Background(),
|
||||||
database.WithCondition(
|
// database.WithCondition(
|
||||||
database.And(
|
// database.And(
|
||||||
database.Or(
|
// database.Or(
|
||||||
user.IDCondition("test"),
|
// user.IDCondition("test"),
|
||||||
user.IDCondition("2"),
|
// user.IDCondition("2"),
|
||||||
),
|
// ),
|
||||||
user.UsernameCondition(database.TextOperationStartsWithIgnoreCase, "test"),
|
// user.UsernameCondition(database.TextOperationStartsWithIgnoreCase, "test"),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
database.WithOrderBy(user.CreatedAtColumn()),
|
// database.WithOrderBy(user.CreatedAtColumn()),
|
||||||
)
|
// )
|
||||||
|
|
||||||
assert.NoError(t, err)
|
// assert.NoError(t, err)
|
||||||
assert.NotNil(t, u)
|
// assert.NotNil(t, u)
|
||||||
})
|
// })
|
||||||
|
|
||||||
t.Run("machine and human filters", func(t *testing.T) {
|
// t.Run("machine and human filters", func(t *testing.T) {
|
||||||
client := dbmock.NewMockClient(gomock.NewController(t))
|
// client := dbmock.NewMockClient(gomock.NewController(t))
|
||||||
|
|
||||||
user := repository.UserRepository(client)
|
// user := repository.UserRepository(client)
|
||||||
machine := user.Machine()
|
// machine := user.Machine()
|
||||||
human := user.Human()
|
// human := user.Human()
|
||||||
email, err := human.GetEmail(context.Background(), database.And(
|
// email, err := human.GetEmail(context.Background(), database.And(
|
||||||
user.UsernameCondition(database.TextOperationStartsWithIgnoreCase, "test"),
|
// user.UsernameCondition(database.TextOperationStartsWithIgnoreCase, "test"),
|
||||||
database.Or(
|
// database.Or(
|
||||||
machine.DescriptionCondition(database.TextOperationStartsWithIgnoreCase, "test"),
|
// machine.DescriptionCondition(database.TextOperationStartsWithIgnoreCase, "test"),
|
||||||
human.EmailVerifiedCondition(true),
|
// human.EmailVerifiedCondition(true),
|
||||||
database.IsNotNull(machine.DescriptionColumn()),
|
// database.IsNotNull(machine.DescriptionColumn()),
|
||||||
),
|
// ),
|
||||||
))
|
// ))
|
||||||
|
|
||||||
assert.NoError(t, err)
|
// assert.NoError(t, err)
|
||||||
assert.NotNil(t, email)
|
// assert.NotNil(t, email)
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
|
|
||||||
type dbInstruction string
|
// type dbInstruction string
|
||||||
|
|
||||||
func TestArg(t *testing.T) {
|
// func TestArg(t *testing.T) {
|
||||||
var bla any = "asdf"
|
// var bla any = "asdf"
|
||||||
instr, ok := bla.(dbInstruction)
|
// instr, ok := bla.(dbInstruction)
|
||||||
assert.False(t, ok)
|
// assert.False(t, ok)
|
||||||
assert.Empty(t, instr)
|
// assert.Empty(t, instr)
|
||||||
bla = dbInstruction("asdf")
|
// bla = dbInstruction("asdf")
|
||||||
instr, ok = bla.(dbInstruction)
|
// instr, ok = bla.(dbInstruction)
|
||||||
assert.True(t, ok)
|
// assert.True(t, ok)
|
||||||
assert.Equal(t, instr, dbInstruction("asdf"))
|
// assert.Equal(t, instr, dbInstruction("asdf"))
|
||||||
}
|
// }
|
||||||
|
|
||||||
func TestWriteUser(t *testing.T) {
|
// func TestWriteUser(t *testing.T) {
|
||||||
t.Skip("tests are meant as examples and are not real tests")
|
// t.Skip("tests are meant as examples and are not real tests")
|
||||||
t.Run("update user", func(t *testing.T) {
|
// t.Run("update user", func(t *testing.T) {
|
||||||
user := repository.UserRepository(nil)
|
// user := repository.UserRepository(nil)
|
||||||
user.Human().Update(context.Background(), user.IDCondition("test"), user.SetUsername("test"))
|
// user.Human().Update(context.Background(), user.IDCondition("test"), user.SetUsername("test"))
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
|
@@ -2,6 +2,7 @@ package command
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/crypto"
|
"github.com/zitadel/zitadel/internal/crypto"
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/eventstore"
|
"github.com/zitadel/zitadel/internal/eventstore"
|
||||||
|
Reference in New Issue
Block a user