diff --git a/internal/api/handlers/management/plugin_quota_test.go b/internal/api/handlers/management/plugin_quota_test.go index 43f62a632..24280a663 100644 --- a/internal/api/handlers/management/plugin_quota_test.go +++ b/internal/api/handlers/management/plugin_quota_test.go @@ -45,6 +45,9 @@ func (m *mockQuotaProvider) FetchQuota(ctx context.Context, req pluginapi.QuotaF TierID: "pro-tier", }, ServerTimeOffsetMs: 42, + Summary: []pluginapi.QuotaMetric{ + {Key: "credits_used", Label: "Credits used", Value: 1740.28, Unit: "credits"}, + }, Groups: []pluginapi.QuotaGroup{ { DisplayName: "Standard Limits", @@ -181,6 +184,9 @@ func TestFetchCredentialQuota_Endpoint(t *testing.T) { if quotaResp.Subscription == nil || quotaResp.Subscription.Plan != "Pro" { t.Fatalf("unexpected subscription: %+v", quotaResp.Subscription) } + if len(quotaResp.Summary) != 1 || quotaResp.Summary[0].Key != "credits_used" || quotaResp.Summary[0].Value != 1740.28 { + t.Fatalf("unexpected quota summary: %+v", quotaResp.Summary) + } if len(quotaResp.Groups) != 1 || len(quotaResp.Groups[0].Buckets) != 1 { t.Fatalf("unexpected quota groups: %+v", quotaResp.Groups) } diff --git a/sdk/pluginapi/types.go b/sdk/pluginapi/types.go index 55bb3b733..4813a7bfb 100644 --- a/sdk/pluginapi/types.go +++ b/sdk/pluginapi/types.go @@ -1601,9 +1601,21 @@ func (b *QuotaBucket) UnmarshalJSON(data []byte) error { return nil } +// QuotaMetric is a provider-defined, bounded numeric account summary for management UI rendering. +// Format is "number" or "currency"; Currency is an ISO 4217 code when Format is "currency". +type QuotaMetric struct { + Key string `json:"key"` + Label string `json:"label"` + Value float64 `json:"value"` + Unit string `json:"unit,omitempty"` + Format string `json:"format,omitempty"` + Currency string `json:"currency,omitempty"` +} + // QuotaFetchResponse carries normalized quota information for management UI rendering. type QuotaFetchResponse struct { Subscription *QuotaSubscription `json:"subscription,omitempty"` + Summary []QuotaMetric `json:"summary,omitempty"` ServerTimeOffsetMs int64 `json:"serverTimeOffsetMs,omitempty"` Groups []QuotaGroup `json:"groups,omitempty"` } diff --git a/sdk/pluginapi/types_test.go b/sdk/pluginapi/types_test.go index 49c3dbab2..ac35abc63 100644 --- a/sdk/pluginapi/types_test.go +++ b/sdk/pluginapi/types_test.go @@ -662,6 +662,10 @@ func TestQuotaPayloadJSON(t *testing.T) { // Test camelCase input camelJSON := []byte(`{ "subscription": {"plan":"Pro","tierName":"Tier 1","tierId":"t-1"}, + "summary": [ + {"key":"credits_used","label":"Credits used","value":1740.28,"unit":"credits","format":"number"}, + {"key":"charged","label":"Charged","value":29.61,"format":"currency","currency":"USD"} + ], "serverTimeOffsetMs": 100, "groups": [ { @@ -680,6 +684,9 @@ func TestQuotaPayloadJSON(t *testing.T) { if respCamel.Subscription == nil || respCamel.Subscription.TierName != "Tier 1" || respCamel.Subscription.TierID != "t-1" { t.Fatalf("unexpected camel subscription: %+v", respCamel.Subscription) } + if len(respCamel.Summary) != 2 || respCamel.Summary[0].Key != "credits_used" || respCamel.Summary[0].Value != 1740.28 || respCamel.Summary[1].Currency != "USD" { + t.Fatalf("unexpected camel summary: %+v", respCamel.Summary) + } if respCamel.ServerTimeOffsetMs != 100 { t.Fatalf("unexpected server time offset: %d", respCamel.ServerTimeOffsetMs) }