Repository navigation
Description
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 areall-upfront(labelled "Upfront") andno-upfront(labelled "Monthly"). Neither isupfrontormonthly, so both are rejected. - GCP cards (
frontend/src/settings.ts:140-143setpaymentId: null):settings.default_paymentis used (settings.ts:3548-3550). That is the global default select (index.html:452-454, AWS tokens, defaultall-upfront), which is nevermonthly.
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, GCPmonthly), or skips services whose card is not applicable. - A test posts the real card values from
index.htmlthrough 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).
Activity
test(settings): pin the service PUT diff against surviving mutations
fix(settings): save only the service configs and keys the user changed (
fix(settings): repair GCP rows holding an AWS payment and refuse an e…