Skip to content

fix(settings): Save sends Azure/GCP payment values the service-config handler rejects #545

Closed

Description

@cristim

Summary

The Settings page Save sends a per-service config PUT for every Azure and GCP service with a payment value the service-config handler rejects with 400. saveGlobalSettings awaits Promise.all of all service PUTs, so Save probably reports "Failed to save settings" on every save, even though the global settings PUT before it already succeeded.

Mismatch (origin/main d2cdc60)

Backend accepts, per provider (internal/config/validation.go:47-51):

  • aws: no-upfront, partial-upfront, all-upfront
  • azure: upfront, monthly
  • gcp: monthly

ServiceConfig.validatePayment (internal/config/validation.go:495-514) rejects any other token, called from updateServiceConfig (internal/api/handler_config.go:293, cfg.Validate() at ~:322) with a 400, e.g. invalid payment option: all-upfront (valid for gcp: monthly).

Frontend sends:

  • Azure cards (frontend/src/index.html:625,630,635,640,645): options are all-upfront (labelled "Upfront") and no-upfront (labelled "Monthly"). Neither is upfront or monthly, so both are rejected.
  • GCP cards (frontend/src/settings.ts:140-143 set paymentId: null): settings.default_payment is used (settings.ts:3548-3550). That is the global default select (index.html:452-454, AWS tokens, default all-upfront), which is never monthly.

SERVICE_FIELDS is iterated unconditionally (settings.ts:3545), so the Azure/GCP rows are sent whether or not their card is shown. I did not check whether the card is hidden when the provider is unselected; the values are read from the DOM either way.

Evidence

A probe that fed the real index.html Azure/GCP card values through the real handler had all 9 Azure/GCP services rejected (found in the independent review of #538).

Not confirmed

Not reproduced in a real browser. Whether the toast shows an error on each real Save, and the exact state left behind, is inferred from the code: api.updateConfig(settings) has already succeeded at settings.ts:3526, then Promise.all rejects at :3595, so snapshotAllFields() and updateDirtyMarkers() (:3597-3598) are skipped and the caches and dirty snapshot stay partly updated.

Acceptance

  • Save sends only payment values valid for the row's provider (Azure upfront/monthly, GCP monthly), or skips services whose card is not applicable.
  • A test posts the real card values from index.html through the real validator.
  • One failed service PUT must not make Save look like a total failure: use Promise.allSettled, report which services failed, and still refresh the snapshot for the ones that saved.

Related

#522, PR #538 (found in its review), #539. Distinct from #277 (purchase modal offers monthly for AWS).

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