zitadel/internal/command/debug_events.go
Tim Möhlmann 3aba942162
feat: add debug events API (#8533)
# Which Problems Are Solved

Add a debug API which allows pushing a set of events to be reduced in a
dedicated projection.
The events can carry a sleep duration which simulates a slow query
during projection handling.

# How the Problems Are Solved

- `CreateDebugEvents` allows pushing multiple events which simulate the
lifecycle of a resource. Each event has a `projectionSleep` field, which
issues a `pg_sleep()` statement query in the projection handler :
  - Add
  - Change
  - Remove
- `ListDebugEventsStates` list the current state of the projection,
optionally with a Trigger
- `GetDebugEventsStateByID` get the current state of the aggregate ID in
the projection, optionally with a Trigger


# Additional Changes

- none

# Additional Context

-  Allows reproduction of https://github.com/zitadel/zitadel/issues/8517
2024-09-11 08:24:00 +00:00

83 lines
2.4 KiB
Go

package command
import (
"context"
"time"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/repository/debug_events"
"github.com/zitadel/zitadel/internal/telemetry/tracing"
"github.com/zitadel/zitadel/internal/zerrors"
)
type DebugEvents struct {
AggregateID string
Events []DebugEvent
}
type DebugEvent interface {
isADebugEvent()
}
type DebugEventAdded struct {
ProjectionSleep time.Duration
Blob *string
}
type DebugEventChanged struct {
ProjectionSleep time.Duration
Blob *string
}
type DebugEventRemoved struct {
ProjectionSleep time.Duration
}
func (DebugEventAdded) isADebugEvent() {}
func (DebugEventChanged) isADebugEvent() {}
func (DebugEventRemoved) isADebugEvent() {}
func (c *Commands) CreateDebugEvents(ctx context.Context, dbe *DebugEvents) (_ *domain.ObjectDetails, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
model := NewDebugEventsWriteModel(dbe.AggregateID, authz.GetInstance(ctx).InstanceID())
if err = c.eventstore.FilterToQueryReducer(ctx, model); err != nil {
return nil, err
}
aggr := debug_events.AggregateFromWriteModel(ctx, &model.WriteModel)
cmds := make([]eventstore.Command, len(dbe.Events))
for i, event := range dbe.Events {
var cmd eventstore.Command
switch e := event.(type) {
case DebugEventAdded:
if model.State.Exists() {
return nil, zerrors.ThrowAlreadyExists(nil, "COMMAND-Aex6j", "debug aggregate already exists")
}
cmd = debug_events.NewAddedEvent(ctx, aggr, e.ProjectionSleep, e.Blob)
case DebugEventChanged:
if !model.State.Exists() {
return nil, zerrors.ThrowNotFound(nil, "COMMAND-Thie6", "debug aggregate not found")
}
cmd = debug_events.NewChangedEvent(ctx, aggr, e.ProjectionSleep, e.Blob)
case DebugEventRemoved:
if !model.State.Exists() {
return nil, zerrors.ThrowNotFound(nil, "COMMAND-Ohna9", "debug aggregate not found")
}
cmd = debug_events.NewRemovedEvent(ctx, aggr, e.ProjectionSleep)
}
cmds[i] = cmd
// be sure the state of the last event is reduced before handling the next one.
model.reduceEvent(cmd.(eventstore.Event))
}
events, err := c.eventstore.Push(ctx, cmds...)
if err != nil {
return nil, err
}
return pushedEventsToObjectDetails(events), nil
}