Skip to content

Normalize 'savings-plans' vs 'savingsplans' identifier across frontend + backend #85

@cristim

Description

@cristim

Problem

Savings Plans is identified inconsistently across the stack:

Layer File Identifier
Frontend dropdown frontend/src/index.html:112,703 "savingsplans"
Frontend SERVICE_FIELDS frontend/src/settings.ts:32 "savingsplans"
Frontend commitmentConfigs frontend/src/commitmentOptions.ts:58 "savingsplans" (after #63)
Backend canonical pkg/common/types.go:49 "savings-plans"
Backend CLI flag cmd/main.go:92 "savingsplans"
Backend purchase normaliser internal/purchase/execution.go:384 accepts both via case "savings-plans", "savingsplans":

The frontend persists "savingsplans" to the service_configs.service column. The Go ServiceType constant is "savings-plans". The two sides only line up because execution.go does a dual-case branch on the way to common.ServiceSavingsPlans. Other services don't have the same compatibility shim, so it's load-bearing for Savings Plans alone.

Why it matters

  • Any new code path that compares ServiceConfig.service against common.ServiceSavingsPlans directly (without the normaliser) will silently miss Savings Plans rows.
  • Adding a third identifier form (e.g., "savings_plans") anywhere is now extra-cheap and an easy oversight.
  • The execution.go case "savings-plans", "savingsplans": branch suggests historical drift — pinning a single canonical removes the foot-gun.

Out of scope (handled in #63)

#63 addressed a downstream consequence — the frontend commitmentOptions.ts had registered the AWS Savings Plans config under "savings-plans" while every other frontend call site used "savingsplans", causing all lookups to fall through to _default. That's now fixed within the frontend's own naming scheme; this issue tracks the broader frontend↔backend split.

Suggested approach

Pick one canonical and migrate the other side, with care for persisted rows:

  • Option A — converge on "savings-plans" (backend canonical): requires a one-shot SQL migration to rename service_configs.service rows from "savingsplans""savings-plans", plus updating the dropdown option values, SERVICE_FIELDS, and commitmentConfigs. Aligns with the pkg/common/types.go constant.
  • Option B — converge on "savingsplans" (frontend canonical): change pkg/common/types.go:49 to "savingsplans" (Go-side enum-style change is safer than a DB migration), drop the case "savings-plans" branch from execution.go:384, audit Go callers for hardcoded "savings-plans" strings.

Either option ends with a single normaliser at the API boundary and removes the dual-case branch.

Acceptance

  • One canonical service identifier for Savings Plans across frontend + backend + DB.
  • The dual-case branch in execution.go:384 removed.
  • A regression test that fails if a future commit reintroduces a divergent identifier.

Discovered while sweeping CodeRabbit nitpicks in #63.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions