Skip to content

feat(api): validate plan account provider matches plan provider on assignment #209

@cristim

Description

@cristim

Gap

When a user assigns accounts to a purchase plan via PUT /api/plans/:id/accounts, the handler currently accepts any account IDs without validating that each account's provider matches the plan's derived provider. Spec acceptance criterion E-4 requires this be caught early with a clear 400.

Concrete example: assigning an Azure account to a plan whose services map contains only AWS keys (e.g. "aws:ec2", "aws:rds") should fail with a message identifying the mismatched account(s) and their providers.

The frontend already enforces single-provider-per-plan, but backend validation is needed to harden the contract — a misconfigured client or direct API caller can otherwise persist a state the executor can't run.

Spec sections: specs/multi-account-execution/acceptance.md E-4; specs/multi-account-execution/api.md "PUT /api/plans/:id/accounts".

Acceptance criteria

  • Derive provider(s) from the plan's services map keys (format "provider:service", e.g. "aws:ec2"); extract the distinct provider set.
  • For each account_id in the request body: load from cloud_accounts, check provider matches the plan's provider set.
  • If mismatch: return 400 with an error message listing each offending (account.name, account.provider, expected_provider) triple. Do not partially persist.
  • If all valid: behaviour unchanged — call SetPlanAccounts (internal/config/store_postgres.go:1890-1915) and return 200.
  • Existing happy-path tests stay green; add table-driven tests for the new mismatch cases (single mismatch, multiple mismatches, valid plan).

Out of scope

  • Multi-provider plans — the structural assumption (one plan = one provider) is unchanged in this PR; the spec doesn't propose lifting it.
  • Auto-filtering of incompatible accounts on the frontend's account-picker — that's a UX nicety; the backend gate must exist independently.

References

  • Spec: specs/multi-account-execution/acceptance.md scenario E-4
  • Handler call site: internal/api/handler_accounts.go:1025-1055 (setPlanAccounts)
  • Storage: internal/config/store_postgres.go:1890-1915 (SetPlanAccounts already validates IDs exist; just lacking the provider-match check)
  • Surfaced via the gap analysis on PR docs(specs): multi-account execution draft spec #89.

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