diff --git a/Cargo.lock b/Cargo.lock index 91bf4c97..ea82471d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -717,7 +717,7 @@ checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "sentry_protos" -version = "0.64.5" +version = "0.64.6" dependencies = [ "prost", "prost-types", diff --git a/proto/sentry_protos/billing/v1/common/v1/seat_object.proto b/proto/sentry_protos/billing/v1/common/v1/seat_object.proto new file mode 100644 index 00000000..0faa2d93 --- /dev/null +++ b/proto/sentry_protos/billing/v1/common/v1/seat_object.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package sentry_protos.billing.v1.common.v1; + +import "sentry_protos/billing/v1/seat_category.proto"; + +// A seat object representing a billable product entity (monitor, uptime +// detector, seer contributor) within an organization. +message SeatObject { + uint64 organization_id = 1; + uint64 project_id = 2; + sentry_protos.billing.v1.SeatCategory seat_category = 3; + string external_product_display_name = 4; + string external_product_identifier = 5; +} diff --git a/proto/sentry_protos/billing/v1/services/seats/v1/endpoint_assign_seat.proto b/proto/sentry_protos/billing/v1/services/seats/v1/endpoint_assign_seat.proto new file mode 100644 index 00000000..e9cde8ad --- /dev/null +++ b/proto/sentry_protos/billing/v1/services/seats/v1/endpoint_assign_seat.proto @@ -0,0 +1,14 @@ +syntax = "proto3"; + +package sentry_protos.billing.v1.services.seats.v1; + +import "sentry_protos/billing/v1/common/v1/seat_object.proto"; +import "sentry_protos/billing/v1/services/seats/v1/seat_outcome.proto"; + +message AssignSeatRequest { + sentry_protos.billing.v1.common.v1.SeatObject seat_object = 1; +} + +message AssignSeatResponse { + SeatOutcome outcome = 1; +} diff --git a/proto/sentry_protos/billing/v1/services/seats/v1/endpoint_check_assign_seats.proto b/proto/sentry_protos/billing/v1/services/seats/v1/endpoint_check_assign_seats.proto new file mode 100644 index 00000000..bc740bdb --- /dev/null +++ b/proto/sentry_protos/billing/v1/services/seats/v1/endpoint_check_assign_seats.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; + +package sentry_protos.billing.v1.services.seats.v1; + +import "sentry_protos/billing/v1/common/v1/seat_object.proto"; +import "sentry_protos/billing/v1/services/seats/v1/seat_outcome.proto"; + +message CheckAssignSeatsRequest { + repeated sentry_protos.billing.v1.common.v1.SeatObject seat_objects = 1; + + // When omitted the service resolves the organization's current contract. + optional uint64 contract_id = 2; +} + +message CheckAssignSeatsResponse { + SeatOutcome outcome = 1; + string reason = 2; +} diff --git a/proto/sentry_protos/billing/v1/services/seats/v1/endpoint_disable_seat.proto b/proto/sentry_protos/billing/v1/services/seats/v1/endpoint_disable_seat.proto new file mode 100644 index 00000000..c408ccea --- /dev/null +++ b/proto/sentry_protos/billing/v1/services/seats/v1/endpoint_disable_seat.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +package sentry_protos.billing.v1.services.seats.v1; + +import "sentry_protos/billing/v1/common/v1/seat_object.proto"; + +message DisableSeatRequest { + sentry_protos.billing.v1.common.v1.SeatObject seat_object = 1; + + // When omitted the service resolves the organization's current contract. + optional uint64 contract_id = 2; +} diff --git a/proto/sentry_protos/billing/v1/services/seats/v1/endpoint_get_daily_seat_usage_by_project.proto b/proto/sentry_protos/billing/v1/services/seats/v1/endpoint_get_daily_seat_usage_by_project.proto new file mode 100644 index 00000000..8f892de0 --- /dev/null +++ b/proto/sentry_protos/billing/v1/services/seats/v1/endpoint_get_daily_seat_usage_by_project.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package sentry_protos.billing.v1.services.seats.v1; + +import "sentry_protos/billing/v1/services/usage/v1/endpoint_usage.proto"; + +// Seat usage for a single project. +message ProjectSeatUsage { + uint64 project_id = 1; + repeated sentry_protos.billing.v1.services.usage.v1.DailySeatUsage seat_days = 2; +} + +message GetDailySeatUsageByProjectResponse { + repeated ProjectSeatUsage projects = 1; +} diff --git a/proto/sentry_protos/billing/v1/services/seats/v1/endpoint_get_seat_status.proto b/proto/sentry_protos/billing/v1/services/seats/v1/endpoint_get_seat_status.proto new file mode 100644 index 00000000..8855a18b --- /dev/null +++ b/proto/sentry_protos/billing/v1/services/seats/v1/endpoint_get_seat_status.proto @@ -0,0 +1,28 @@ +syntax = "proto3"; + +package sentry_protos.billing.v1.services.seats.v1; + +import "sentry_protos/billing/v1/common/v1/seat_object.proto"; + +// The status of a seat object from the perspective of the external product +// that owns it (monitor, uptime detector, seer contributor). +enum ExternalProductSeatStatus { + EXTERNAL_PRODUCT_SEAT_STATUS_UNSPECIFIED = 0; + + // Does not exist, either because it was removed or it was never created. + EXTERNAL_PRODUCT_SEAT_STATUS_DNE = 1; + + // Alive and well. + EXTERNAL_PRODUCT_SEAT_STATUS_ACTIVE = 2; + + // The billing platform deems this seat object unusable for billing reasons. + EXTERNAL_PRODUCT_SEAT_STATUS_DISABLED = 3; +} + +message GetSeatStatusForExternalProductRequest { + sentry_protos.billing.v1.common.v1.SeatObject seat_object = 1; +} + +message GetSeatStatusForExternalProductResponse { + ExternalProductSeatStatus status = 1; +} diff --git a/proto/sentry_protos/billing/v1/services/seats/v1/endpoint_remove_seat.proto b/proto/sentry_protos/billing/v1/services/seats/v1/endpoint_remove_seat.proto new file mode 100644 index 00000000..ef458cd1 --- /dev/null +++ b/proto/sentry_protos/billing/v1/services/seats/v1/endpoint_remove_seat.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +package sentry_protos.billing.v1.services.seats.v1; + +import "sentry_protos/billing/v1/common/v1/seat_object.proto"; + +message RemoveSeatRequest { + sentry_protos.billing.v1.common.v1.SeatObject seat_object = 1; + + // When omitted the service resolves the organization's current contract. + optional uint64 contract_id = 2; +} diff --git a/proto/sentry_protos/billing/v1/services/seats/v1/endpoint_rollover_seats.proto b/proto/sentry_protos/billing/v1/services/seats/v1/endpoint_rollover_seats.proto new file mode 100644 index 00000000..434bd270 --- /dev/null +++ b/proto/sentry_protos/billing/v1/services/seats/v1/endpoint_rollover_seats.proto @@ -0,0 +1,8 @@ +syntax = "proto3"; + +package sentry_protos.billing.v1.services.seats.v1; + +message RolloverSeatsToNewContractRequest { + uint64 prev_contract_id = 1; + uint64 new_contract_id = 2; +} diff --git a/proto/sentry_protos/billing/v1/services/seats/v1/endpoint_update_seat_identifier.proto b/proto/sentry_protos/billing/v1/services/seats/v1/endpoint_update_seat_identifier.proto new file mode 100644 index 00000000..61dd2eb5 --- /dev/null +++ b/proto/sentry_protos/billing/v1/services/seats/v1/endpoint_update_seat_identifier.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; + +package sentry_protos.billing.v1.services.seats.v1; + +import "sentry_protos/billing/v1/common/v1/seat_object.proto"; + +message UpdateSeatIdentifierRequest { + sentry_protos.billing.v1.common.v1.SeatObject seat_object = 1; + string new_identifier = 2; + + // When omitted the service resolves the organization's current contract. + optional uint64 contract_id = 3; +} diff --git a/proto/sentry_protos/billing/v1/services/seats/v1/seat_outcome.proto b/proto/sentry_protos/billing/v1/services/seats/v1/seat_outcome.proto new file mode 100644 index 00000000..206cd57b --- /dev/null +++ b/proto/sentry_protos/billing/v1/services/seats/v1/seat_outcome.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package sentry_protos.billing.v1.services.seats.v1; + +// The outcome of a seat assignment check. +enum SeatOutcome { + // Default / unspecified. + SEAT_OUTCOME_UNSPECIFIED = 0; + // The seat(s) can be assigned — budget is available. + SEAT_OUTCOME_ACCEPTED = 1; + // The seat category is not available on the current plan. + SEAT_OUTCOME_INVALID = 2; + // The plan includes the category but all budget is exhausted. + SEAT_OUTCOME_RATE_LIMITED = 3; +} diff --git a/rust/src/_include.rs b/rust/src/_include.rs index e7ec5d80..dc5ea4fd 100644 --- a/rust/src/_include.rs +++ b/rust/src/_include.rs @@ -74,6 +74,11 @@ pub mod sentry_protos { include!("sentry_protos.billing.v1.services.rate_card.v1.rs"); } } + pub mod seats { + pub mod v1 { + include!("sentry_protos.billing.v1.services.seats.v1.rs"); + } + } pub mod trial { pub mod v1 { include!("sentry_protos.billing.v1.services.trial.v1.rs"); diff --git a/rust/src/sentry_protos.billing.v1.common.v1.rs b/rust/src/sentry_protos.billing.v1.common.v1.rs index ebaa7be8..a21e30b9 100644 --- a/rust/src/sentry_protos.billing.v1.common.v1.rs +++ b/rust/src/sentry_protos.billing.v1.common.v1.rs @@ -530,6 +530,21 @@ pub struct DataCategoryRetention { #[prost(message, optional, tag = "2")] pub settings: ::core::option::Option, } +/// A seat object representing a billable product entity (monitor, uptime +/// detector, seer contributor) within an organization. +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct SeatObject { + #[prost(uint64, tag = "1")] + pub organization_id: u64, + #[prost(uint64, tag = "2")] + pub project_id: u64, + #[prost(enumeration = "super::super::SeatCategory", tag = "3")] + pub seat_category: i32, + #[prost(string, tag = "4")] + pub external_product_display_name: ::prost::alloc::string::String, + #[prost(string, tag = "5")] + pub external_product_identifier: ::prost::alloc::string::String, +} /// Card payment method details. #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct Card { diff --git a/rust/src/sentry_protos.billing.v1.services.seats.v1.rs b/rust/src/sentry_protos.billing.v1.services.seats.v1.rs new file mode 100644 index 00000000..c98f8aa9 --- /dev/null +++ b/rust/src/sentry_protos.billing.v1.services.seats.v1.rs @@ -0,0 +1,158 @@ +// This file is @generated by prost-build. +/// The outcome of a seat assignment check. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum SeatOutcome { + /// Default / unspecified. + Unspecified = 0, + /// The seat(s) can be assigned — budget is available. + Accepted = 1, + /// The seat category is not available on the current plan. + Invalid = 2, + /// The plan includes the category but all budget is exhausted. + RateLimited = 3, +} +impl SeatOutcome { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "SEAT_OUTCOME_UNSPECIFIED", + Self::Accepted => "SEAT_OUTCOME_ACCEPTED", + Self::Invalid => "SEAT_OUTCOME_INVALID", + Self::RateLimited => "SEAT_OUTCOME_RATE_LIMITED", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "SEAT_OUTCOME_UNSPECIFIED" => Some(Self::Unspecified), + "SEAT_OUTCOME_ACCEPTED" => Some(Self::Accepted), + "SEAT_OUTCOME_INVALID" => Some(Self::Invalid), + "SEAT_OUTCOME_RATE_LIMITED" => Some(Self::RateLimited), + _ => None, + } + } +} +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct AssignSeatRequest { + #[prost(message, optional, tag = "1")] + pub seat_object: ::core::option::Option, +} +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] +pub struct AssignSeatResponse { + #[prost(enumeration = "SeatOutcome", tag = "1")] + pub outcome: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CheckAssignSeatsRequest { + #[prost(message, repeated, tag = "1")] + pub seat_objects: ::prost::alloc::vec::Vec< + super::super::super::common::v1::SeatObject, + >, + /// When omitted the service resolves the organization's current contract. + #[prost(uint64, optional, tag = "2")] + pub contract_id: ::core::option::Option, +} +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct CheckAssignSeatsResponse { + #[prost(enumeration = "SeatOutcome", tag = "1")] + pub outcome: i32, + #[prost(string, tag = "2")] + pub reason: ::prost::alloc::string::String, +} +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct DisableSeatRequest { + #[prost(message, optional, tag = "1")] + pub seat_object: ::core::option::Option, + /// When omitted the service resolves the organization's current contract. + #[prost(uint64, optional, tag = "2")] + pub contract_id: ::core::option::Option, +} +/// Seat usage for a single project. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ProjectSeatUsage { + #[prost(uint64, tag = "1")] + pub project_id: u64, + #[prost(message, repeated, tag = "2")] + pub seat_days: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetDailySeatUsageByProjectResponse { + #[prost(message, repeated, tag = "1")] + pub projects: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct GetSeatStatusForExternalProductRequest { + #[prost(message, optional, tag = "1")] + pub seat_object: ::core::option::Option, +} +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] +pub struct GetSeatStatusForExternalProductResponse { + #[prost(enumeration = "ExternalProductSeatStatus", tag = "1")] + pub status: i32, +} +/// The status of a seat object from the perspective of the external product +/// that owns it (monitor, uptime detector, seer contributor). +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum ExternalProductSeatStatus { + Unspecified = 0, + /// Does not exist, either because it was removed or it was never created. + Dne = 1, + /// Alive and well. + Active = 2, + /// The billing platform deems this seat object unusable for billing reasons. + Disabled = 3, +} +impl ExternalProductSeatStatus { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "EXTERNAL_PRODUCT_SEAT_STATUS_UNSPECIFIED", + Self::Dne => "EXTERNAL_PRODUCT_SEAT_STATUS_DNE", + Self::Active => "EXTERNAL_PRODUCT_SEAT_STATUS_ACTIVE", + Self::Disabled => "EXTERNAL_PRODUCT_SEAT_STATUS_DISABLED", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "EXTERNAL_PRODUCT_SEAT_STATUS_UNSPECIFIED" => Some(Self::Unspecified), + "EXTERNAL_PRODUCT_SEAT_STATUS_DNE" => Some(Self::Dne), + "EXTERNAL_PRODUCT_SEAT_STATUS_ACTIVE" => Some(Self::Active), + "EXTERNAL_PRODUCT_SEAT_STATUS_DISABLED" => Some(Self::Disabled), + _ => None, + } + } +} +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct RemoveSeatRequest { + #[prost(message, optional, tag = "1")] + pub seat_object: ::core::option::Option, + /// When omitted the service resolves the organization's current contract. + #[prost(uint64, optional, tag = "2")] + pub contract_id: ::core::option::Option, +} +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] +pub struct RolloverSeatsToNewContractRequest { + #[prost(uint64, tag = "1")] + pub prev_contract_id: u64, + #[prost(uint64, tag = "2")] + pub new_contract_id: u64, +} +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct UpdateSeatIdentifierRequest { + #[prost(message, optional, tag = "1")] + pub seat_object: ::core::option::Option, + #[prost(string, tag = "2")] + pub new_identifier: ::prost::alloc::string::String, + /// When omitted the service resolves the organization's current contract. + #[prost(uint64, optional, tag = "3")] + pub contract_id: ::core::option::Option, +} diff --git a/rust/src/sentry_protos.billing.v1.services.usage.v1.rs b/rust/src/sentry_protos.billing.v1.services.usage.v1.rs index c0055c36..ad20be77 100644 --- a/rust/src/sentry_protos.billing.v1.services.usage.v1.rs +++ b/rust/src/sentry_protos.billing.v1.services.usage.v1.rs @@ -1,43 +1,5 @@ // This file is @generated by prost-build. #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] -pub struct PageToken { - #[prost(oneof = "page_token::Value", tags = "1")] - pub value: ::core::option::Option, -} -/// Nested message and enum types in `PageToken`. -pub mod page_token { - #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Oneof)] - pub enum Value { - #[prost(uint64, tag = "1")] - Offset(u64), - } -} -#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] -pub struct GetOrgsWithUsageRequest { - #[prost(message, optional, tag = "1")] - pub start: ::core::option::Option<::prost_types::Timestamp>, - #[prost(message, optional, tag = "2")] - pub end: ::core::option::Option<::prost_types::Timestamp>, - /// Optional filter for specific data categories. - /// When empty, usage for all categories is queried. - #[prost(enumeration = "super::super::super::DataCategory", repeated, tag = "3")] - pub categories: ::prost::alloc::vec::Vec, - /// Optional max number of items to return. - #[prost(uint32, tag = "4")] - pub limit: u32, - /// Optional page token for pagination. If provided, returns the next page of results. - #[prost(message, optional, tag = "5")] - pub page_token: ::core::option::Option, -} -#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] -pub struct GetOrgsWithUsageResponse { - #[prost(uint64, repeated, tag = "1")] - pub organization_ids: ::prost::alloc::vec::Vec, - /// Page token for the next page of results - #[prost(message, optional, tag = "2")] - pub page_token: ::core::option::Option, -} -#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct CategoryUsage { #[prost(enumeration = "super::super::super::DataCategory", tag = "1")] pub category: i32, @@ -98,6 +60,44 @@ pub struct GetUsageRequest { #[prost(enumeration = "super::super::super::DataCategory", repeated, tag = "4")] pub categories: ::prost::alloc::vec::Vec, } +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] +pub struct PageToken { + #[prost(oneof = "page_token::Value", tags = "1")] + pub value: ::core::option::Option, +} +/// Nested message and enum types in `PageToken`. +pub mod page_token { + #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Oneof)] + pub enum Value { + #[prost(uint64, tag = "1")] + Offset(u64), + } +} +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct GetOrgsWithUsageRequest { + #[prost(message, optional, tag = "1")] + pub start: ::core::option::Option<::prost_types::Timestamp>, + #[prost(message, optional, tag = "2")] + pub end: ::core::option::Option<::prost_types::Timestamp>, + /// Optional filter for specific data categories. + /// When empty, usage for all categories is queried. + #[prost(enumeration = "super::super::super::DataCategory", repeated, tag = "3")] + pub categories: ::prost::alloc::vec::Vec, + /// Optional max number of items to return. + #[prost(uint32, tag = "4")] + pub limit: u32, + /// Optional page token for pagination. If provided, returns the next page of results. + #[prost(message, optional, tag = "5")] + pub page_token: ::core::option::Option, +} +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct GetOrgsWithUsageResponse { + #[prost(uint64, repeated, tag = "1")] + pub organization_ids: ::prost::alloc::vec::Vec, + /// Page token for the next page of results + #[prost(message, optional, tag = "2")] + pub page_token: ::core::option::Option, +} #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct GetUsageByProjectRequest { #[prost(uint64, tag = "1")]