Skip to content

Commit

Permalink
CORE-2016: updated the subscription endpoints to support plan rates a…
Browse files Browse the repository at this point in the history
…nd multiple scheduled plan quota default updates
  • Loading branch information
slr71 committed Oct 29, 2024
1 parent 9ec70b4 commit 4db2914
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 15 deletions.
1 change: 1 addition & 0 deletions internal/controllers/admins.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ func (s Server) GetAllActiveSubscriptions(ctx echo.Context) error {
Preload("Quotas.ResourceType").
Preload("Usages").
Preload("Usages.ResourceType").
Preload("PlanRate").
Where(
s.GORMDB.WithContext(context).
Where("CURRENT_TIMESTAMP BETWEEN subscriptions.effective_start_date AND subscriptions.effective_end_date").
Expand Down
28 changes: 23 additions & 5 deletions internal/db/subscriptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,29 @@ import (
"gorm.io/gorm/clause"
)

// QuotasFromPlan generates a set of quotas from the plan quota defaults in a plan. This function assumes that the
// given plan already contains the plan quota defaults.
// QuotasFromPlan generates a set of quotas from the plan quota defaults in a plan.
func QuotasFromPlan(plan *model.Plan, periods int32) []model.Quota {
result := make([]model.Quota, len(plan.PlanQuotaDefaults))
for i, quotaDefault := range plan.PlanQuotaDefaults {

// Get the active plan quota defaults from the plan.
pqds := plan.GetDefaultQuotaValues()

// Build the array of quotas.
result := make([]model.Quota, len(pqds))

// Populate the quotas.
currentIndex := 0
for _, quotaDefault := range pqds {
quotaValue := quotaDefault.QuotaValue
if quotaDefault.ResourceType.Consumable {
quotaValue *= float64(periods)
}
result[i] = model.Quota{
result[currentIndex] = model.Quota{
Quota: quotaValue,
ResourceTypeID: quotaDefault.ResourceTypeID,
}
currentIndex++
}

return result
}

Expand All @@ -36,6 +45,12 @@ func SubscribeUserToPlan(
wrapMsg := "unable to add user plan"
var err error

// Look up the active plan rate.
planRate, err := plan.GetActivePlanRate()
if err != nil {
return nil, errors.Wrap(err, wrapMsg)
}

// Define the user plan.
effectiveStartDate := time.Now()
effectiveEndDate := opts.GetEndDate(effectiveStartDate)
Expand All @@ -46,6 +61,7 @@ func SubscribeUserToPlan(
PlanID: plan.ID,
Quotas: QuotasFromPlan(plan, opts.GetPeriods()),
Paid: opts.IsPaid(),
PlanRateID: planRate.ID,
}
err = db.WithContext(ctx).Create(&subscription).Error
if err != nil {
Expand Down Expand Up @@ -148,6 +164,7 @@ func GetSubscriptionDetails(ctx context.Context, db *gorm.DB, subscriptionID str
Preload("Quotas.ResourceType").
Preload("Usages").
Preload("Usages.ResourceType").
Preload("PlanRate").
Where("id = ?", subscriptionID).
First(&subscription).
Error
Expand Down Expand Up @@ -201,6 +218,7 @@ func ListSubscriptions(ctx context.Context, db *gorm.DB, params *SubscriptionLis
Preload("Quotas.ResourceType").
Preload("Usages").
Preload("Usages.ResourceType").
Preload("PlanRate").
Where(
db.Where("CURRENT_TIMESTAMP BETWEEN subscriptions.effective_start_date AND subscriptions.effective_end_date").
Or("CURRENT_TIMESTAMP > subscriptions.effective_start_date AND subscriptions.effective_end_date IS NULL"),
Expand Down
62 changes: 52 additions & 10 deletions internal/model/plan.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package model

import (
"fmt"
"time"
)

Expand Down Expand Up @@ -28,30 +29,65 @@ type Plan struct {
PlanRates []PlanRate `json:"plan_rates,omitempty"`
}

// GetCurrentRate returns the current rate associated with the plan.
func (p *Plan) GetCurrentRate() float64 {
var rate float64
currentDate := time.Now()
for _, planRate := range p.PlanRates {
if !planRate.EffectiveDate.After(currentDate) {
rate = planRate.Rate
// Returns the currently active rate for a subscription plan. The active plan rate is the plan with the most recent
// effective timestamp that occurs at or befor the curren time. This function assumes that the plan quota defaults are
// sorted in ascending order by effective date.
func (p *Plan) GetActivePlanRate() (*PlanRate, error) {
currentTime := time.Now()

// Find the active plan rate.
var activePlanRate *PlanRate
for _, pr := range p.PlanRates {
if pr.EffectiveDate.After(currentTime) {
break
}
activePlanRate = &pr
}
return rate

// It's an error for a plan not to have an active rate.
if activePlanRate == nil {
return nil, fmt.Errorf("no active rate found for subscription plan %s", *p.ID)
}

return activePlanRate, nil
}

// GetDefaultQuotaValue returns the default quota value associated with the resource type with the given name.
// GetDefaultQuotaValue returns the default quota value associated with the resource type with the given name. This
// funciton assumes that the plan quota defaults ar sorted in ascending order by effective date.
func (p *Plan) GetDefaultQuotaValue(resourcetypeName string) float64 {
currentTime := time.Now()

// Find the active plan quota default value for the given resource type.
var value float64
for _, quotaDefault := range p.PlanQuotaDefaults {
if quotaDefault.EffectiveDate.After(currentTime) {
break
}
if quotaDefault.ResourceType.Name == resourcetypeName {
value = quotaDefault.QuotaValue
break
}
}

return value
}

// GetActiveQuotaValues returns the active quota values for a plan. This function assumes that the plan quota defaults
// are sorted in ascending order by effective date.
func (p *Plan) GetDefaultQuotaValues() map[string]*PlanQuotaDefault {
currentTime := time.Now()

// Find the active plan quota defaults for each resource type.
result := make(map[string]*PlanQuotaDefault)
for _, planQuotaDefault := range p.PlanQuotaDefaults {
if planQuotaDefault.EffectiveDate.After(currentTime) {
break
}
result[planQuotaDefault.ResourceType.Name] = &planQuotaDefault
}

return result
}

// PlanQuotaDefault define the structure for an Api Plan and Quota.
type PlanQuotaDefault struct {
// The plan quota default identifier
Expand Down Expand Up @@ -135,6 +171,12 @@ type Subscription struct {

// True if the user paid for the subscription.
Paid bool `json:"paid"`

// The ID of the plan rate at the time the subscription was created.
PlanRateID *string `gorm:"type:uuid;not null" json:"-"`

// The plan rate at the time the subscription was created.
PlanRate *PlanRate `json:"plan_rate,omitempty"`
}

// GetCurrentUsageValue returns the current usage value for the resource type with the given resource type ID. Be
Expand Down

0 comments on commit 4db2914

Please sign in to comment.