| package patch |
|
|
| import ( |
| "fmt" |
|
|
| "github.com/openmeterio/openmeter/openmeter/subscription" |
| "github.com/openmeterio/openmeter/pkg/datetime" |
| ) |
|
|
| type PatchAddPhase struct { |
| PhaseKey string |
| CreateInput subscription.CreateSubscriptionPhaseInput |
| } |
|
|
| func (a PatchAddPhase) Op() subscription.PatchOperation { |
| return subscription.PatchOperationAdd |
| } |
|
|
| func (a PatchAddPhase) Path() subscription.SpecPath { |
| return subscription.NewPhasePath(a.PhaseKey) |
| } |
|
|
| func (a PatchAddPhase) Value() subscription.CreateSubscriptionPhaseInput { |
| return a.CreateInput |
| } |
|
|
| func (a PatchAddPhase) ValueAsAny() any { |
| return a.CreateInput |
| } |
|
|
| func (a PatchAddPhase) Validate() error { |
| if err := a.Path().Validate(); err != nil { |
| return err |
| } |
|
|
| if err := a.Op().Validate(); err != nil { |
| return err |
| } |
|
|
| if err := a.CreateInput.Validate(); err != nil { |
| return err |
| } |
|
|
| return nil |
| } |
|
|
| var _ subscription.ValuePatch[subscription.CreateSubscriptionPhaseInput] = PatchAddPhase{} |
|
|
| func (a PatchAddPhase) ApplyTo(spec *subscription.SubscriptionSpec, actx subscription.ApplyContext) error { |
| if _, exists := spec.Phases[a.PhaseKey]; exists { |
| return &subscription.PatchConflictError{Msg: fmt.Sprintf("phase %s already exists", a.PhaseKey)} |
| } |
|
|
| |
| vST, _ := a.Value().StartAfter.AddTo(spec.ActiveFrom) |
|
|
| |
| if !vST.After(actx.CurrentTime) { |
| return &subscription.PatchForbiddenError{Msg: "cannot add phase in the past"} |
| } |
|
|
| |
| if spec.ActiveTo != nil && !vST.Before(*spec.ActiveTo) { |
| return &subscription.PatchForbiddenError{Msg: "cannot add phase after the subscription ends"} |
| } |
|
|
| |
|
|
| |
| |
| |
| |
|
|
| sortedPhases := spec.GetSortedPhases() |
| var diff datetime.ISODuration |
|
|
| for i := range sortedPhases { |
| p := sortedPhases[i] |
| |
| if v, _ := p.StartAfter.AddTo(spec.ActiveFrom); !v.Before(vST) && diff.IsZero() { |
| tillNextPhase, err := p.StartAfter.Subtract(a.Value().StartAfter) |
| if err != nil { |
| return fmt.Errorf("failed to calculate difference between phases: %w", err) |
| } |
| diff, err = a.Value().Duration.Subtract(tillNextPhase) |
| if err != nil { |
| return fmt.Errorf("failed to calculate difference between phases: %w", err) |
| } |
| } |
|
|
| |
| if !diff.IsZero() { |
| sa, err := p.StartAfter.Add(diff) |
| if err != nil { |
| return fmt.Errorf("failed to adjust phase %s start time: %w", p.PhaseKey, err) |
| } |
| sortedPhases[i].StartAfter = sa |
| } |
| } |
|
|
| |
| spec.Phases[a.PhaseKey] = &subscription.SubscriptionPhaseSpec{ |
| CreateSubscriptionPhasePlanInput: a.CreateInput.CreateSubscriptionPhasePlanInput, |
| CreateSubscriptionPhaseCustomerInput: a.CreateInput.CreateSubscriptionPhaseCustomerInput, |
| ItemsByKey: make(map[string][]*subscription.SubscriptionItemSpec), |
| } |
|
|
| return nil |
| } |
|
|