Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions src/sentry/billing/platform/services/quota_config_mapping.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from __future__ import annotations

from sentry_protos.billing.v1.quota_config_pb2 import QuotaConfig as ProtoQuotaConfig
from sentry_protos.billing.v1.quota_config_pb2 import QuotaScope as ProtoQuotaScope

from sentry.billing.platform.services.category_mapping import (
proto_to_sentry_category,
sentry_to_proto_category,
)
from sentry.constants import DataCategory
from sentry.quotas.base import QuotaConfig, QuotaScope


def sentry_to_proto_quota_config(quota: QuotaConfig) -> ProtoQuotaConfig:
"""Convert a sentry QuotaConfig to its proto equivalent."""
kwargs: dict = {
"id": quota.id or "",
"categories": sorted(sentry_to_proto_category(int(c)) for c in quota.categories),
"scope": ProtoQuotaScope.ValueType(int(quota.scope))
if quota.scope is not None
else ProtoQuotaScope.QUOTA_SCOPE_UNSPECIFIED,
}
if quota.scope_id is not None:
kwargs["scope_id"] = quota.scope_id
if quota.limit is not None:
kwargs["limit"] = quota.limit
if quota.window is not None:
kwargs["window"] = quota.window
if quota.reason_code is not None:
kwargs["reason_code"] = quota.reason_code

return ProtoQuotaConfig(**kwargs)


def proto_to_sentry_quota_config(proto_quota: ProtoQuotaConfig) -> QuotaConfig:
"""Convert a proto QuotaConfig to its sentry equivalent."""
categories = [DataCategory(proto_to_sentry_category(c)) for c in proto_quota.categories]

Check failure on line 37 in src/sentry/billing/platform/services/quota_config_mapping.py

View check run for this annotation

@sentry/warden / warden: sentry-backend-bugs

DataCategory(proto_to_sentry_category(c)) raises ValueError for unmapped proto categories

`proto_to_sentry_category()` is designed to gracefully pass through the raw proto int (and emit a metric) when it encounters an unmapped proto category, expecting the caller to handle it. However, `proto_to_sentry_quota_config` immediately wraps the result in `DataCategory(...)`. If the proto category is unmapped, the raw proto int is passed to `DataCategory(...)`, which raises `ValueError` for any int that is not a valid `DataCategory` member (proto and sentry enums use different int values, as documented in `sentry_to_proto_category`). This defeats the graceful-degradation intent of `proto_to_sentry_category` and crashes the conversion. This is reachable when getsentry sends a proto QuotaConfig containing a category value added to the proto enum but not yet present in `SENTRY_TO_PROTO_CATEGORY`.
Comment thread
sentry-warden[bot] marked this conversation as resolved.
Outdated

scope = (
QuotaScope(proto_quota.scope)

Check warning on line 40 in src/sentry/billing/platform/services/quota_config_mapping.py

View check run for this annotation

@sentry/warden / warden: sentry-backend-bugs

QuotaScope(proto_quota.scope) raises ValueError for unknown proto scope values

`QuotaScope` is an `IntEnum` with only values 1, 2, 3; any proto scope value that is not `QUOTA_SCOPE_UNSPECIFIED` but also not in `{1, 2, 3}` will raise an unhandled `ValueError` at line 40.
Comment thread
sentry-warden[bot] marked this conversation as resolved.
Outdated
if proto_quota.scope != ProtoQuotaScope.QUOTA_SCOPE_UNSPECIFIED
else QuotaScope.ORGANIZATION
)

return QuotaConfig(
id=proto_quota.id or None,
categories=categories,
scope=scope,
scope_id=proto_quota.scope_id if proto_quota.HasField("scope_id") else None,
limit=proto_quota.limit if proto_quota.HasField("limit") else None,
window=proto_quota.window if proto_quota.HasField("window") else None,
reason_code=proto_quota.reason_code if proto_quota.HasField("reason_code") else None,
)
Loading