Răsfoiți Sursa

Add metric name to usage response

Mauricio Araujo 2 ani în urmă
părinte
comite
59a0e185f7

+ 2 - 3
api/server/handlers/billing/plan.go

@@ -204,12 +204,11 @@ func (c *ListCustomerUsageHandler) ServeHTTP(w http.ResponseWriter, r *http.Requ
 		return
 	}
 
-	credits, err := c.Config().BillingManager.MetronomeClient.ListCustomerUsage(ctx, proj.UsageID, req.StartingOn, req.EndingBefore, req.WindowSize, req.CurrentPeriod)
+	usage, err := c.Config().BillingManager.MetronomeClient.ListCustomerUsage(ctx, proj.UsageID, req.StartingOn, req.EndingBefore, req.WindowSize, req.CurrentPeriod)
 	if err != nil {
 		err := telemetry.Error(ctx, span, err, "error listing customer usage")
 		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
 		return
 	}
-
-	c.WriteResult(w, r, credits)
+	c.WriteResult(w, r, usage)
 }

+ 10 - 4
api/types/billing_metronome.go

@@ -83,13 +83,19 @@ type ListCustomerUsageRequest struct {
 	CustomerID       uuid.UUID `json:"customer_id"`
 	BillableMetricID uuid.UUID `json:"billable_metric_id"`
 	WindowSize       string    `json:"window_size"`
-	StartingOn       string    `json:"starting_on"`
-	EndingBefore     string    `json:"ending_before"`
-	CurrentPeriod    bool      `json:"current_period"`
+	StartingOn       string    `json:"starting_on,omitempty"`
+	EndingBefore     string    `json:"ending_before,omitempty"`
+	CurrentPeriod    bool      `json:"current_period,omitempty"`
 }
 
-// Usage is the usage of a customer
+// Usage is the aggregated usage for a customer
 type Usage struct {
+	MetricName   string                `json:"metric_name"`
+	UsageMetrics []CustomerUsageMetric `json:"usage_metrics"`
+}
+
+// CustomerUsageMetric is a metric representing usage for a customer
+type CustomerUsageMetric struct {
 	StartingOn   string  `json:"starting_on"`
 	EndingBefore string  `json:"ending_before"`
 	Value        float64 `json:"value"`

+ 9 - 3
dashboard/src/lib/billing/types.tsx

@@ -27,14 +27,20 @@ export const Plan = z.object({
   trial_info: Trial,
 });
 
-export type UsageList = Usage[];
-export type Usage = z.infer<typeof Usage>;
-export const Usage = z.object({
+export type UsageMetric = z.infer<typeof UsageMetricValidator>;
+export const UsageMetricValidator = z.object({
   starting_on: z.string(),
   ending_on: z.string(),
   value: z.number(),
 });
 
+export type UsageList = Usage[];
+export type Usage = z.infer<typeof UsageValidator>;
+export const UsageValidator = z.object({
+  metric_name: z.string(),
+  usage_metrics: z.array(UsageMetricValidator),
+});
+
 export type CreditGrants = z.infer<typeof CreditGrantsValidator>;
 export const CreditGrantsValidator = z.object({
   granted_credits: z.number(),

+ 2 - 2
dashboard/src/lib/hooks/useStripe.tsx

@@ -7,7 +7,7 @@ import {
   CreditGrantsValidator,
   PaymentMethodValidator,
   Plan,
-  Usage,
+  UsageValidator,
   UsageList,
   type CreditGrants,
   type PaymentMethod,
@@ -334,7 +334,7 @@ export const useCustomerUsage = (): TGetUsage => {
           project_id: currentProject?.id,
         }
       );
-      const usage = Usage.array().parse(res.data);
+      const usage = UsageValidator.array().parse(res.data);
       return usage;
     }
   );

+ 12 - 11
internal/billing/metronome.go

@@ -24,7 +24,7 @@ const (
 // MetronomeClient is the client used to call the Metronome API
 type MetronomeClient struct {
 	ApiKey               string
-	billableMetricIDs    []uuid.UUID
+	billableMetrics      []types.BillableMetric
 	PorterCloudPlanID    uuid.UUID
 	PorterStandardPlanID uuid.UUID
 }
@@ -195,14 +195,14 @@ func (m MetronomeClient) EndCustomerPlan(ctx context.Context, customerID uuid.UU
 
 // ListCustomerCredits will return the total number of credits for the customer
 func (m MetronomeClient) ListCustomerCredits(ctx context.Context, customerID uuid.UUID) (credits types.ListCreditGrantsResponse, err error) {
-	ctx, span := telemetry.NewSpan(ctx, "list-customer-usage")
+	ctx, span := telemetry.NewSpan(ctx, "list-customer-credits")
 	defer span.End()
 
 	if customerID == uuid.Nil {
 		return credits, telemetry.Error(ctx, span, err, "customer id empty")
 	}
 
-	path := "usage/groups"
+	path := "credits/listGrants"
 
 	req := types.ListCreditGrantsRequest{
 		CustomerIDs: []uuid.UUID{
@@ -266,7 +266,7 @@ func (m MetronomeClient) ListCustomerUsage(ctx context.Context, customerID uuid.
 		return usage, telemetry.Error(ctx, span, err, "customer id empty")
 	}
 
-	if len(m.billableMetricIDs) == 0 {
+	if len(m.billableMetrics) == 0 {
 		billableMetrics, err := m.listBillableMetricIDs(ctx, customerID)
 		if err != nil {
 			return nil, telemetry.Error(ctx, span, err, "failed to list billable metrics")
@@ -277,9 +277,7 @@ func (m MetronomeClient) ListCustomerUsage(ctx context.Context, customerID uuid.
 		)
 
 		// Cache billable metric ids for future calls
-		for _, billableMetricID := range billableMetrics {
-			m.billableMetricIDs = append(m.billableMetricIDs, billableMetricID.ID)
-		}
+		m.billableMetrics = append(m.billableMetrics, billableMetrics...)
 	}
 
 	path := "usage/groups"
@@ -292,22 +290,25 @@ func (m MetronomeClient) ListCustomerUsage(ctx context.Context, customerID uuid.
 		CurrentPeriod: currentPeriod,
 	}
 
-	for _, billableMetric := range m.billableMetricIDs {
+	for _, billableMetric := range m.billableMetrics {
 		telemetry.WithAttributes(span,
 			telemetry.AttributeKV{Key: "billable-metric-id", Value: billableMetric.ID},
 		)
 
 		var result struct {
-			Data []types.Usage `json:"data"`
+			Data []types.CustomerUsageMetric `json:"data"`
 		}
 
-		baseReq.BillableMetricID = billableMetric
+		baseReq.BillableMetricID = billableMetric.ID
 		_, err = m.do(http.MethodPost, path, baseReq, &result)
 		if err != nil {
 			return usage, telemetry.Error(ctx, span, err, "failed to get customer usage")
 		}
 
-		usage = append(usage, result.Data...)
+		usage = append(usage, types.Usage{
+			MetricName:   billableMetric.Name,
+			UsageMetrics: result.Data,
+		})
 	}
 
 	return usage, nil