zitadel/internal/command/action_v2_execution_model.go
Elio Bischof cc3ec1e2a7
feat(v3alpha): write actions (#8225)
# Which Problems Are Solved

The current v3alpha actions APIs don't exactly adhere to the [new
resources API
design](https://zitadel.com/docs/apis/v3#standard-resources).

# How the Problems Are Solved

- **Breaking**: The current v3alpha actions APIs are removed. This is
breaking.
- **Resource Namespace**: New v3alpha actions APIs for targets and
executions are added under the namespace /resources.
- **Feature Flag**: New v3alpha actions APIs still have to be activated
using the actions feature flag
- **Reduced Executions Overhead**: Executions are managed similar to
settings according to the new API design: an empty list of targets
basically makes an execution a Noop. So a single method, SetExecution is
enough to cover all use cases. Noop executions are not returned in
future search requests.
- **Compatibility**: The executions created with previous v3alpha APIs
are still available to be managed with the new executions API.

# Additional Changes

- Removed integration tests which test executions but rely on readable
targets. They are added again with #8169

# Additional Context

Closes #8168
2024-07-31 14:42:12 +02:00

147 lines
3.8 KiB
Go

package command
import (
"slices"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/repository/execution"
)
type ExecutionWriteModel struct {
eventstore.WriteModel
Targets []string
Includes []string
ExecutionTargets []*execution.Target
}
func (e *ExecutionWriteModel) ExecutionTargetsEqual(targets []*execution.Target) bool {
if len(e.ExecutionTargets) != len(targets) {
return false
}
for i := range e.ExecutionTargets {
if e.ExecutionTargets[i].Type != targets[i].Type || e.ExecutionTargets[i].Target != targets[i].Target {
return false
}
}
return true
}
func (e *ExecutionWriteModel) IncludeList() []string {
includes := make([]string, 0)
for i := range e.ExecutionTargets {
if e.ExecutionTargets[i].Type == domain.ExecutionTargetTypeInclude {
includes = append(includes, e.ExecutionTargets[i].Target)
}
}
return includes
}
func (e *ExecutionWriteModel) Exists() bool {
return len(e.ExecutionTargets) > 0 || len(e.Includes) > 0 || len(e.Targets) > 0
}
func NewExecutionWriteModel(id string, resourceOwner string) *ExecutionWriteModel {
return &ExecutionWriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: id,
ResourceOwner: resourceOwner,
InstanceID: resourceOwner,
},
}
}
func (wm *ExecutionWriteModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *execution.SetEvent:
wm.Targets = e.Targets
wm.Includes = e.Includes
case *execution.SetEventV2:
wm.ExecutionTargets = e.Targets
case *execution.RemovedEvent:
wm.Targets = nil
wm.Includes = nil
wm.ExecutionTargets = nil
}
}
return wm.WriteModel.Reduce()
}
func (wm *ExecutionWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
ResourceOwner(wm.ResourceOwner).
AddQuery().
AggregateTypes(execution.AggregateType).
AggregateIDs(wm.AggregateID).
EventTypes(execution.SetEventType,
execution.SetEventV2Type,
execution.RemovedEventType).
Builder()
}
func ExecutionAggregateFromWriteModel(wm *eventstore.WriteModel) *eventstore.Aggregate {
return &eventstore.Aggregate{
ID: wm.AggregateID,
Type: execution.AggregateType,
ResourceOwner: wm.ResourceOwner,
InstanceID: wm.InstanceID,
Version: execution.AggregateVersion,
}
}
type ExecutionsExistWriteModel struct {
eventstore.WriteModel
ids []string
existingIDs []string
}
func (e *ExecutionsExistWriteModel) AllExists() bool {
return len(e.ids) == len(e.existingIDs)
}
func NewExecutionsExistWriteModel(ids []string, resourceOwner string) *ExecutionsExistWriteModel {
return &ExecutionsExistWriteModel{
WriteModel: eventstore.WriteModel{
ResourceOwner: resourceOwner,
InstanceID: resourceOwner,
},
ids: ids,
}
}
func (wm *ExecutionsExistWriteModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *execution.SetEvent:
if !slices.Contains(wm.existingIDs, e.Aggregate().ID) {
wm.existingIDs = append(wm.existingIDs, e.Aggregate().ID)
}
case *execution.SetEventV2:
if !slices.Contains(wm.existingIDs, e.Aggregate().ID) {
wm.existingIDs = append(wm.existingIDs, e.Aggregate().ID)
}
case *execution.RemovedEvent:
i := slices.Index(wm.existingIDs, e.Aggregate().ID)
if i >= 0 {
wm.existingIDs = slices.Delete(wm.existingIDs, i, i+1)
}
}
}
return wm.WriteModel.Reduce()
}
func (wm *ExecutionsExistWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
ResourceOwner(wm.ResourceOwner).
AddQuery().
AggregateTypes(execution.AggregateType).
AggregateIDs(wm.ids...).
EventTypes(execution.SetEventType,
execution.SetEventV2Type,
execution.RemovedEventType).
Builder()
}