Skip to content

Commit

Permalink
CORE-2016: added duplicate checks for plan quota defaults and plan rates
Browse files Browse the repository at this point in the history
  • Loading branch information
slr71 committed Oct 30, 2024
1 parent c69bb51 commit 5d84e2c
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 12 deletions.
56 changes: 49 additions & 7 deletions internal/controllers/plans.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,15 @@ func (s Server) AddPlan(ctx echo.Context) error {
// Begin a transaction.
return s.GORMDB.Transaction(func(tx *gorm.DB) error {
dbPlan := plan.ToDBModel()

// Make sure that a plan with the same name doesn't already exist.
planNameExists, err := db.CheckPlanNameExistence(context, tx, plan.Name)
if err != nil {
return model.Error(ctx, err.Error(), http.StatusInternalServerError)
} else if planNameExists {
return model.Error(ctx, fmt.Sprintf("a plan named `%s` already exists", plan.Name), http.StatusBadRequest)
}

// Look up each resource type and update it in the struct.
for i, planQuotaDefault := range dbPlan.PlanQuotaDefaults {
resourceType, err := db.GetResourceTypeByName(context, tx, planQuotaDefault.ResourceType.Name)
Expand All @@ -147,7 +156,7 @@ func (s Server) AddPlan(ctx echo.Context) error {
log.Debugf("translated plan: %+v", dbPlan)

// Add the plan to the database.
err := tx.WithContext(context).Create(&dbPlan).Error
err = tx.WithContext(context).Create(&dbPlan).Error
if err != nil {
return model.Error(ctx, err.Error(), http.StatusInternalServerError)
}
Expand Down Expand Up @@ -310,16 +319,19 @@ func (s Server) AddPlanQuotaDefaults(ctx echo.Context) error {
if err = ctx.Bind(&planQuotaDefaultList); err != nil {
return model.Error(ctx, err.Error(), http.StatusBadRequest)
}
if err = planQuotaDefaultList.Validate(); err != nil {
return model.Error(ctx, err.Error(), http.StatusBadRequest)
}

// Begin a transaction.
return s.GORMDB.Transaction(func(tx *gorm.DB) error {
context := ctx.Request().Context()

// Verify that the plan exists.
exists, err := db.CheckPlanExistence(context, tx, planID)
plan, err := db.GetPlanByID(context, tx, planID)
if err != nil {
return model.Error(ctx, err.Error(), http.StatusInternalServerError)
} else if !exists {
} else if plan == nil {
msg := fmt.Sprintf("plan ID %s not found", planID)
return model.Error(ctx, msg, http.StatusNotFound)
}
Expand All @@ -345,14 +357,32 @@ func (s Server) AddPlanQuotaDefaults(ctx echo.Context) error {
planQuotaDefaults[i].ResourceType = *rt
}

// Compare the plan quota defaults to existing quota defaults to check for duplicates.
existingPlanQuotaDefaults := make(map[httpmodel.PlanQuotaDefaultKey]bool)
for _, pqd := range plan.PlanQuotaDefaults {
key := httpmodel.KeyFromPlanQuotaDefault(pqd)
existingPlanQuotaDefaults[key] = true
}
for _, pqd := range planQuotaDefaults {
key := httpmodel.KeyFromPlanQuotaDefault(pqd)
if existingPlanQuotaDefaults[key] {
msg := fmt.Sprintf(
"plan quota default for resource type %s with effective date %s already exists",
pqd.ResourceType.Name,
pqd.EffectiveDate,
)
return model.Error(ctx, msg, http.StatusBadRequest)
}
}

// Save the list of plan quota defaults.
err = db.SavePlanQuotaDefaults(context, tx, planQuotaDefaults)
if err != nil {
return model.Error(ctx, err.Error(), http.StatusInternalServerError)
}

// Look up the plan with the new plan quota defaults included and return it in the response.
plan, err := db.GetPlanByID(context, tx, planID)
plan, err = db.GetPlanByID(context, tx, planID)
if err != nil {
return model.Error(ctx, err.Error(), http.StatusInternalServerError)
} else if plan == nil {
Expand Down Expand Up @@ -407,17 +437,29 @@ func (s Server) AddPlanRates(ctx echo.Context) error {
context := ctx.Request().Context()

// Verify that the plan existws.
exists, err := db.CheckPlanExistence(context, tx, planID)
plan, err := db.GetPlanByID(context, tx, planID)
if err != nil {
return model.Error(ctx, err.Error(), http.StatusInternalServerError)
} else if !exists {
} else if plan == nil {
msg := fmt.Sprintf("plan ID %s not found", planID)
return model.Error(ctx, msg, http.StatusNotFound)
}

// Convert the list of plan rates to the corresponding DB model.
planRates := planRateList.ToDBModel()

// Verify that none of the incoming plan rates duplicate existing plan rates.
existingPlanRates := make(map[int64]bool)
for _, pr := range plan.PlanRates {
existingPlanRates[pr.EffectiveDate.UnixMilli()] = true
}
for _, pr := range planRates {
if existingPlanRates[pr.EffectiveDate.UnixMilli()] {
msg := fmt.Sprintf("plan rate with effective date %s already exists", pr.EffectiveDate)
return model.Error(ctx, msg, http.StatusBadRequest)
}
}

// Plug the plan ID into each of the plan rates.
for i := range planRates {
planRates[i].PlanID = &planID
Expand All @@ -430,7 +472,7 @@ func (s Server) AddPlanRates(ctx echo.Context) error {
}

// Look up the plan with the new plan quota defaults included and return it in the response.
plan, err := db.GetPlanByID(context, tx, planID)
plan, err = db.GetPlanByID(context, tx, planID)
if err != nil {
return model.Error(ctx, err.Error(), http.StatusInternalServerError)
} else if plan == nil {
Expand Down
41 changes: 37 additions & 4 deletions internal/db/plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ func GetPlan(ctx context.Context, db *gorm.DB, planName string) (*model.Plan, er
err = db.
WithContext(ctx).
Where("name=?", planName).
Preload("PlanQuotaDefaults").
Preload("PlanQuotaDefaults", func(db *gorm.DB) *gorm.DB {
return db.
Joins("INNER JOIN resource_types ON plan_quota_defaults.resource_type_id = resource_types.id").
Order("plan_quota_defaults.effective_date asc, resource_types.name asc")
}).
Preload("PlanQuotaDefaults.ResourceType").
Preload("PlanRates", func(db *gorm.DB) *gorm.DB {
return db.Order("effective_date asc")
Expand All @@ -36,7 +40,28 @@ func GetPlan(ctx context.Context, db *gorm.DB, planName string) (*model.Plan, er
return &plan, nil
}

// PlanExists determines whether or not a subscription plan with the given identifier exists.
// CheckPlanNameExistence determines whether or not a subscription plan with a given name exists.
func CheckPlanNameExistence(ctx context.Context, db *gorm.DB, planName string) (bool, error) {
wrapMsg := fmt.Sprintf("unable to look up plan Name `%s`", planName)
var err error

// Query the database.
var exists bool
var plan = model.Plan{}
err = db.Model(plan).
Select("count(*) > 0").
Where("name = ?", planName).
Find(&exists).
Error

// Return the result.
if err != nil {
return false, errors.Wrap(err, wrapMsg)
}
return exists, nil
}

// CheckPlanExistence determines whether or not a subscription plan with the given identifier exists.
func CheckPlanExistence(ctx context.Context, db *gorm.DB, planID string) (bool, error) {
wrapMsg := fmt.Sprintf("unable to look up plan ID '%s'", planID)
var err error
Expand Down Expand Up @@ -64,7 +89,11 @@ func GetPlanByID(ctx context.Context, db *gorm.DB, planID string) (*model.Plan,
plan := model.Plan{ID: &planID}
err = db.
WithContext(ctx).
Preload("PlanQuotaDefaults").
Preload("PlanQuotaDefaults", func(db *gorm.DB) *gorm.DB {
return db.
Joins("INNER JOIN resource_types ON plan_quota_defaults.resource_type_id = resource_types.id").
Order("plan_quota_defaults.effective_date asc, resource_types.name asc")
}).
Preload("PlanQuotaDefaults.ResourceType").
Preload("PlanRates", func(db *gorm.DB) *gorm.DB {
return db.Order("effective_date asc")
Expand Down Expand Up @@ -131,7 +160,11 @@ func ListPlans(ctx context.Context, db *gorm.DB) ([]*model.Plan, error) {
var plans []*model.Plan
err = db.
WithContext(ctx).
Preload("PlanQuotaDefaults").
Preload("PlanQuotaDefaults", func(db *gorm.DB) *gorm.DB {
return db.
Joins("INNER JOIN resource_types ON plan_quota_defaults.resource_type_id = resource_types.id").
Order("plan_quota_defaults.effective_date asc, resource_types.name asc")
}).
Preload("PlanQuotaDefaults.ResourceType").
Preload("PlanRates", func(db *gorm.DB) *gorm.DB {
return db.Order("effective_date asc")
Expand Down
53 changes: 52 additions & 1 deletion internal/httpmodel/new_plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,28 @@ import (
// Note: the names in the comments may deviate a bit from the actual structure names in order to avoid producing
// confusing Swagger docs.

// A plan quota default key.
type PlanQuotaDefaultKey struct {
ResourceTypeName string
EffectiveDate int64
}

// KeyFromPlanQuotaDefault generates a plan quota default key from a PlanQuotaDefault object.
func KeyFromPlanQuotaDefault(planQuotaDefault model.PlanQuotaDefault) PlanQuotaDefaultKey {
return PlanQuotaDefaultKey{
ResourceTypeName: planQuotaDefault.ResourceType.Name,
EffectiveDate: planQuotaDefault.EffectiveDate.UnixMilli(),
}
}

// KeyFromNewPlanQuotaDefault generates a plan quota defual key from a NewPlanQuotaDefaultObject.
func KeyFromNewPlanQuotaDefault(newPlanQuotaDefault NewPlanQuotaDefault) PlanQuotaDefaultKey {
return PlanQuotaDefaultKey{
ResourceTypeName: newPlanQuotaDefault.ResourceType.Name,
EffectiveDate: newPlanQuotaDefault.EffectiveDate.UnixMilli(),
}
}

// NewPlan
//
// swagger:model
Expand Down Expand Up @@ -52,14 +74,33 @@ func (p NewPlan) Validate() error {
}
}

// Validate each of the plan rates.
// Verify that the resource type and effective date are unique for all of the plan quota defaults.
uniquePlanQuotaDefaults := make(map[PlanQuotaDefaultKey]bool)
for _, d := range p.PlanQuotaDefaults {
key := KeyFromNewPlanQuotaDefault(d)
if uniquePlanQuotaDefaults[key] {
return fmt.Errorf("multiple plan quota defaults found with the same resource type and effective date")
}
uniquePlanQuotaDefaults[key] = true
}

// Verify each of the plan rates.
for _, pr := range p.PlanRates {
err = pr.Validate()
if err != nil {
return err
}
}

// Verify that the effective date it unique for all of the plan rates.
uniquePlanRates := make(map[int64]bool)
for _, pr := range p.PlanRates {
if uniquePlanRates[pr.EffectiveDate.UnixMilli()] {
return fmt.Errorf("multiple plan rates found with the same effective date")
}
uniquePlanRates[pr.EffectiveDate.UnixMilli()] = true
}

return nil
}

Expand Down Expand Up @@ -108,6 +149,16 @@ func (pqdl NewPlanQuotaDefaultList) Validate() error {
}
}

// Verify that the resource type and effective date are unique for all of the plan quota defaults.
uniquePlanQuotaDefaults := make(map[PlanQuotaDefaultKey]bool)
for _, d := range pqdl.PlanQuotaDefaults {
key := KeyFromNewPlanQuotaDefault(d)
if uniquePlanQuotaDefaults[key] {
return fmt.Errorf("multiple plan quota defaults found with the same resource type and effective date")
}
uniquePlanQuotaDefaults[key] = true
}

return nil
}

Expand Down

0 comments on commit 5d84e2c

Please sign in to comment.