package action import ( "context" "encoding/json" "time" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore/repository" ) const ( UniqueActionNameType = "action_names" eventTypePrefix = eventstore.EventType("action.") AddedEventType = eventTypePrefix + "added" ChangedEventType = eventTypePrefix + "changed" DeactivatedEventType = eventTypePrefix + "deactivated" ReactivatedEventType = eventTypePrefix + "reactivated" RemovedEventType = eventTypePrefix + "removed" ) func NewAddActionNameUniqueConstraint(actionName, resourceOwner string) *eventstore.EventUniqueConstraint { return eventstore.NewAddEventUniqueConstraint( UniqueActionNameType, actionName+":"+resourceOwner, "Errors.Action.AlreadyExists") } func NewRemoveActionNameUniqueConstraint(actionName, resourceOwner string) *eventstore.EventUniqueConstraint { return eventstore.NewRemoveEventUniqueConstraint( UniqueActionNameType, actionName+":"+resourceOwner) } type AddedEvent struct { eventstore.BaseEvent `json:"-"` Name string `json:"name"` Script string `json:"script,omitempty"` Timeout time.Duration `json:"timeout,omitempty"` AllowedToFail bool `json:"allowedToFail"` } func (e *AddedEvent) Data() interface{} { return e } func (e *AddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { return []*eventstore.EventUniqueConstraint{NewAddActionNameUniqueConstraint(e.Name, e.Aggregate().ResourceOwner)} } func NewAddedEvent( ctx context.Context, aggregate *eventstore.Aggregate, name, script string, timeout time.Duration, allowedToFail bool, ) *AddedEvent { return &AddedEvent{ BaseEvent: *eventstore.NewBaseEventForPush( ctx, aggregate, AddedEventType, ), Name: name, Script: script, Timeout: timeout, AllowedToFail: allowedToFail, } } func AddedEventMapper(event *repository.Event) (eventstore.Event, error) { e := &AddedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } err := json.Unmarshal(event.Data, e) if err != nil { return nil, errors.ThrowInternal(err, "ACTION-4n8vs", "unable to unmarshal action added") } return e, nil } type ChangedEvent struct { eventstore.BaseEvent `json:"-"` Name *string `json:"name,omitempty"` Script *string `json:"script,omitempty"` Timeout *time.Duration `json:"timeout,omitempty"` AllowedToFail *bool `json:"allowedToFail,omitempty"` oldName string } func (e *ChangedEvent) Data() interface{} { return e } func (e *ChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { if e.oldName == "" { return nil } return []*eventstore.EventUniqueConstraint{ NewRemoveActionNameUniqueConstraint(e.oldName, e.Aggregate().ResourceOwner), NewAddActionNameUniqueConstraint(*e.Name, e.Aggregate().ResourceOwner), } } func NewChangedEvent( ctx context.Context, aggregate *eventstore.Aggregate, changes []ActionChanges, ) (*ChangedEvent, error) { if len(changes) == 0 { return nil, errors.ThrowPreconditionFailed(nil, "ACTION-dg4t2", "Errors.NoChangesFound") } changeEvent := &ChangedEvent{ BaseEvent: *eventstore.NewBaseEventForPush( ctx, aggregate, ChangedEventType, ), } for _, change := range changes { change(changeEvent) } return changeEvent, nil } type ActionChanges func(event *ChangedEvent) func ChangeName(name, oldName string) func(event *ChangedEvent) { return func(e *ChangedEvent) { e.Name = &name e.oldName = oldName } } func ChangeScript(script string) func(event *ChangedEvent) { return func(e *ChangedEvent) { e.Script = &script } } func ChangeTimeout(timeout time.Duration) func(event *ChangedEvent) { return func(e *ChangedEvent) { e.Timeout = &timeout } } func ChangeAllowedToFail(allowedToFail bool) func(event *ChangedEvent) { return func(e *ChangedEvent) { e.AllowedToFail = &allowedToFail } } func ChangedEventMapper(event *repository.Event) (eventstore.Event, error) { e := &ChangedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } err := json.Unmarshal(event.Data, e) if err != nil { return nil, errors.ThrowInternal(err, "ACTION-4n8vs", "unable to unmarshal action changed") } return e, nil } type DeactivatedEvent struct { eventstore.BaseEvent `json:"-"` } func (e *DeactivatedEvent) Data() interface{} { return nil } func (e *DeactivatedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { return nil } func NewDeactivatedEvent(ctx context.Context, aggregate *eventstore.Aggregate) *DeactivatedEvent { return &DeactivatedEvent{ BaseEvent: *eventstore.NewBaseEventForPush( ctx, aggregate, DeactivatedEventType, ), } } func DeactivatedEventMapper(event *repository.Event) (eventstore.Event, error) { return &DeactivatedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil } type ReactivatedEvent struct { eventstore.BaseEvent `json:"-"` } func (e *ReactivatedEvent) Data() interface{} { return nil } func (e *ReactivatedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { return nil } func NewReactivatedEvent(ctx context.Context, aggregate *eventstore.Aggregate) *ReactivatedEvent { return &ReactivatedEvent{ BaseEvent: *eventstore.NewBaseEventForPush( ctx, aggregate, ReactivatedEventType, ), } } func ReactivatedEventMapper(event *repository.Event) (eventstore.Event, error) { return &ReactivatedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil } type RemovedEvent struct { eventstore.BaseEvent `json:"-"` name string } func (e *RemovedEvent) Data() interface{} { return e } func (e *RemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { return []*eventstore.EventUniqueConstraint{NewRemoveActionNameUniqueConstraint(e.name, e.Aggregate().ResourceOwner)} } func NewRemovedEvent( ctx context.Context, aggregate *eventstore.Aggregate, name string, ) *RemovedEvent { return &RemovedEvent{ BaseEvent: *eventstore.NewBaseEventForPush( ctx, aggregate, RemovedEventType, ), name: name, } } func RemovedEventMapper(event *repository.Event) (eventstore.Event, error) { return &RemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), }, nil }