From 5bfb1a85e3cf3286b4870fe58f794cdd9a5cbfb0 Mon Sep 17 00:00:00 2001 From: Steve Konves Date: Thu, 18 Jun 2026 12:46:07 -0700 Subject: [PATCH 1/2] feat(billing-platform): Add UsagePricer daily breakdown endpoint proto Adds ``UsagePricerService.GetDailyBreakdown``, an internal endpoint that emits per-day, per-line-item ``UsageData`` (every outcome field, not just ``accepted``) for one or more contracts. Used by the REVENG-188 platform CSV export to back the existing legacy ``CustomerHistoryCsvResponder`` without re-implementing the ``BillableMetric.expression`` evaluator. Initial revision also added a ``ChargeService.GetDailyUsageBreakdown`` endpoint that translated UsagePricer's ``line_item_uid``-keyed output into ``DataCategory``-keyed rows for the customer-facing CSV. That translation has since moved to the getsentry web layer (where customer-facing rendering decisions like column naming and future i18n belong), so the Charge endpoint is dropped before this lands. --- .../v1/endpoint_get_daily_breakdown.proto | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 proto/sentry_protos/billing/v1/services/usage_pricer/v1/endpoint_get_daily_breakdown.proto diff --git a/proto/sentry_protos/billing/v1/services/usage_pricer/v1/endpoint_get_daily_breakdown.proto b/proto/sentry_protos/billing/v1/services/usage_pricer/v1/endpoint_get_daily_breakdown.proto new file mode 100644 index 00000000..987a25f6 --- /dev/null +++ b/proto/sentry_protos/billing/v1/services/usage_pricer/v1/endpoint_get_daily_breakdown.proto @@ -0,0 +1,54 @@ +syntax = "proto3"; + +package sentry_protos.billing.v1.services.usage_pricer.v1; + +import "sentry_protos/billing/v1/date.proto"; +import "sentry_protos/billing/v1/usage_data.proto"; + +// Compiles per-day, per-line-item usage breakdowns spanning one or more +// contracts. UsagePricer evaluates each contract's package +// ``BillableMetric.expression`` once per outcome field of the raw +// usage, so callers receive the package-projected ``UsageData`` view +// rather than the raw outcomes returned by ``BillingUsageService``. +// +// This is the "non-pricing" derived view UsagePricer exposes -- the +// projection step pricing already performs internally, lifted out so +// other presentation services (e.g. ChargeService for CSV / UI) can +// consume it without re-implementing evaluation. +message GetDailyBreakdownRequest { + // The contracts whose daily breakdowns should be returned. + repeated string contract_ids = 1; +} + +// One line item's projected per-outcome quantities on a given date. +message LineItemDailyOutcomes { + // Package line item uid (UsagePricer's native vocabulary). + string line_item_uid = 1; + + // The seven outcome fields, each populated by evaluating the line + // item's ``BillableMetric.expression`` against that outcome's raw + // counts in the day's usage. + sentry_protos.billing.v1.UsageData outcomes = 2; +} + +// One row per calendar date spanning the union of the requested +// contracts' on-demand periods. +message DailyLineItemOutcomes { + sentry_protos.billing.v1.Date date = 1; + + // Per-line-item outcomes on this date. Every metered line item from + // the union of the requested contracts' packages is emitted, + // including line items with zero usage, so consumers can render + // zero-rows without a separate zero-fill pass. + repeated LineItemDailyOutcomes line_items = 2; +} + +message GetDailyBreakdownResponse { + // Days in ascending date order. A given calendar date appears at + // most once regardless of how many of the requested contracts + // overlap it (BillingUsageService reports usage at calendar-day + // granularity, so a day straddling a mid-day rollover is still one + // row carrying the day's full outcomes). Contract ids from the + // request that do not resolve contribute no rows. + repeated DailyLineItemOutcomes days = 1; +} From 2060a5f9b78eac765996a3634fd19f46bd33d314 Mon Sep 17 00:00:00 2001 From: "getsantry[bot]" <66042841+getsantry[bot]@users.noreply.github.com> Date: Fri, 19 Jun 2026 00:56:39 +0000 Subject: [PATCH 2/2] chore: Regenerate Rust bindings --- Cargo.lock | 2 +- ..._protos.billing.v1.services.contract.v1.rs | 5 ++ ...tos.billing.v1.services.usage_pricer.v1.rs | 52 +++++++++++++++++++ 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index e8698790..812b14d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -717,7 +717,7 @@ checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "sentry_protos" -version = "0.30.1" +version = "0.32.2" dependencies = [ "prost", "prost-types", diff --git a/rust/src/sentry_protos.billing.v1.services.contract.v1.rs b/rust/src/sentry_protos.billing.v1.services.contract.v1.rs index 0a75a6e7..57a521e8 100644 --- a/rust/src/sentry_protos.billing.v1.services.contract.v1.rs +++ b/rust/src/sentry_protos.billing.v1.services.contract.v1.rs @@ -50,6 +50,11 @@ pub struct BillingConfig { /// (1 = monthly, 12 = annual). Frozen for the life of the contract. #[prost(uint32, tag = "7")] pub month_interval: u32, + /// Whether the org is allowed to incur pay-as-you-go usage. + /// Credit-card orgs always support payg; invoiced orgs that should + /// support it are an explicit override. + #[prost(bool, tag = "8")] + pub supports_payg: bool, } /// Indicates how the account is billed. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] diff --git a/rust/src/sentry_protos.billing.v1.services.usage_pricer.v1.rs b/rust/src/sentry_protos.billing.v1.services.usage_pricer.v1.rs index 7901199b..ca1187bb 100644 --- a/rust/src/sentry_protos.billing.v1.services.usage_pricer.v1.rs +++ b/rust/src/sentry_protos.billing.v1.services.usage_pricer.v1.rs @@ -1,4 +1,56 @@ // This file is @generated by prost-build. +/// Compiles per-day, per-line-item usage breakdowns spanning one or more +/// contracts. UsagePricer evaluates each contract's package +/// `BillableMetric.expression` once per outcome field of the raw +/// usage, so callers receive the package-projected `UsageData` view +/// rather than the raw outcomes returned by `BillingUsageService`. +/// +/// This is the "non-pricing" derived view UsagePricer exposes -- the +/// projection step pricing already performs internally, lifted out so +/// other presentation services (e.g. ChargeService for CSV / UI) can +/// consume it without re-implementing evaluation. +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct GetDailyBreakdownRequest { + /// The contracts whose daily breakdowns should be returned. + #[prost(string, repeated, tag = "1")] + pub contract_ids: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, +} +/// One line item's projected per-outcome quantities on a given date. +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct LineItemDailyOutcomes { + /// Package line item uid (UsagePricer's native vocabulary). + #[prost(string, tag = "1")] + pub line_item_uid: ::prost::alloc::string::String, + /// The seven outcome fields, each populated by evaluating the line + /// item's `BillableMetric.expression` against that outcome's raw + /// counts in the day's usage. + #[prost(message, optional, tag = "2")] + pub outcomes: ::core::option::Option, +} +/// One row per calendar date spanning the union of the requested +/// contracts' on-demand periods. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DailyLineItemOutcomes { + #[prost(message, optional, tag = "1")] + pub date: ::core::option::Option, + /// Per-line-item outcomes on this date. Every metered line item from + /// the union of the requested contracts' packages is emitted, + /// including line items with zero usage, so consumers can render + /// zero-rows without a separate zero-fill pass. + #[prost(message, repeated, tag = "2")] + pub line_items: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetDailyBreakdownResponse { + /// Days in ascending date order. A given calendar date appears at + /// most once regardless of how many of the requested contracts + /// overlap it (BillingUsageService reports usage at calendar-day + /// granularity, so a day straddling a mid-day rollover is still one + /// row carrying the day's full outcomes). Contract ids from the + /// request that do not resolve contribute no rows. + #[prost(message, repeated, tag = "1")] + pub days: ::prost::alloc::vec::Vec, +} #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct UsagePricerRequest { #[prost(uint64, tag = "1")]