mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 20:17:32 +00:00
show tim
This commit is contained in:
@@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/backend/repository"
|
"github.com/zitadel/zitadel/backend/repository"
|
||||||
"github.com/zitadel/zitadel/backend/repository/orchestrate"
|
|
||||||
"github.com/zitadel/zitadel/backend/storage/database"
|
"github.com/zitadel/zitadel/backend/storage/database"
|
||||||
"github.com/zitadel/zitadel/backend/telemetry/logging"
|
"github.com/zitadel/zitadel/backend/telemetry/logging"
|
||||||
"github.com/zitadel/zitadel/backend/telemetry/tracing"
|
"github.com/zitadel/zitadel/backend/telemetry/tracing"
|
||||||
@@ -13,11 +12,11 @@ import (
|
|||||||
type Instance struct {
|
type Instance struct {
|
||||||
db database.Pool
|
db database.Pool
|
||||||
|
|
||||||
instance instanceOrchestrator
|
instance instanceRepository
|
||||||
user userOrchestrator
|
user userRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
type instanceOrchestrator interface {
|
type instanceRepository interface {
|
||||||
ByID(ctx context.Context, querier database.Querier, id string) (*repository.Instance, error)
|
ByID(ctx context.Context, querier database.Querier, id string) (*repository.Instance, error)
|
||||||
ByDomain(ctx context.Context, querier database.Querier, domain string) (*repository.Instance, error)
|
ByDomain(ctx context.Context, querier database.Querier, domain string) (*repository.Instance, error)
|
||||||
Create(ctx context.Context, tx database.Transaction, instance *repository.Instance) (*repository.Instance, error)
|
Create(ctx context.Context, tx database.Transaction, instance *repository.Instance) (*repository.Instance, error)
|
||||||
@@ -26,8 +25,14 @@ type instanceOrchestrator interface {
|
|||||||
func NewInstance(db database.Pool, tracer *tracing.Tracer, logger *logging.Logger) *Instance {
|
func NewInstance(db database.Pool, tracer *tracing.Tracer, logger *logging.Logger) *Instance {
|
||||||
b := &Instance{
|
b := &Instance{
|
||||||
db: db,
|
db: db,
|
||||||
instance: orchestrate.Instance(),
|
instance: repository.NewInstance(
|
||||||
user: orchestrate.User(),
|
repository.WithLogger[repository.InstanceOptions](logger),
|
||||||
|
repository.WithTracer[repository.InstanceOptions](tracer),
|
||||||
|
),
|
||||||
|
user: repository.NewUser(
|
||||||
|
repository.WithLogger[repository.UserOptions](logger),
|
||||||
|
repository.WithTracer[repository.UserOptions](tracer),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
return b
|
return b
|
||||||
@@ -59,5 +64,6 @@ func (b *Instance) SetUp(ctx context.Context, request *SetUpInstance) (err error
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = b.user.Create(ctx, tx, request.User)
|
_, err = b.user.Create(ctx, tx, request.User)
|
||||||
|
b.authorizations.authorizeusers
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@@ -7,7 +7,7 @@ import (
|
|||||||
"github.com/zitadel/zitadel/backend/storage/database"
|
"github.com/zitadel/zitadel/backend/storage/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
type userOrchestrator interface {
|
type userRepository interface {
|
||||||
Create(ctx context.Context, tx database.Transaction, user *repository.User) (*repository.User, error)
|
Create(ctx context.Context, tx database.Transaction, user *repository.User) (*repository.User, error)
|
||||||
ByID(ctx context.Context, querier database.Querier, id string) (*repository.User, error)
|
ByID(ctx context.Context, querier database.Querier, id string) (*repository.User, error)
|
||||||
}
|
}
|
||||||
|
@@ -1,34 +0,0 @@
|
|||||||
package cached
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/backend/repository"
|
|
||||||
"github.com/zitadel/zitadel/backend/storage/cache"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Instance struct {
|
|
||||||
cache.Cache[repository.InstanceIndex, string, *repository.Instance]
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewInstance(c cache.Cache[repository.InstanceIndex, string, *repository.Instance]) *Instance {
|
|
||||||
return &Instance{c}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *Instance) ByID(ctx context.Context, id string) *repository.Instance {
|
|
||||||
log.Println("cached.instance.byID")
|
|
||||||
instance, _ := i.Cache.Get(ctx, repository.InstanceByID, id)
|
|
||||||
return instance
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *Instance) ByDomain(ctx context.Context, domain string) *repository.Instance {
|
|
||||||
log.Println("cached.instance.byDomain")
|
|
||||||
instance, _ := i.Cache.Get(ctx, repository.InstanceByDomain, domain)
|
|
||||||
return instance
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *Instance) Set(ctx context.Context, instance *repository.Instance) {
|
|
||||||
log.Println("cached.instance.set")
|
|
||||||
i.Cache.Set(ctx, instance)
|
|
||||||
}
|
|
@@ -1,28 +0,0 @@
|
|||||||
package cached
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/backend/repository"
|
|
||||||
"github.com/zitadel/zitadel/backend/storage/cache"
|
|
||||||
)
|
|
||||||
|
|
||||||
type User struct {
|
|
||||||
cache.Cache[repository.UserIndex, string, *repository.User]
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewUser(c cache.Cache[repository.UserIndex, string, *repository.User]) *User {
|
|
||||||
return &User{c}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *User) ByID(ctx context.Context, id string) *repository.User {
|
|
||||||
log.Println("cached.user.byid")
|
|
||||||
user, _ := i.Cache.Get(ctx, repository.UserByIDIndex, id)
|
|
||||||
return user
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *User) Set(ctx context.Context, user *repository.User) {
|
|
||||||
log.Println("cached.user.set")
|
|
||||||
i.Cache.Set(ctx, user)
|
|
||||||
}
|
|
@@ -1,17 +0,0 @@
|
|||||||
package event
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/backend/repository"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *store) CreateInstance(ctx context.Context, instance *repository.Instance) (*repository.Instance, error) {
|
|
||||||
log.Println("event.instance.create")
|
|
||||||
err := s.es.Push(ctx, instance)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return instance, nil
|
|
||||||
}
|
|
@@ -1,16 +0,0 @@
|
|||||||
package event
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/zitadel/zitadel/backend/storage/database"
|
|
||||||
"github.com/zitadel/zitadel/backend/storage/eventstore"
|
|
||||||
)
|
|
||||||
|
|
||||||
type store struct {
|
|
||||||
es *eventstore.Eventstore
|
|
||||||
}
|
|
||||||
|
|
||||||
func Store(client database.Executor) *store {
|
|
||||||
return &store{
|
|
||||||
es: eventstore.New(client),
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,17 +0,0 @@
|
|||||||
package event
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/backend/repository"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *store) CreateUser(ctx context.Context, user *repository.User) (*repository.User, error) {
|
|
||||||
log.Println("event.user.create")
|
|
||||||
err := s.es.Push(ctx, user)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return user, nil
|
|
||||||
}
|
|
@@ -1,37 +1,115 @@
|
|||||||
package repository
|
package repository
|
||||||
|
|
||||||
import "github.com/zitadel/zitadel/backend/storage/cache"
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/backend/handler"
|
||||||
|
"github.com/zitadel/zitadel/backend/storage/database"
|
||||||
|
"github.com/zitadel/zitadel/backend/telemetry/logging"
|
||||||
|
"github.com/zitadel/zitadel/backend/telemetry/tracing"
|
||||||
|
)
|
||||||
|
|
||||||
type Instance struct {
|
type Instance struct {
|
||||||
ID string
|
ID string
|
||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
type InstanceIndex uint8
|
type InstanceOptions struct {
|
||||||
|
cache *InstanceCache
|
||||||
var InstanceIndices = []InstanceIndex{
|
|
||||||
InstanceByID,
|
|
||||||
InstanceByDomain,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
type instance struct {
|
||||||
InstanceByID InstanceIndex = iota
|
options[InstanceOptions]
|
||||||
InstanceByDomain
|
*InstanceOptions
|
||||||
)
|
|
||||||
|
|
||||||
var _ cache.Entry[InstanceIndex, string] = (*Instance)(nil)
|
|
||||||
|
|
||||||
// Keys implements [cache.Entry].
|
|
||||||
func (i *Instance) Keys(index InstanceIndex) (key []string) {
|
|
||||||
switch index {
|
|
||||||
case InstanceByID:
|
|
||||||
return []string{i.ID}
|
|
||||||
case InstanceByDomain:
|
|
||||||
return []string{i.Name}
|
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
|
func NewInstance(opts ...Option[InstanceOptions]) *instance {
|
||||||
|
i := new(instance)
|
||||||
|
i.InstanceOptions = &i.options.custom
|
||||||
|
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt.apply(&i.options)
|
||||||
|
}
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithInstanceCache(c *InstanceCache) Option[InstanceOptions] {
|
||||||
|
return func(opts *options[InstanceOptions]) {
|
||||||
|
opts.custom.cache = c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *instance) Create(ctx context.Context, tx database.Transaction, instance *Instance) (*Instance, error) {
|
||||||
|
return tracing.Wrap(i.tracer, "instance.SetUp",
|
||||||
|
handler.Chains(
|
||||||
|
handler.Decorates(
|
||||||
|
execute(tx).CreateInstance,
|
||||||
|
tracing.Decorate[*Instance, *Instance](i.tracer, tracing.WithSpanName("instance.sql.SetUp")),
|
||||||
|
logging.Decorate[*Instance, *Instance](i.logger, "instance.sql.SetUp"),
|
||||||
|
),
|
||||||
|
handler.Decorates(
|
||||||
|
events(tx).CreateInstance,
|
||||||
|
tracing.Decorate[*Instance, *Instance](i.tracer, tracing.WithSpanName("instance.event.SetUp")),
|
||||||
|
logging.Decorate[*Instance, *Instance](i.logger, "instance.event.SetUp"),
|
||||||
|
),
|
||||||
|
handler.SkipReturnPreviousHandler(i.cache,
|
||||||
|
handler.Decorates(
|
||||||
|
handler.NoReturnToHandle(i.cache.Set),
|
||||||
|
tracing.Decorate[*Instance, *Instance](i.tracer, tracing.WithSpanName("instance.cache.SetUp")),
|
||||||
|
logging.Decorate[*Instance, *Instance](i.logger, "instance.cache.SetUp"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)(ctx, instance)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *instance) ByID(ctx context.Context, querier database.Querier, id string) (*Instance, error) {
|
||||||
|
return tracing.Wrap(i.tracer, "instance.byID",
|
||||||
|
handler.SkipNext(
|
||||||
|
handler.SkipNilHandler(i.cache,
|
||||||
|
handler.ResFuncToHandle(i.cache.ByID),
|
||||||
|
),
|
||||||
|
handler.Chain(
|
||||||
|
handler.Decorates(
|
||||||
|
query(querier).InstanceByID,
|
||||||
|
tracing.Decorate[string, *Instance](i.tracer, tracing.WithSpanName("instance.sql.ByID")),
|
||||||
|
logging.Decorate[string, *Instance](i.logger, "instance.sql.ByID"),
|
||||||
|
),
|
||||||
|
handler.SkipNilHandler(i.cache, handler.NoReturnToHandle(i.cache.Set)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)(ctx, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *instance) ByDomain(ctx context.Context, querier database.Querier, domain string) (*Instance, error) {
|
||||||
|
return tracing.Wrap(i.tracer, "instance.byDomain",
|
||||||
|
handler.SkipNext(
|
||||||
|
handler.SkipNilHandler(i.cache,
|
||||||
|
handler.ResFuncToHandle(i.cache.ByDomain),
|
||||||
|
),
|
||||||
|
handler.Chain(
|
||||||
|
handler.Decorate(
|
||||||
|
query(querier).InstanceByDomain,
|
||||||
|
tracing.Decorate[string, *Instance](i.tracer, tracing.WithSpanName("instance.sql.ByDomain")),
|
||||||
|
),
|
||||||
|
handler.SkipNilHandler(i.cache, handler.NoReturnToHandle(i.cache.Set)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)(ctx, domain)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ListRequest struct {
|
type ListRequest struct {
|
||||||
Limit uint16
|
Limit uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *instance) List(ctx context.Context, querier database.Querier, request *ListRequest) ([]*Instance, error) {
|
||||||
|
return tracing.Wrap(i.tracer, "instance.list",
|
||||||
|
handler.Chains(
|
||||||
|
handler.Decorates(
|
||||||
|
query(querier).ListInstances,
|
||||||
|
tracing.Decorate[*ListRequest, []*Instance](i.tracer, tracing.WithSpanName("instance.sql.List")),
|
||||||
|
logging.Decorate[*ListRequest, []*Instance](i.logger, "instance.sql.List"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)(ctx, request)
|
||||||
|
}
|
||||||
|
@@ -1,110 +0,0 @@
|
|||||||
package handler
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Handler is a function that handles the in.
|
|
||||||
type Handler[Out, In any] func(ctx context.Context, in Out) (out In, err error)
|
|
||||||
|
|
||||||
// Decorator is a function that decorates the handle function.
|
|
||||||
type Decorator[In, Out any] func(ctx context.Context, in In, handle Handler[In, Out]) (out Out, err error)
|
|
||||||
|
|
||||||
// Chain chains the handle function with the next handler.
|
|
||||||
// The next handler is called after the handle function.
|
|
||||||
func Chain[In, Out any](handle Handler[In, Out], next Handler[Out, Out]) Handler[In, Out] {
|
|
||||||
return func(ctx context.Context, in In) (out Out, err error) {
|
|
||||||
out, err = handle(ctx, in)
|
|
||||||
if err != nil {
|
|
||||||
return out, err
|
|
||||||
}
|
|
||||||
return next(ctx, out)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Chains[In, Out any](handle Handler[In, Out], nexts ...Handler[Out, Out]) Handler[In, Out] {
|
|
||||||
return func(ctx context.Context, in In) (out Out, err error) {
|
|
||||||
for _, next := range nexts {
|
|
||||||
handle = Chain(handle, next)
|
|
||||||
}
|
|
||||||
return handle(ctx, in)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decorate decorates the handle function with the decorate function.
|
|
||||||
// The decorate function is called before the handle function.
|
|
||||||
func Decorate[In, Out any](handle Handler[In, Out], decorate Decorator[In, Out]) Handler[In, Out] {
|
|
||||||
return func(ctx context.Context, in In) (out Out, err error) {
|
|
||||||
return decorate(ctx, in, handle)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decorates decorates the handle function with the decorate functions.
|
|
||||||
// The decorates function is called before the handle function.
|
|
||||||
func Decorates[In, Out any](handle Handler[In, Out], decorates ...Decorator[In, Out]) Handler[In, Out] {
|
|
||||||
return func(ctx context.Context, in In) (out Out, err error) {
|
|
||||||
for i := len(decorates) - 1; i >= 0; i-- {
|
|
||||||
handle = Decorate(handle, decorates[i])
|
|
||||||
}
|
|
||||||
return handle(ctx, in)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SkipNext skips the next handler if the handle function returns a non-nil response.
|
|
||||||
func SkipNext[In, Out any](handle Handler[In, Out], next Handler[In, Out]) Handler[In, Out] {
|
|
||||||
return func(ctx context.Context, in In) (out Out, err error) {
|
|
||||||
var empty Out
|
|
||||||
out, err = handle(ctx, in)
|
|
||||||
// TODO: does this work?
|
|
||||||
if any(out) != any(empty) || err != nil {
|
|
||||||
return out, err
|
|
||||||
}
|
|
||||||
return next(ctx, in)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SkipNilHandler skips the handle function if the handler is nil.
|
|
||||||
// If handle is nil, an empty output is returned.
|
|
||||||
// The function is safe to call with nil handler.
|
|
||||||
func SkipNilHandler[O, In, Out any](handler *O, handle Handler[In, Out]) Handler[In, Out] {
|
|
||||||
return func(ctx context.Context, in In) (out Out, err error) {
|
|
||||||
if handler == nil {
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
return handle(ctx, in)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SkipReturnPreviousHandler skips the handle function if the handler is nil and returns the input.
|
|
||||||
// The function is safe to call with nil handler.
|
|
||||||
func SkipReturnPreviousHandler[O, In any](handler *O, handle Handler[In, In]) Handler[In, In] {
|
|
||||||
return func(ctx context.Context, in In) (out In, err error) {
|
|
||||||
if handler == nil {
|
|
||||||
return in, nil
|
|
||||||
}
|
|
||||||
return handle(ctx, in)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ResFuncToHandle[In any, Out any](fn func(context.Context, In) Out) Handler[In, Out] {
|
|
||||||
return func(ctx context.Context, in In) (out Out, err error) {
|
|
||||||
return fn(ctx, in), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ErrFuncToHandle[In any](fn func(context.Context, In) error) Handler[In, In] {
|
|
||||||
return func(ctx context.Context, in In) (out In, err error) {
|
|
||||||
err = fn(ctx, in)
|
|
||||||
if err != nil {
|
|
||||||
return out, err
|
|
||||||
}
|
|
||||||
return in, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NoReturnToHandle[In any](fn func(context.Context, In)) Handler[In, In] {
|
|
||||||
return func(ctx context.Context, in In) (out In, err error) {
|
|
||||||
fn(ctx, in)
|
|
||||||
return in, nil
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,100 +0,0 @@
|
|||||||
package orchestrate
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/backend/repository"
|
|
||||||
"github.com/zitadel/zitadel/backend/repository/cached"
|
|
||||||
"github.com/zitadel/zitadel/backend/repository/event"
|
|
||||||
"github.com/zitadel/zitadel/backend/repository/orchestrate/handler"
|
|
||||||
"github.com/zitadel/zitadel/backend/repository/sql"
|
|
||||||
"github.com/zitadel/zitadel/backend/repository/telemetry/logged"
|
|
||||||
"github.com/zitadel/zitadel/backend/repository/telemetry/traced"
|
|
||||||
"github.com/zitadel/zitadel/backend/storage/cache"
|
|
||||||
"github.com/zitadel/zitadel/backend/storage/database"
|
|
||||||
"github.com/zitadel/zitadel/backend/telemetry/tracing"
|
|
||||||
)
|
|
||||||
|
|
||||||
type InstanceOptions struct {
|
|
||||||
cache *cached.Instance
|
|
||||||
}
|
|
||||||
|
|
||||||
type instance struct {
|
|
||||||
options[InstanceOptions]
|
|
||||||
*InstanceOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
func Instance(opts ...Option[InstanceOptions]) *instance {
|
|
||||||
i := new(instance)
|
|
||||||
i.InstanceOptions = &i.options.custom
|
|
||||||
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt.apply(&i.options)
|
|
||||||
}
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithInstanceCache(c cache.Cache[repository.InstanceIndex, string, *repository.Instance]) Option[InstanceOptions] {
|
|
||||||
return func(opts *options[InstanceOptions]) {
|
|
||||||
opts.custom.cache = cached.NewInstance(c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *instance) Create(ctx context.Context, tx database.Transaction, instance *repository.Instance) (*repository.Instance, error) {
|
|
||||||
return traced.Wrap(i.tracer, "instance.SetUp",
|
|
||||||
handler.Chains(
|
|
||||||
handler.Decorates(
|
|
||||||
sql.Execute(tx).CreateInstance,
|
|
||||||
traced.Decorate[*repository.Instance, *repository.Instance](i.tracer, tracing.WithSpanName("instance.sql.SetUp")),
|
|
||||||
logged.Decorate[*repository.Instance, *repository.Instance](i.logger, "instance.sql.SetUp"),
|
|
||||||
),
|
|
||||||
handler.Decorates(
|
|
||||||
event.Store(tx).CreateInstance,
|
|
||||||
traced.Decorate[*repository.Instance, *repository.Instance](i.tracer, tracing.WithSpanName("instance.event.SetUp")),
|
|
||||||
logged.Decorate[*repository.Instance, *repository.Instance](i.logger, "instance.event.SetUp"),
|
|
||||||
),
|
|
||||||
handler.SkipReturnPreviousHandler(i.cache,
|
|
||||||
handler.Decorates(
|
|
||||||
handler.NoReturnToHandle(i.cache.Set),
|
|
||||||
traced.Decorate[*repository.Instance, *repository.Instance](i.tracer, tracing.WithSpanName("instance.cache.SetUp")),
|
|
||||||
logged.Decorate[*repository.Instance, *repository.Instance](i.logger, "instance.cache.SetUp"),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)(ctx, instance)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *instance) ByID(ctx context.Context, querier database.Querier, id string) (*repository.Instance, error) {
|
|
||||||
return traced.Wrap(i.tracer, "instance.byID",
|
|
||||||
handler.SkipNext(
|
|
||||||
handler.SkipNilHandler(i.cache,
|
|
||||||
handler.ResFuncToHandle(i.cache.ByID),
|
|
||||||
),
|
|
||||||
handler.Chain(
|
|
||||||
handler.Decorates(
|
|
||||||
sql.Query(querier).InstanceByID,
|
|
||||||
traced.Decorate[string, *repository.Instance](i.tracer, tracing.WithSpanName("instance.sql.ByID")),
|
|
||||||
logged.Decorate[string, *repository.Instance](i.logger, "instance.sql.ByID"),
|
|
||||||
),
|
|
||||||
handler.SkipNilHandler(i.cache, handler.NoReturnToHandle(i.cache.Set)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)(ctx, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *instance) ByDomain(ctx context.Context, querier database.Querier, domain string) (*repository.Instance, error) {
|
|
||||||
return traced.Wrap(i.tracer, "instance.byDomain",
|
|
||||||
handler.SkipNext(
|
|
||||||
handler.SkipNilHandler(i.cache,
|
|
||||||
handler.ResFuncToHandle(i.cache.ByDomain),
|
|
||||||
),
|
|
||||||
handler.Chain(
|
|
||||||
handler.Decorate(
|
|
||||||
sql.Query(querier).InstanceByDomain,
|
|
||||||
traced.Decorate[string, *repository.Instance](i.tracer, tracing.WithSpanName("instance.sql.ByDomain")),
|
|
||||||
),
|
|
||||||
handler.SkipNilHandler(i.cache, handler.NoReturnToHandle(i.cache.Set)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)(ctx, domain)
|
|
||||||
}
|
|
@@ -1,244 +0,0 @@
|
|||||||
package orchestrate_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"log/slog"
|
|
||||||
"os"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/backend/repository"
|
|
||||||
"github.com/zitadel/zitadel/backend/repository/orchestrate"
|
|
||||||
"github.com/zitadel/zitadel/backend/repository/sql"
|
|
||||||
"github.com/zitadel/zitadel/backend/storage/cache"
|
|
||||||
"github.com/zitadel/zitadel/backend/storage/cache/connector/gomap"
|
|
||||||
"github.com/zitadel/zitadel/backend/storage/database"
|
|
||||||
"github.com/zitadel/zitadel/backend/storage/database/mock"
|
|
||||||
"github.com/zitadel/zitadel/backend/telemetry/logging"
|
|
||||||
"github.com/zitadel/zitadel/backend/telemetry/tracing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_instance_Create(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
ctx context.Context
|
|
||||||
tx database.Transaction
|
|
||||||
instance *repository.Instance
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
opts []orchestrate.Option[orchestrate.InstanceOptions]
|
|
||||||
args args
|
|
||||||
want *repository.Instance
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "simple",
|
|
||||||
opts: []orchestrate.Option[orchestrate.InstanceOptions]{
|
|
||||||
orchestrate.WithTracer[orchestrate.InstanceOptions](tracing.NewTracer("test")),
|
|
||||||
orchestrate.WithLogger[orchestrate.InstanceOptions](logging.New(slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug})))),
|
|
||||||
orchestrate.WithInstanceCache(
|
|
||||||
gomap.NewCache[repository.InstanceIndex, string, *repository.Instance](context.Background(), repository.InstanceIndices, cache.Config{}),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
args: args{
|
|
||||||
ctx: context.Background(),
|
|
||||||
tx: mock.NewTransaction(t, mock.ExpectExec(sql.InstanceCreateStmt, "ID", "Name")),
|
|
||||||
instance: &repository.Instance{
|
|
||||||
ID: "ID",
|
|
||||||
Name: "Name",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
want: &repository.Instance{
|
|
||||||
ID: "ID",
|
|
||||||
Name: "Name",
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "without cache",
|
|
||||||
opts: []orchestrate.Option[orchestrate.InstanceOptions]{
|
|
||||||
orchestrate.WithTracer[orchestrate.InstanceOptions](tracing.NewTracer("test")),
|
|
||||||
orchestrate.WithLogger[orchestrate.InstanceOptions](logging.New(slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug})))),
|
|
||||||
},
|
|
||||||
args: args{
|
|
||||||
ctx: context.Background(),
|
|
||||||
tx: mock.NewTransaction(t, mock.ExpectExec(sql.InstanceCreateStmt, "ID", "Name")),
|
|
||||||
instance: &repository.Instance{
|
|
||||||
ID: "ID",
|
|
||||||
Name: "Name",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
want: &repository.Instance{
|
|
||||||
ID: "ID",
|
|
||||||
Name: "Name",
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "without cache, tracer",
|
|
||||||
opts: []orchestrate.Option[orchestrate.InstanceOptions]{
|
|
||||||
orchestrate.WithLogger[orchestrate.InstanceOptions](logging.New(slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug})))),
|
|
||||||
},
|
|
||||||
args: args{
|
|
||||||
ctx: context.Background(),
|
|
||||||
tx: mock.NewTransaction(t, mock.ExpectExec(sql.InstanceCreateStmt, "ID", "Name")),
|
|
||||||
instance: &repository.Instance{
|
|
||||||
ID: "ID",
|
|
||||||
Name: "Name",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
want: &repository.Instance{
|
|
||||||
ID: "ID",
|
|
||||||
Name: "Name",
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "without cache, tracer, logger",
|
|
||||||
args: args{
|
|
||||||
ctx: context.Background(),
|
|
||||||
tx: mock.NewTransaction(t, mock.ExpectExec(sql.InstanceCreateStmt, "ID", "Name")),
|
|
||||||
instance: &repository.Instance{
|
|
||||||
ID: "ID",
|
|
||||||
Name: "Name",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
want: &repository.Instance{
|
|
||||||
ID: "ID",
|
|
||||||
Name: "Name",
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
fmt.Printf("------------------------ %s ------------------------\n", tt.name)
|
|
||||||
i := orchestrate.Instance(tt.opts...)
|
|
||||||
got, err := i.Create(tt.args.ctx, tt.args.tx, tt.args.instance)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("instance.Create() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("instance.Create() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_instance_ByID(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
ctx context.Context
|
|
||||||
tx database.Transaction
|
|
||||||
id string
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
opts []orchestrate.Option[orchestrate.InstanceOptions]
|
|
||||||
args args
|
|
||||||
want *repository.Instance
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "simple, not cached",
|
|
||||||
opts: []orchestrate.Option[orchestrate.InstanceOptions]{
|
|
||||||
orchestrate.WithTracer[orchestrate.InstanceOptions](tracing.NewTracer("test")),
|
|
||||||
orchestrate.WithLogger[orchestrate.InstanceOptions](logging.New(slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug})))),
|
|
||||||
orchestrate.WithInstanceCache(
|
|
||||||
gomap.NewCache[repository.InstanceIndex, string, *repository.Instance](context.Background(), repository.InstanceIndices, cache.Config{}),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
args: args{
|
|
||||||
ctx: context.Background(),
|
|
||||||
tx: mock.NewTransaction(t,
|
|
||||||
mock.ExpectQueryRow(mock.NewRow(t, "id", "Name"), sql.InstanceByIDStmt, "id"),
|
|
||||||
),
|
|
||||||
id: "id",
|
|
||||||
},
|
|
||||||
want: &repository.Instance{
|
|
||||||
ID: "id",
|
|
||||||
Name: "Name",
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "simple, cached",
|
|
||||||
opts: []orchestrate.Option[orchestrate.InstanceOptions]{
|
|
||||||
orchestrate.WithTracer[orchestrate.InstanceOptions](tracing.NewTracer("test")),
|
|
||||||
orchestrate.WithLogger[orchestrate.InstanceOptions](logging.New(slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug})))),
|
|
||||||
orchestrate.WithInstanceCache(
|
|
||||||
func() cache.Cache[repository.InstanceIndex, string, *repository.Instance] {
|
|
||||||
c := gomap.NewCache[repository.InstanceIndex, string, *repository.Instance](context.Background(), repository.InstanceIndices, cache.Config{})
|
|
||||||
c.Set(context.Background(), &repository.Instance{
|
|
||||||
ID: "id",
|
|
||||||
Name: "Name",
|
|
||||||
})
|
|
||||||
return c
|
|
||||||
}(),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
args: args{
|
|
||||||
ctx: context.Background(),
|
|
||||||
tx: mock.NewTransaction(t,
|
|
||||||
mock.ExpectQueryRow(mock.NewRow(t, "id", "Name"), sql.InstanceByIDStmt, "id"),
|
|
||||||
),
|
|
||||||
id: "id",
|
|
||||||
},
|
|
||||||
want: &repository.Instance{
|
|
||||||
ID: "id",
|
|
||||||
Name: "Name",
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
// {
|
|
||||||
// name: "without cache, tracer",
|
|
||||||
// opts: []orchestrate.Option[orchestrate.InstanceOptions]{
|
|
||||||
// orchestrate.WithLogger[orchestrate.InstanceOptions](logging.New(slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug})))),
|
|
||||||
// },
|
|
||||||
// args: args{
|
|
||||||
// ctx: context.Background(),
|
|
||||||
// tx: mock.NewTransaction(),
|
|
||||||
// id: &repository.Instance{
|
|
||||||
// ID: "ID",
|
|
||||||
// Name: "Name",
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// want: &repository.Instance{
|
|
||||||
// ID: "ID",
|
|
||||||
// Name: "Name",
|
|
||||||
// },
|
|
||||||
// wantErr: false,
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// name: "without cache, tracer, logger",
|
|
||||||
// args: args{
|
|
||||||
// ctx: context.Background(),
|
|
||||||
// tx: mock.NewTransaction(),
|
|
||||||
// id: &repository.Instance{
|
|
||||||
// ID: "ID",
|
|
||||||
// Name: "Name",
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// want: &repository.Instance{
|
|
||||||
// ID: "ID",
|
|
||||||
// Name: "Name",
|
|
||||||
// },
|
|
||||||
// wantErr: false,
|
|
||||||
// },
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
fmt.Printf("------------------------ %s ------------------------\n", tt.name)
|
|
||||||
i := orchestrate.Instance(tt.opts...)
|
|
||||||
got, err := i.ByID(tt.args.ctx, tt.args.tx, tt.args.id)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("instance.Create() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("instance.Create() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,35 +0,0 @@
|
|||||||
package orchestrate
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/zitadel/zitadel/backend/telemetry/logging"
|
|
||||||
"github.com/zitadel/zitadel/backend/telemetry/tracing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// options are the default options for orchestrators.
|
|
||||||
type options[T any] struct {
|
|
||||||
custom T
|
|
||||||
defaultOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
type defaultOptions struct {
|
|
||||||
tracer *tracing.Tracer
|
|
||||||
logger *logging.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
type Option[T any] func(*options[T])
|
|
||||||
|
|
||||||
func WithTracer[T any](tracer *tracing.Tracer) Option[T] {
|
|
||||||
return func(o *options[T]) {
|
|
||||||
o.tracer = tracer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithLogger[T any](logger *logging.Logger) Option[T] {
|
|
||||||
return func(o *options[T]) {
|
|
||||||
o.logger = logger
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o Option[T]) apply(opts *options[T]) {
|
|
||||||
o(opts)
|
|
||||||
}
|
|
@@ -1,70 +0,0 @@
|
|||||||
package orchestrate
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/backend/repository"
|
|
||||||
"github.com/zitadel/zitadel/backend/repository/cached"
|
|
||||||
"github.com/zitadel/zitadel/backend/repository/event"
|
|
||||||
"github.com/zitadel/zitadel/backend/repository/orchestrate/handler"
|
|
||||||
"github.com/zitadel/zitadel/backend/repository/sql"
|
|
||||||
"github.com/zitadel/zitadel/backend/repository/telemetry/traced"
|
|
||||||
"github.com/zitadel/zitadel/backend/storage/cache"
|
|
||||||
"github.com/zitadel/zitadel/backend/storage/database"
|
|
||||||
"github.com/zitadel/zitadel/backend/telemetry/tracing"
|
|
||||||
)
|
|
||||||
|
|
||||||
type UserOptions struct {
|
|
||||||
cache *cached.User
|
|
||||||
}
|
|
||||||
|
|
||||||
type user struct {
|
|
||||||
options[UserOptions]
|
|
||||||
*UserOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
func User(opts ...Option[UserOptions]) *user {
|
|
||||||
i := new(user)
|
|
||||||
i.UserOptions = &i.options.custom
|
|
||||||
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(&i.options)
|
|
||||||
}
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithUserCache(cache cache.Cache[repository.UserIndex, string, *repository.User]) Option[UserOptions] {
|
|
||||||
return func(i *options[UserOptions]) {
|
|
||||||
i.custom.cache = cached.NewUser(cache)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *user) Create(ctx context.Context, tx database.Transaction, user *repository.User) (*repository.User, error) {
|
|
||||||
return traced.Wrap(i.tracer, "user.Create",
|
|
||||||
handler.Chain(
|
|
||||||
handler.Decorate(
|
|
||||||
sql.Execute(tx).CreateUser,
|
|
||||||
traced.Decorate[*repository.User, *repository.User](i.tracer, tracing.WithSpanName("user.sql.Create")),
|
|
||||||
),
|
|
||||||
handler.Decorate(
|
|
||||||
event.Store(tx).CreateUser,
|
|
||||||
traced.Decorate[*repository.User, *repository.User](i.tracer, tracing.WithSpanName("user.event.Create")),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)(ctx, user)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *user) ByID(ctx context.Context, querier database.Querier, id string) (*repository.User, error) {
|
|
||||||
return handler.SkipNext(
|
|
||||||
handler.SkipNilHandler(i.cache,
|
|
||||||
handler.ResFuncToHandle(i.cache.ByID),
|
|
||||||
),
|
|
||||||
handler.Chain(
|
|
||||||
handler.Decorate(
|
|
||||||
sql.Query(querier).UserByID,
|
|
||||||
traced.Decorate[string, *repository.User](i.tracer, tracing.WithSpanName("user.sql.ByID")),
|
|
||||||
),
|
|
||||||
handler.SkipNilHandler(i.custom.cache, handler.NoReturnToHandle(i.cache.Set)),
|
|
||||||
),
|
|
||||||
)(ctx, id)
|
|
||||||
}
|
|
@@ -1,21 +0,0 @@
|
|||||||
package sql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/zitadel/zitadel/backend/storage/database"
|
|
||||||
)
|
|
||||||
|
|
||||||
type executor[C database.Executor] struct {
|
|
||||||
client C
|
|
||||||
}
|
|
||||||
|
|
||||||
func Execute[C database.Executor](client C) *executor[C] {
|
|
||||||
return &executor[C]{client: client}
|
|
||||||
}
|
|
||||||
|
|
||||||
type querier[C database.Querier] struct {
|
|
||||||
client C
|
|
||||||
}
|
|
||||||
|
|
||||||
func Query[C database.Querier](client C) *querier[C] {
|
|
||||||
return &querier[C]{client: client}
|
|
||||||
}
|
|
@@ -1,63 +0,0 @@
|
|||||||
package sql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/backend/repository"
|
|
||||||
)
|
|
||||||
|
|
||||||
const InstanceByIDStmt = `SELECT id, name FROM instances WHERE id = $1`
|
|
||||||
|
|
||||||
func (q *querier[C]) InstanceByID(ctx context.Context, id string) (*repository.Instance, error) {
|
|
||||||
log.Println("sql.instance.byID")
|
|
||||||
row := q.client.QueryRow(ctx, InstanceByIDStmt, id)
|
|
||||||
var instance repository.Instance
|
|
||||||
if err := row.Scan(&instance.ID, &instance.Name); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &instance, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
const instanceByDomainQuery = `SELECT i.id, i.name FROM instances i JOIN instance_domains id ON i.id = id.instance_id WHERE id.domain = $1`
|
|
||||||
|
|
||||||
func (q *querier[C]) InstanceByDomain(ctx context.Context, domain string) (*repository.Instance, error) {
|
|
||||||
log.Println("sql.instance.byDomain")
|
|
||||||
row := q.client.QueryRow(ctx, instanceByDomainQuery, domain)
|
|
||||||
var instance repository.Instance
|
|
||||||
if err := row.Scan(&instance.ID, &instance.Name); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &instance, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *querier[C]) ListInstances(ctx context.Context, request *repository.ListRequest) (res []*repository.Instance, err error) {
|
|
||||||
log.Println("sql.instance.list")
|
|
||||||
rows, err := q.client.Query(ctx, "SELECT id, name FROM instances")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
for rows.Next() {
|
|
||||||
var instance repository.Instance
|
|
||||||
if err = rows.Scan(&instance.ID, &instance.Name); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
res = append(res, &instance)
|
|
||||||
}
|
|
||||||
if err = rows.Err(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
const InstanceCreateStmt = `INSERT INTO instances (id, name) VALUES ($1, $2)`
|
|
||||||
|
|
||||||
func (e *executor[C]) CreateInstance(ctx context.Context, instance *repository.Instance) (*repository.Instance, error) {
|
|
||||||
log.Println("sql.instance.create")
|
|
||||||
err := e.client.Exec(ctx, InstanceCreateStmt, instance.ID, instance.Name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return instance, nil
|
|
||||||
}
|
|
@@ -1,29 +0,0 @@
|
|||||||
package sql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/backend/repository"
|
|
||||||
)
|
|
||||||
|
|
||||||
const userByIDQuery = `SELECT id, username FROM users WHERE id = $1`
|
|
||||||
|
|
||||||
func (q *querier[C]) UserByID(ctx context.Context, id string) (res *repository.User, err error) {
|
|
||||||
log.Println("sql.user.byID")
|
|
||||||
row := q.client.QueryRow(ctx, userByIDQuery, id)
|
|
||||||
var user repository.User
|
|
||||||
if err := row.Scan(&user.ID, &user.Username); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &user, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *executor[C]) CreateUser(ctx context.Context, user *repository.User) (res *repository.User, err error) {
|
|
||||||
log.Println("sql.user.create")
|
|
||||||
err = e.client.Exec(ctx, "INSERT INTO users (id, username) VALUES ($1, $2)", user.ID, user.Username)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return user, nil
|
|
||||||
}
|
|
@@ -1,44 +0,0 @@
|
|||||||
package logged
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"log"
|
|
||||||
"log/slog"
|
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/backend/repository/orchestrate/handler"
|
|
||||||
"github.com/zitadel/zitadel/backend/telemetry/logging"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Wrap decorates the given handle function with logging.
|
|
||||||
// The function is safe to call with nil logger.
|
|
||||||
func Wrap[Req, Res any](logger *logging.Logger, name string, handle handler.Handler[Req, Res]) handler.Handler[Req, Res] {
|
|
||||||
if logger == nil {
|
|
||||||
return handle
|
|
||||||
}
|
|
||||||
return func(ctx context.Context, r Req) (_ Res, err error) {
|
|
||||||
logger.Debug("execute", slog.String("handler", name))
|
|
||||||
defer logger.Debug("done", slog.String("handler", name))
|
|
||||||
log.Println("log.wrap", name)
|
|
||||||
return handle(ctx, r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decorate decorates the given handle function with logging.
|
|
||||||
// The function is safe to call with nil logger.
|
|
||||||
func Decorate[Req, Res any](logger *logging.Logger, name string) handler.Decorator[Req, Res] {
|
|
||||||
return func(ctx context.Context, request Req, handle handler.Handler[Req, Res]) (res Res, err error) {
|
|
||||||
if logger == nil {
|
|
||||||
return handle(ctx, request)
|
|
||||||
}
|
|
||||||
logger = logger.With("handler", name)
|
|
||||||
logger.DebugContext(ctx, "execute")
|
|
||||||
log.Println("logged.decorate", name)
|
|
||||||
defer func() {
|
|
||||||
if err != nil {
|
|
||||||
logger.ErrorContext(ctx, "failed", slog.String("cause", err.Error()))
|
|
||||||
}
|
|
||||||
logger.DebugContext(ctx, "done")
|
|
||||||
}()
|
|
||||||
return handle(ctx, request)
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,50 +0,0 @@
|
|||||||
package traced
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/backend/repository/orchestrate/handler"
|
|
||||||
"github.com/zitadel/zitadel/backend/telemetry/tracing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Wrap decorates the given handle function with tracing.
|
|
||||||
// The function is safe to call with nil tracer.
|
|
||||||
func Wrap[Req, Res any](tracer *tracing.Tracer, name string, handle handler.Handler[Req, Res]) handler.Handler[Req, Res] {
|
|
||||||
if tracer == nil {
|
|
||||||
return handle
|
|
||||||
}
|
|
||||||
return func(ctx context.Context, r Req) (_ Res, err error) {
|
|
||||||
ctx, span := tracer.Start(
|
|
||||||
ctx,
|
|
||||||
name,
|
|
||||||
)
|
|
||||||
log.Println("trace.wrap", name)
|
|
||||||
defer func() {
|
|
||||||
if err != nil {
|
|
||||||
span.RecordError(err)
|
|
||||||
}
|
|
||||||
span.End()
|
|
||||||
}()
|
|
||||||
return handle(ctx, r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decorate decorates the given handle function with tracing.
|
|
||||||
// The function is safe to call with nil tracer.
|
|
||||||
func Decorate[Req, Res any](tracer *tracing.Tracer, opts ...tracing.DecorateOption) handler.Decorator[Req, Res] {
|
|
||||||
return func(ctx context.Context, r Req, handle handler.Handler[Req, Res]) (_ Res, err error) {
|
|
||||||
if tracer == nil {
|
|
||||||
return handle(ctx, r)
|
|
||||||
}
|
|
||||||
o := new(tracing.DecorateOptions)
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(o)
|
|
||||||
}
|
|
||||||
log.Println("traced.decorate")
|
|
||||||
|
|
||||||
ctx, end := o.Start(ctx, tracer)
|
|
||||||
defer end(err)
|
|
||||||
return handle(ctx, r)
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,33 +1,97 @@
|
|||||||
package repository
|
package repository
|
||||||
|
|
||||||
import "github.com/zitadel/zitadel/backend/storage/cache"
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/backend/handler"
|
||||||
|
"github.com/zitadel/zitadel/backend/storage/database"
|
||||||
|
"github.com/zitadel/zitadel/backend/telemetry/tracing"
|
||||||
|
"github.com/zitadel/zitadel/internal/crypto"
|
||||||
|
)
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
ID string
|
ID string
|
||||||
Username string
|
Username string
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserIndex uint8
|
type UserOptions struct {
|
||||||
|
cache *UserCache
|
||||||
var UserIndices = []UserIndex{
|
|
||||||
UserByIDIndex,
|
|
||||||
UserByUsernameIndex,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
type user struct {
|
||||||
UserByIDIndex UserIndex = iota
|
options[UserOptions]
|
||||||
UserByUsernameIndex
|
*UserOptions
|
||||||
)
|
|
||||||
|
|
||||||
var _ cache.Entry[UserIndex, string] = (*User)(nil)
|
|
||||||
|
|
||||||
// Keys implements [cache.Entry].
|
|
||||||
func (u *User) Keys(index UserIndex) (key []string) {
|
|
||||||
switch index {
|
|
||||||
case UserByIDIndex:
|
|
||||||
return []string{u.ID}
|
|
||||||
case UserByUsernameIndex:
|
|
||||||
return []string{u.Username}
|
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
|
func NewUser(opts ...Option[UserOptions]) *user {
|
||||||
|
i := new(user)
|
||||||
|
i.UserOptions = &i.options.custom
|
||||||
|
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(&i.options)
|
||||||
}
|
}
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithUserCache(c *UserCache) Option[UserOptions] {
|
||||||
|
return func(i *options[UserOptions]) {
|
||||||
|
i.custom.cache = c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *user) Create(ctx context.Context, tx database.Transaction, user *User) (*User, error) {
|
||||||
|
return tracing.Wrap(u.tracer, "user.Create",
|
||||||
|
handler.Chain(
|
||||||
|
handler.Decorate(
|
||||||
|
execute(tx).CreateUser,
|
||||||
|
tracing.Decorate[*User, *User](u.tracer, tracing.WithSpanName("user.sql.Create")),
|
||||||
|
),
|
||||||
|
handler.Decorate(
|
||||||
|
events(tx).CreateUser,
|
||||||
|
tracing.Decorate[*User, *User](u.tracer, tracing.WithSpanName("user.event.Create")),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)(ctx, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *user) ByID(ctx context.Context, client database.Querier, id string) (*User, error) {
|
||||||
|
return handler.SkipNext(
|
||||||
|
handler.SkipNilHandler(u.cache,
|
||||||
|
handler.ResFuncToHandle(u.cache.ByID),
|
||||||
|
),
|
||||||
|
handler.Chain(
|
||||||
|
handler.Decorate(
|
||||||
|
query(client).UserByID,
|
||||||
|
tracing.Decorate[string, *User](u.tracer, tracing.WithSpanName("user.sql.ByID")),
|
||||||
|
),
|
||||||
|
handler.SkipNilHandler(u.custom.cache, handler.NoReturnToHandle(u.cache.Set)),
|
||||||
|
),
|
||||||
|
)(ctx, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChangeEmail struct {
|
||||||
|
UserID string
|
||||||
|
Email string
|
||||||
|
Opt *ChangeEmailOption
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChangeEmailOption struct {
|
||||||
|
returnCode bool
|
||||||
|
isVerified bool
|
||||||
|
sendCode bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChangeEmailVerifiedOption struct {
|
||||||
|
isVerified bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChangeEmailReturnCodeOption struct {
|
||||||
|
alg crypto.EncryptionAlgorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChangeEmailSendCodeOption struct {
|
||||||
|
alg crypto.EncryptionAlgorithm
|
||||||
|
urlTemplate string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *user) ChangeEmail(ctx context.Context, client database.Executor, change *ChangeEmail)
|
||||||
|
@@ -72,6 +72,14 @@ type Querier interface {
|
|||||||
QueryRow(ctx context.Context, stmt string, args ...any) Row
|
QueryRow(ctx context.Context, stmt string, args ...any) Row
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Query[Out any](q Querier, fn func(q Querier) ([]Out, error)) ([]Out, error) {
|
||||||
|
return fn(q)
|
||||||
|
}
|
||||||
|
|
||||||
|
func QueryRow[Out any](q Querier, fn func(q Querier) (Out, error)) (Out, error) {
|
||||||
|
return fn(q)
|
||||||
|
}
|
||||||
|
|
||||||
type Executor interface {
|
type Executor interface {
|
||||||
Exec(ctx context.Context, stmt string, args ...any) error
|
Exec(ctx context.Context, stmt string, args ...any) error
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,12 @@
|
|||||||
package logging
|
package logging
|
||||||
|
|
||||||
import "log/slog"
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/backend/handler"
|
||||||
|
)
|
||||||
|
|
||||||
type Logger struct {
|
type Logger struct {
|
||||||
*slog.Logger
|
*slog.Logger
|
||||||
@@ -13,3 +19,37 @@ func New(l *slog.Logger) *Logger {
|
|||||||
func (l *Logger) With(args ...any) *Logger {
|
func (l *Logger) With(args ...any) *Logger {
|
||||||
return &Logger{l.Logger.With(args...)}
|
return &Logger{l.Logger.With(args...)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wrap decorates the given handle function with
|
||||||
|
// The function is safe to call with nil logger.
|
||||||
|
func Wrap[Req, Res any](logger *Logger, name string, handle handler.Handle[Req, Res]) handler.Handle[Req, Res] {
|
||||||
|
if logger == nil {
|
||||||
|
return handle
|
||||||
|
}
|
||||||
|
return func(ctx context.Context, r Req) (_ Res, err error) {
|
||||||
|
logger.Debug("execute", slog.String("handler", name))
|
||||||
|
defer logger.Debug("done", slog.String("handler", name))
|
||||||
|
log.Println("log.wrap", name)
|
||||||
|
return handle(ctx, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decorate decorates the given handle function with logging.
|
||||||
|
// The function is safe to call with nil logger.
|
||||||
|
func Decorate[Req, Res any](logger *Logger, name string) handler.Middleware[Req, Res] {
|
||||||
|
return func(ctx context.Context, request Req, handle handler.Handle[Req, Res]) (res Res, err error) {
|
||||||
|
if logger == nil {
|
||||||
|
return handle(ctx, request)
|
||||||
|
}
|
||||||
|
logger = logger.With("handler", name)
|
||||||
|
logger.DebugContext(ctx, "execute")
|
||||||
|
log.Println("logged.decorate", name)
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
logger.ErrorContext(ctx, "failed", slog.String("cause", err.Error()))
|
||||||
|
}
|
||||||
|
logger.DebugContext(ctx, "done")
|
||||||
|
}()
|
||||||
|
return handle(ctx, request)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -2,10 +2,13 @@ package tracing
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"log"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"go.opentelemetry.io/otel"
|
"go.opentelemetry.io/otel"
|
||||||
"go.opentelemetry.io/otel/trace"
|
"go.opentelemetry.io/otel/trace"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/backend/handler"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Tracer struct{ trace.Tracer }
|
type Tracer struct{ trace.Tracer }
|
||||||
@@ -43,6 +46,47 @@ func WithSpanEndOptions(opts ...trace.SpanEndOption) DecorateOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wrap decorates the given handle function with tracing.
|
||||||
|
// The function is safe to call with nil tracer.
|
||||||
|
func Wrap[Req, Res any](tracer *Tracer, name string, handle handler.Handle[Req, Res]) handler.Handle[Req, Res] {
|
||||||
|
if tracer == nil {
|
||||||
|
return handle
|
||||||
|
}
|
||||||
|
return func(ctx context.Context, r Req) (_ Res, err error) {
|
||||||
|
ctx, span := tracer.Start(
|
||||||
|
ctx,
|
||||||
|
name,
|
||||||
|
)
|
||||||
|
log.Println("trace.wrap", name)
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
span.RecordError(err)
|
||||||
|
}
|
||||||
|
span.End()
|
||||||
|
}()
|
||||||
|
return handle(ctx, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decorate decorates the given handle function with
|
||||||
|
// The function is safe to call with nil tracer.
|
||||||
|
func Decorate[Req, Res any](tracer *Tracer, opts ...DecorateOption) handler.Middleware[Req, Res] {
|
||||||
|
return func(ctx context.Context, r Req, handle handler.Handle[Req, Res]) (_ Res, err error) {
|
||||||
|
if tracer == nil {
|
||||||
|
return handle(ctx, r)
|
||||||
|
}
|
||||||
|
o := new(DecorateOptions)
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(o)
|
||||||
|
}
|
||||||
|
log.Println("traced.decorate")
|
||||||
|
|
||||||
|
ctx, end := o.Start(ctx, tracer)
|
||||||
|
defer end(err)
|
||||||
|
return handle(ctx, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (o *DecorateOptions) Start(ctx context.Context, tracer *Tracer) (context.Context, func(error)) {
|
func (o *DecorateOptions) Start(ctx context.Context, tracer *Tracer) (context.Context, func(error)) {
|
||||||
if o.spanName == "" {
|
if o.spanName == "" {
|
||||||
o.spanName = functionName()
|
o.spanName = functionName()
|
||||||
@@ -56,23 +100,6 @@ func (o *DecorateOptions) end(err error) {
|
|||||||
o.span.End(o.endOpts...)
|
o.span.End(o.endOpts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// func (t Tracer) Decorate(ctx context.Context, fn func(ctx context.Context) error, opts ...DecorateOption) {
|
|
||||||
// o := new(DecorateOptions)
|
|
||||||
// for _, opt := range opts {
|
|
||||||
// opt(o)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if o.spanName == "" {
|
|
||||||
// o.spanName = functionName()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// ctx, span := t.Tracer.Start(ctx, o.spanName, o.startOpts...)
|
|
||||||
// defer span.End(o.endOpts...)
|
|
||||||
|
|
||||||
// err := fn(ctx)
|
|
||||||
// span.RecordError(err)
|
|
||||||
// }
|
|
||||||
|
|
||||||
func functionName() string {
|
func functionName() string {
|
||||||
counter, _, _, success := runtime.Caller(2)
|
counter, _, _, success := runtime.Caller(2)
|
||||||
|
|
||||||
|
BIN
docs/static/img/zitadel_cluster_architecture.png
vendored
BIN
docs/static/img/zitadel_cluster_architecture.png
vendored
Binary file not shown.
Before Width: | Height: | Size: 10 KiB |
Binary file not shown.
Before Width: | Height: | Size: 7.2 KiB |
Reference in New Issue
Block a user