store.QuotaVersion embeds store.Quota, and Quota declares a
value-receiver MarshalJSON. Go promotes that method to the embedding
struct, and QuotaVersion declares none of its own — so json.Marshal of a
QuotaVersion emits only the Quota wire shape and silently drops every field
the version type adds.
Reproduction:
v := store.QuotaVersion{
Quota: store.Quota{ID: "abc", Provider: "aws", Name: "n"},
RootID: "abc",
VersionRowID: "0197f0a2-1c3d-7e00-9a1b-2c3d4e5f6a7b",
SupersededBy: &sup,
}
b, _ := json.Marshal(v)
Output — no rootId, no versionRowId, no supersededBy:
{"id":"abc","provider":"aws","accountId":"","accountName":null,"region":"",
"serviceCode":"","serviceName":null,"quotaCode":"","name":"n",
"description":null,"unit":null,"value":null,"defaultValue":null,
"adjustable":false,"globalQuota":false,"appliedLevel":null,
"discoveredAt":"","discoveredBy":"","attributes":{}}
Six fields are lost: versionRowId, rootId, previousVersionId,
supersededBy, verifiedAt, verifiedBy.
This matters most for the version chain, which is the reason the separate
quotas table keeps history at all. Without supersededBy a consumer cannot
tell which row is current, and without previousVersionId the chain cannot be
ordered except by discoveredAt. Any JSON output of GetQuotaVersions is
affected — disco history for a quota, and any downstream consumer.
store.ResourceVersion has the identical defect for the same reason, so a fix
should probably cover both rather than just this one.
Suggested fix: give QuotaVersion (and ResourceVersion) its own
MarshalJSON that starts from the embedded type's output and adds the chain
fields, rather than relying on promotion.
disco-saas currently works around this by assembling the wire object itself
(marshalQuotaVersion in internal/http/v1_quota_handlers.go), guarded by a
test that logs when the workaround becomes redundant. That wrapper can be
deleted once this is fixed.
store.QuotaVersionembedsstore.Quota, andQuotadeclares avalue-receiver
MarshalJSON. Go promotes that method to the embeddingstruct, and
QuotaVersiondeclares none of its own — sojson.Marshalof aQuotaVersionemits only theQuotawire shape and silently drops every fieldthe version type adds.
Reproduction:
Output — no
rootId, noversionRowId, nosupersededBy:Six fields are lost:
versionRowId,rootId,previousVersionId,supersededBy,verifiedAt,verifiedBy.This matters most for the version chain, which is the reason the separate
quotastable keeps history at all. WithoutsupersededBya consumer cannottell which row is current, and without
previousVersionIdthe chain cannot beordered except by
discoveredAt. Any JSON output ofGetQuotaVersionsisaffected —
disco historyfor a quota, and any downstream consumer.store.ResourceVersionhas the identical defect for the same reason, so a fixshould probably cover both rather than just this one.
Suggested fix: give
QuotaVersion(andResourceVersion) its ownMarshalJSONthat starts from the embedded type's output and adds the chainfields, rather than relying on promotion.
disco-saas currently works around this by assembling the wire object itself
(
marshalQuotaVersionininternal/http/v1_quota_handlers.go), guarded by atest that logs when the workaround becomes redundant. That wrapper can be
deleted once this is fixed.