Context
Adversarial review of PR LeanerCloud/cloud-commitments-cli#1208 (ARCH-01) swept all Azure reservation purchase bodies in providers/azure/services/{cache,compute,cosmosdb,database,managedredis,search,synapse}/client.go and found two more enum-as-string anti-patterns living in the same lines as the reservedResourceType fixes, identical in shape:
1. appliedScopeType: "Shared" (7 services)
Every purchase body sets "appliedScopeType": "Shared" as a raw literal. The Azure SDK defines a typed enum:
// armreservations@v1.1.0/constants.go:18-22
type AppliedScopeType string
const (
AppliedScopeTypeShared AppliedScopeType = "Shared"
AppliedScopeTypeSingle AppliedScopeType = "Single"
)
Same risk shape as ARCH-01: a typo in any of the 7 occurrences (e.g. lowercase "shared", plural "Shareds", capitalisation drift) would silently break the purchase request once the LeanerCloud/cloud-commitments-cli#731 role fix makes non-VM purchases reachable.
2. term: fmt.Sprintf("P%dY", termYears) (7 services)
Every purchase body builds the term string by hand. The SDK enum:
// armreservations@v1.1.0/constants.go:473-478
type ReservationTerm string
const (
ReservationTermP1Y ReservationTerm = "P1Y"
ReservationTermP3Y ReservationTerm = "P3Y"
ReservationTermP5Y ReservationTerm = "P5Y"
)
Today this is fail-loud-safe because reservations.ParseTermYears only accepts 1 or 3 and the format string mechanically produces "P1Y" / "P3Y". But if someone ever extends ParseTermYears to accept 2 years or 10 years, the format string would happily produce "P2Y" / "P10Y" — invalid enum values Azure would reject. Today the calculation is correct only by accident.
Locations to sweep
providers/azure/services/cache/client.go:326,337
providers/azure/services/compute/client.go:433,444
providers/azure/services/cosmosdb/client.go:319,330
providers/azure/services/database/client.go:324,335
providers/azure/services/managedredis/client.go:266,269
providers/azure/services/search/client.go:304,315
providers/azure/services/synapse/client.go:274,277
Recommendation
- Replace
"Shared" with string(armreservations.AppliedScopeTypeShared) across the 7 services.
- Replace
fmt.Sprintf("P%dY", termYears) with a typed helper that returns the SDK constant (e.g. add reservations.AzureReservationTerm(termYears) (armreservations.ReservationTerm, error) returning ReservationTermP1Y / ReservationTermP3Y / ReservationTermP5Y, fail-loud on anything else). Use the same helper at the 7 call sites.
- Extend the existing
TestDatabaseClient_PurchaseCommitment_CanonicalReservedResourceType / TestPurchaseCommitment_canonicalReservedResourceType regression tests to also assert appliedScopeType and term against PossibleAppliedScopeTypeValues() / PossibleReservationTermValues().
Why this matters
The whole point of ARCH-01 (PR LeanerCloud/cloud-commitments-cli#1208) and the project memory entry feedback_sdk_enum_string_literals.md is "any string literal that maps to a typed enum field MUST be the exact SDK enum member, derived from the SDK". appliedScopeType and term sit on the same map literal as the reservedResourceType that LeanerCloud/cloud-commitments-cli#1208 fixes; leaving them as raw strings is the same latent failure mode, just one PR away.
Per the closing issue (LeanerCloud/cloud-commitments-cli#1189) and the PR description, ARCH-01 is explicitly scoped to reservedResourceType. These sibling enums are a clean follow-up.
Surfaced by
PR LeanerCloud/cloud-commitments-cli#1208 adversarial review sweep.
Context
Adversarial review of PR LeanerCloud/cloud-commitments-cli#1208 (ARCH-01) swept all Azure reservation purchase bodies in
providers/azure/services/{cache,compute,cosmosdb,database,managedredis,search,synapse}/client.goand found two more enum-as-string anti-patterns living in the same lines as thereservedResourceTypefixes, identical in shape:1.
appliedScopeType: "Shared"(7 services)Every purchase body sets
"appliedScopeType": "Shared"as a raw literal. The Azure SDK defines a typed enum:Same risk shape as ARCH-01: a typo in any of the 7 occurrences (e.g. lowercase
"shared", plural"Shareds", capitalisation drift) would silently break the purchase request once the LeanerCloud/cloud-commitments-cli#731 role fix makes non-VM purchases reachable.2.
term: fmt.Sprintf("P%dY", termYears)(7 services)Every purchase body builds the term string by hand. The SDK enum:
Today this is fail-loud-safe because
reservations.ParseTermYearsonly accepts 1 or 3 and the format string mechanically produces"P1Y"/"P3Y". But if someone ever extendsParseTermYearsto accept 2 years or 10 years, the format string would happily produce"P2Y"/"P10Y"— invalid enum values Azure would reject. Today the calculation is correct only by accident.Locations to sweep
Recommendation
"Shared"withstring(armreservations.AppliedScopeTypeShared)across the 7 services.fmt.Sprintf("P%dY", termYears)with a typed helper that returns the SDK constant (e.g. addreservations.AzureReservationTerm(termYears) (armreservations.ReservationTerm, error)returningReservationTermP1Y/ReservationTermP3Y/ReservationTermP5Y, fail-loud on anything else). Use the same helper at the 7 call sites.TestDatabaseClient_PurchaseCommitment_CanonicalReservedResourceType/TestPurchaseCommitment_canonicalReservedResourceTyperegression tests to also assertappliedScopeTypeandtermagainstPossibleAppliedScopeTypeValues()/PossibleReservationTermValues().Why this matters
The whole point of ARCH-01 (PR LeanerCloud/cloud-commitments-cli#1208) and the project memory entry
feedback_sdk_enum_string_literals.mdis "any string literal that maps to a typed enum field MUST be the exact SDK enum member, derived from the SDK".appliedScopeTypeandtermsit on the same map literal as thereservedResourceTypethat LeanerCloud/cloud-commitments-cli#1208 fixes; leaving them as raw strings is the same latent failure mode, just one PR away.Out of scope for PR LeanerCloud/cloud-commitments-cli#1208
Per the closing issue (LeanerCloud/cloud-commitments-cli#1189) and the PR description, ARCH-01 is explicitly scoped to
reservedResourceType. These sibling enums are a clean follow-up.Surfaced by
PR LeanerCloud/cloud-commitments-cli#1208 adversarial review sweep.