File size: 2,481 Bytes
d6f631f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | package subscription
import (
"fmt"
"time"
"github.com/samber/lo"
"github.com/openmeterio/openmeter/pkg/models"
)
// The spec is the core object of Subscriptions being manipulated.
// Different resources like Patches, Addons, etc... can apply themselves to the spec.
type ApplyContext struct {
CurrentTime time.Time
}
// Things can apply themselves to the spec
type AppliesToSpec interface {
// This method should only ever be invoked by SubscriptionSpec (spec.ApplyX), so subsequent validations and logic are always guaranteed to run
// FIXME(galexi): this can be enforced by making it private, but that will require rewriting all patches & addons
ApplyTo(spec *SubscriptionSpec, actx ApplyContext) error
}
const (
// Some errors are allowed during applying individual things to the spec, but still mean the Spec as a whole is invalid
subscriptionPatchErrAttrNameAllowedDuringApplyingToSpecError = "allowed_during_applying_to_spec_error"
)
func AllowedDuringApplyingToSpecError() models.ValidationIssueOption {
return models.WithAttribute(subscriptionPatchErrAttrNameAllowedDuringApplyingToSpecError, true)
}
func NewAppliesToSpec(fn func(spec *SubscriptionSpec, actx ApplyContext) error) AppliesToSpec {
return &someAppliesToSpec{
Fn: fn,
}
}
var _ AppliesToSpec = &someAppliesToSpec{}
type someAppliesToSpec struct {
Fn func(spec *SubscriptionSpec, actx ApplyContext) error
}
func (s *someAppliesToSpec) ApplyTo(spec *SubscriptionSpec, actx ApplyContext) error {
return s.Fn(spec, actx)
}
// NewAggregateAppliesToSpec aggregates multiple applies to spec into a single applies to spec, and also validates the spec after applying all the patches
func NewAggregateAppliesToSpec(applieses []AppliesToSpec) AppliesToSpec {
return NewAppliesToSpec(func(spec *SubscriptionSpec, actx ApplyContext) error {
for i, applies := range applieses {
if err := spec.Apply(applies, actx); err != nil {
wrapError := func(err error) error {
return models.ErrorWithComponent(models.ComponentName(fmt.Sprintf("patch at idx %d", i)), err)
}
issues, err := models.AsValidationIssues(err)
if err != nil {
return wrapError(err)
}
if lo.EveryBy(issues, func(issue models.ValidationIssue) bool {
return IsValidationIssueWithBoolAttr(issue, subscriptionPatchErrAttrNameAllowedDuringApplyingToSpecError)
}) {
continue
}
// Otherwise we return with the error
return wrapError(err)
}
}
return nil
})
}
|