|
|
@@ -120,7 +120,7 @@ func (m MetronomeClient) createCustomer(ctx context.Context, userEmail string, p
|
|
|
Data types.Customer `json:"data"`
|
|
|
}
|
|
|
|
|
|
- _, err = m.do(http.MethodPost, path, customer, &result)
|
|
|
+ _, err = m.do(http.MethodPost, path, "", customer, &result)
|
|
|
if err != nil {
|
|
|
return customerID, telemetry.Error(ctx, span, err, "error creating customer")
|
|
|
}
|
|
|
@@ -160,7 +160,7 @@ func (m MetronomeClient) addCustomerPlan(ctx context.Context, customerID uuid.UU
|
|
|
} `json:"data"`
|
|
|
}
|
|
|
|
|
|
- _, err = m.do(http.MethodPost, path, req, &result)
|
|
|
+ _, err = m.do(http.MethodPost, path, "", req, &result)
|
|
|
if err != nil {
|
|
|
return customerPlanID, telemetry.Error(ctx, span, err, "failed to add customer to plan")
|
|
|
}
|
|
|
@@ -183,7 +183,7 @@ func (m MetronomeClient) ListCustomerPlan(ctx context.Context, customerID uuid.U
|
|
|
Data []types.Plan `json:"data"`
|
|
|
}
|
|
|
|
|
|
- _, err = m.do(http.MethodGet, path, nil, &result)
|
|
|
+ _, err = m.do(http.MethodGet, path, "", nil, &result)
|
|
|
if err != nil {
|
|
|
return plan, telemetry.Error(ctx, span, err, "failed to list customer plans")
|
|
|
}
|
|
|
@@ -215,7 +215,7 @@ func (m MetronomeClient) EndCustomerPlan(ctx context.Context, customerID uuid.UU
|
|
|
EndingBeforeUTC: endBefore,
|
|
|
}
|
|
|
|
|
|
- _, err = m.do(http.MethodPost, path, req, nil)
|
|
|
+ _, err = m.do(http.MethodPost, path, "", req, nil)
|
|
|
if err != nil {
|
|
|
return telemetry.Error(ctx, span, err, "failed to end customer plan")
|
|
|
}
|
|
|
@@ -244,7 +244,7 @@ func (m MetronomeClient) ListCustomerCredits(ctx context.Context, customerID uui
|
|
|
Data []types.CreditGrant `json:"data"`
|
|
|
}
|
|
|
|
|
|
- _, err = m.do(http.MethodPost, path, req, &result)
|
|
|
+ _, err = m.do(http.MethodPost, path, "", req, &result)
|
|
|
if err != nil {
|
|
|
return credits, telemetry.Error(ctx, span, err, "failed to list customer credits")
|
|
|
}
|
|
|
@@ -290,7 +290,7 @@ func (m MetronomeClient) CreateCreditsGrant(ctx context.Context, customerID uuid
|
|
|
Priority: 1,
|
|
|
}
|
|
|
|
|
|
- statusCode, err := m.do(http.MethodPost, path, req, nil)
|
|
|
+ statusCode, err := m.do(http.MethodPost, path, "", req, nil)
|
|
|
if err != nil && statusCode != http.StatusConflict {
|
|
|
// a conflict response indicates the grant already exists
|
|
|
return telemetry.Error(ctx, span, err, "failed to create credits grant")
|
|
|
@@ -342,7 +342,7 @@ func (m MetronomeClient) ListCustomerUsage(ctx context.Context, customerID uuid.
|
|
|
}
|
|
|
|
|
|
baseReq.BillableMetricID = billableMetric.ID
|
|
|
- _, err = m.do(http.MethodPost, path, baseReq, &result)
|
|
|
+ _, err = m.do(http.MethodPost, path, "", baseReq, &result)
|
|
|
if err != nil {
|
|
|
return usage, telemetry.Error(ctx, span, err, "failed to get customer usage")
|
|
|
}
|
|
|
@@ -356,6 +356,42 @@ func (m MetronomeClient) ListCustomerUsage(ctx context.Context, customerID uuid.
|
|
|
return usage, nil
|
|
|
}
|
|
|
|
|
|
+// ListCustomerCosts will return the costs for a customer over a time period
|
|
|
+func (m MetronomeClient) ListCustomerCosts(ctx context.Context, customerID uuid.UUID, startingOn string, endingBefore string, limit int) (costs []types.FormattedCost, err error) {
|
|
|
+ ctx, span := telemetry.NewSpan(ctx, "list-customer-costs")
|
|
|
+ defer span.End()
|
|
|
+
|
|
|
+ if customerID == uuid.Nil {
|
|
|
+ return costs, telemetry.Error(ctx, span, err, "customer id empty")
|
|
|
+ }
|
|
|
+
|
|
|
+ path := fmt.Sprintf("customers/%s/costs", customerID)
|
|
|
+
|
|
|
+ var result struct {
|
|
|
+ Data []types.Cost `json:"data"`
|
|
|
+ }
|
|
|
+
|
|
|
+ queryParams := fmt.Sprintf("starting_on=%s&ending_before=%s&limit=%d", startingOn, endingBefore, limit)
|
|
|
+
|
|
|
+ _, err = m.do(http.MethodGet, path, queryParams, nil, &result)
|
|
|
+ if err != nil {
|
|
|
+ return costs, telemetry.Error(ctx, span, err, "failed to create credits grant")
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, customerCost := range result.Data {
|
|
|
+ formattedCost := types.FormattedCost{
|
|
|
+ StartTimestamp: customerCost.StartTimestamp,
|
|
|
+ EndTimestamp: customerCost.EndTimestamp,
|
|
|
+ }
|
|
|
+ for _, creditType := range customerCost.CreditTypes {
|
|
|
+ formattedCost.Cost += creditType.Cost
|
|
|
+ }
|
|
|
+ costs = append(costs, formattedCost)
|
|
|
+ }
|
|
|
+
|
|
|
+ return costs, nil
|
|
|
+}
|
|
|
+
|
|
|
// IngestEvents sends a list of billing events to Metronome's ingest endpoint
|
|
|
func (m MetronomeClient) IngestEvents(ctx context.Context, events []types.BillingEvent) (err error) {
|
|
|
ctx, span := telemetry.NewSpan(ctx, "ingets-billing-events")
|
|
|
@@ -378,7 +414,7 @@ func (m MetronomeClient) IngestEvents(ctx context.Context, events []types.Billin
|
|
|
// Retry each batch to make sure all events are ingested
|
|
|
var currentAttempts int
|
|
|
for currentAttempts < defaultMaxRetries {
|
|
|
- statusCode, err := m.do(http.MethodPost, path, batch, nil)
|
|
|
+ statusCode, err := m.do(http.MethodPost, path, "", batch, nil)
|
|
|
// Check errors that are not from error http codes
|
|
|
if statusCode == 0 && err != nil {
|
|
|
return telemetry.Error(ctx, span, err, "failed to ingest billing events")
|
|
|
@@ -422,7 +458,7 @@ func (m MetronomeClient) listBillableMetricIDs(ctx context.Context, customerID u
|
|
|
Data []types.BillableMetric `json:"data"`
|
|
|
}
|
|
|
|
|
|
- _, err = m.do(http.MethodGet, path, nil, &result)
|
|
|
+ _, err = m.do(http.MethodGet, path, "", nil, &result)
|
|
|
if err != nil {
|
|
|
return billableMetrics, telemetry.Error(ctx, span, err, "failed to retrieve billable metrics from metronome")
|
|
|
}
|
|
|
@@ -440,7 +476,7 @@ func (m MetronomeClient) getCreditTypeID(ctx context.Context, currencyCode strin
|
|
|
Data []types.PricingUnit `json:"data"`
|
|
|
}
|
|
|
|
|
|
- _, err = m.do(http.MethodGet, path, nil, &result)
|
|
|
+ _, err = m.do(http.MethodGet, path, "", nil, &result)
|
|
|
if err != nil {
|
|
|
return creditTypeID, telemetry.Error(ctx, span, err, "failed to retrieve billable metrics from metronome")
|
|
|
}
|
|
|
@@ -454,7 +490,7 @@ func (m MetronomeClient) getCreditTypeID(ctx context.Context, currencyCode strin
|
|
|
return creditTypeID, telemetry.Error(ctx, span, fmt.Errorf("credit type not found for currency code %s", currencyCode), "failed to find credit type")
|
|
|
}
|
|
|
|
|
|
-func (m MetronomeClient) do(method string, path string, body interface{}, data interface{}) (statusCode int, err error) {
|
|
|
+func (m MetronomeClient) do(method string, path string, queryParams string, body interface{}, data interface{}) (statusCode int, err error) {
|
|
|
client := http.Client{}
|
|
|
endpoint, err := url.JoinPath(metronomeBaseUrl, path)
|
|
|
if err != nil {
|
|
|
@@ -469,6 +505,11 @@ func (m MetronomeClient) do(method string, path string, body interface{}, data i
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // Add raw query parameters to the endpoint
|
|
|
+ if queryParams != "" {
|
|
|
+ endpoint += "?" + queryParams
|
|
|
+ }
|
|
|
+
|
|
|
req, err := http.NewRequest(method, endpoint, bytes.NewBuffer(bodyJson))
|
|
|
if err != nil {
|
|
|
return statusCode, err
|