fix: set quotas (#6597)

* feat: set quotas

* fix: start new period on younger anchor

* cleanup e2e config

* fix set notifications

* lint

* test: fix quota projection tests

* fix add quota tests

* make quota fields nullable

* enable amount 0

* fix initial setup

* create a prerelease

* avoid success comments

* fix quota projection primary key

* Revert "fix quota projection primary key"

This reverts commit e72f4d7fa1.

* simplify write model

* fix aggregate id

* avoid push without changes

* test set quota lifecycle

* test set quota mutations

* fix quota unit test

* fix: quotas

* test quota.set event projection

* use SetQuota in integration tests

* fix: release quotas 3

* reset releaserc

* fix comment

* test notification order doesn't matter

* test notification order doesn't matter

* test with unmarshalled events

* test with unmarshalled events
This commit is contained in:
Elio Bischof
2023-09-22 11:37:16 +02:00
committed by GitHub
parent e6d273b328
commit ae1af6bc8c
20 changed files with 1385 additions and 318 deletions

View File

@@ -17,6 +17,7 @@ const (
UniqueQuotaNameType = "quota_units"
eventTypePrefix = eventstore.EventType("quota.")
AddedEventType = eventTypePrefix + "added"
SetEventType = eventTypePrefix + "set"
NotifiedEventType = eventTypePrefix + "notified"
NotificationDueEventType = eventTypePrefix + "notificationdue"
RemovedEventType = eventTypePrefix + "removed"
@@ -43,65 +44,87 @@ func NewRemoveQuotaNameUniqueConstraint(unit Unit) *eventstore.EventUniqueConstr
)
}
type AddedEvent struct {
// SetEvent describes that a quota is added or modified and contains only changed properties
type SetEvent struct {
eventstore.BaseEvent `json:"-"`
Unit Unit `json:"unit"`
From time.Time `json:"from"`
ResetInterval time.Duration `json:"interval,omitempty"`
Amount uint64 `json:"amount"`
Limit bool `json:"limit"`
Notifications []*AddedEventNotification `json:"notifications,omitempty"`
Unit Unit `json:"unit"`
From *time.Time `json:"from,omitempty"`
ResetInterval *time.Duration `json:"interval,omitempty"`
Amount *uint64 `json:"amount,omitempty"`
Limit *bool `json:"limit,omitempty"`
Notifications *[]*SetEventNotification `json:"notifications,omitempty"`
}
type AddedEventNotification struct {
type SetEventNotification struct {
ID string `json:"id"`
Percent uint16 `json:"percent"`
Repeat bool `json:"repeat,omitempty"`
Repeat bool `json:"repeat"`
CallURL string `json:"callUrl"`
}
func (e *AddedEvent) Data() interface{} {
func (e *SetEvent) Data() interface{} {
return e
}
func (e *AddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return []*eventstore.EventUniqueConstraint{NewAddQuotaUnitUniqueConstraint(e.Unit)}
func (e *SetEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return nil
}
func NewAddedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
func NewSetEvent(
base *eventstore.BaseEvent,
unit Unit,
from time.Time,
resetInterval time.Duration,
amount uint64,
limit bool,
notifications []*AddedEventNotification,
) *AddedEvent {
return &AddedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
aggregate,
AddedEventType,
),
Unit: unit,
From: from,
ResetInterval: resetInterval,
Amount: amount,
Limit: limit,
Notifications: notifications,
changes ...QuotaChange,
) *SetEvent {
changedEvent := &SetEvent{
BaseEvent: *base,
Unit: unit,
}
for _, change := range changes {
change(changedEvent)
}
return changedEvent
}
type QuotaChange func(*SetEvent)
func ChangeAmount(amount uint64) QuotaChange {
return func(e *SetEvent) {
e.Amount = &amount
}
}
func AddedEventMapper(event *repository.Event) (eventstore.Event, error) {
e := &AddedEvent{
func ChangeLimit(limit bool) QuotaChange {
return func(e *SetEvent) {
e.Limit = &limit
}
}
func ChangeFrom(from time.Time) QuotaChange {
return func(event *SetEvent) {
event.From = &from
}
}
func ChangeResetInterval(interval time.Duration) QuotaChange {
return func(event *SetEvent) {
event.ResetInterval = &interval
}
}
func ChangeNotifications(notifications []*SetEventNotification) QuotaChange {
return func(event *SetEvent) {
event.Notifications = &notifications
}
}
func SetEventMapper(event *repository.Event) (eventstore.Event, error) {
e := &SetEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}
err := json.Unmarshal(event.Data, e)
if err != nil {
return nil, errors.ThrowInternal(err, "QUOTA-4n8vs", "unable to unmarshal quota added")
return nil, errors.ThrowInternal(err, "QUOTA-kmIpI", "unable to unmarshal quota set")
}
return e, nil

View File

@@ -5,7 +5,11 @@ import (
)
func RegisterEventMappers(es *eventstore.Eventstore) {
es.RegisterFilterEventMapper(AggregateType, AddedEventType, AddedEventMapper).
// AddedEventType is not emitted anymore.
// For ease of use, old events are directly mapped to SetEvent.
// This works, because the data structures are compatible.
es.RegisterFilterEventMapper(AggregateType, AddedEventType, SetEventMapper).
RegisterFilterEventMapper(AggregateType, SetEventType, SetEventMapper).
RegisterFilterEventMapper(AggregateType, RemovedEventType, RemovedEventMapper).
RegisterFilterEventMapper(AggregateType, NotificationDueEventType, NotificationDueEventMapper).
RegisterFilterEventMapper(AggregateType, NotifiedEventType, NotifiedEventMapper)