Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
52 changes: 1 addition & 51 deletions src/sentry/models/options/option.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,12 @@
from __future__ import annotations

import abc

from django.db import models
from django.utils import timezone

from sentry.backup.dependencies import PrimaryKeyMap
from sentry.backup.mixins import OverwritableConfigMixin
from sentry.backup.scopes import RelocationScope
from sentry.db.models import (
Model,
OptionManager,
ValidateFunction,
Value,
control_silo_model,
region_silo_model,
sane_repr,
)
from sentry.db.models import Model, control_silo_model, region_silo_model, sane_repr
from sentry.db.models.fields.picklefield import PickledObjectField
from sentry.options.manager import UpdateChannel

Expand Down Expand Up @@ -81,43 +71,3 @@ class Meta:
db_table = "sentry_controloption"

__repr__ = sane_repr("key", "value")


class HasOption:
# Logically this is an abstract interface. Leaving off abc.ABC because it clashes
# with the Model metaclass.

@abc.abstractmethod
def get_option(
self,
key: str,
default: Value | None = None,
validate: ValidateFunction | None = None,
) -> Value:
raise NotImplementedError

@abc.abstractmethod
def update_option(self, key: str, value: Value) -> bool:
raise NotImplementedError

@abc.abstractmethod
def delete_option(self, key: str) -> None:
raise NotImplementedError


class OptionMixin(HasOption):
@property
@abc.abstractmethod
def option_manager(self) -> OptionManager:
raise NotImplementedError

def get_option(
self, key: str, default: Value | None = None, validate: ValidateFunction | None = None
) -> Value:
return self.option_manager.get_value(self, key, default, validate)

def update_option(self, key: str, value: Value) -> bool:
return self.option_manager.set_value(self, key, value)

def delete_option(self, key: str) -> None:
self.option_manager.unset_value(self, key)
15 changes: 13 additions & 2 deletions src/sentry/models/organization.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@
sane_repr,
)
from sentry.db.models.fields.slug import SentryOrgSlugField
from sentry.db.models.manager import ValidateFunction
from sentry.db.models.outboxes import ReplicatedRegionModel
from sentry.db.models.utils import slugify_instance
from sentry.db.postgres.transactions import in_test_hide_transaction_boundary
from sentry.locks import locks
from sentry.models.options.option import OptionMixin
from sentry.models.outbox import OutboxCategory
from sentry.roles.manager import Role
from sentry.services.hybrid_cloud.notifications import notifications_service
Expand Down Expand Up @@ -144,7 +144,7 @@ def get_organizations_where_user_is_owner(self, user_id: int) -> QuerySet:

@snowflake_id_model
@region_silo_model
class Organization(ReplicatedRegionModel, OptionMixin, OrganizationAbsoluteUrlMixin):
class Organization(ReplicatedRegionModel, OrganizationAbsoluteUrlMixin):
"""
An organization represents a group of individuals which maintain ownership of projects.
"""
Expand Down Expand Up @@ -476,3 +476,14 @@ def get_scopes(self, role: Role) -> frozenset[str]:
if not self.get_option("sentry:alerts_member_write", ALERTS_MEMBER_WRITE_DEFAULT):
scopes.discard("alerts:write")
return frozenset(scopes)

def get_option(
self, key: str, default: Any | None = None, validate: ValidateFunction | None = None
) -> Any:
return self.option_manager.get_value(self, key, default, validate)

def update_option(self, key: str, value: Any) -> bool:
return self.option_manager.set_value(self, key, value)

def delete_option(self, key: str) -> None:
self.option_manager.unset_value(self, key)
22 changes: 13 additions & 9 deletions src/sentry/models/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import logging
from collections import defaultdict
from collections.abc import Collection, Iterable, Mapping
from typing import TYPE_CHECKING, ClassVar
from typing import TYPE_CHECKING, Any, ClassVar
from uuid import uuid1

import sentry_sdk
Expand All @@ -27,16 +27,14 @@
BoundedPositiveIntegerField,
FlexibleForeignKey,
Model,
OptionManager,
Value,
region_silo_model,
sane_repr,
)
from sentry.db.models.fields.slug import SentrySlugField
from sentry.db.models.manager import ValidateFunction
from sentry.db.models.utils import slugify_instance
from sentry.locks import locks
from sentry.models.grouplink import GroupLink
from sentry.models.options.option import OptionMixin
from sentry.models.outbox import OutboxCategory, OutboxScope, RegionOutbox, outbox_context
from sentry.models.team import Team
from sentry.monitors.models import MonitorEnvironment, MonitorStatus
Expand All @@ -52,6 +50,7 @@
from sentry.utils.snowflake import save_with_snowflake_id, snowflake_id_model

if TYPE_CHECKING:
from sentry.models.options.project_option import ProjectOptionManager
from sentry.models.user import User

SENTRY_USE_SNOWFLAKE = getattr(settings, "SENTRY_USE_SNOWFLAKE", False)
Expand Down Expand Up @@ -217,7 +216,7 @@ def get_for_user(self, team, user, scope=None, _skip_team_check=False):

@snowflake_id_model
@region_silo_model
class Project(Model, PendingDeletionMixin, OptionMixin):
class Project(Model, PendingDeletionMixin):
from sentry.models.projectteam import ProjectTeam

"""
Expand Down Expand Up @@ -374,18 +373,23 @@ def is_internal_project(self):
return False

@property
def option_manager(self) -> OptionManager:
def option_manager(self) -> ProjectOptionManager:
from sentry.models.options.project_option import ProjectOption

return ProjectOption.objects

def update_option(self, key: str, value: Value) -> bool:
def get_option(
self, key: str, default: Any | None = None, validate: ValidateFunction | None = None
) -> Any:
return self.option_manager.get_value(self, key, default, validate)

def update_option(self, key: str, value: Any) -> bool:
projectoptions.update_rev_for_option(self)
return super().update_option(key, value)
return self.option_manager.set_value(self, key, value)

def delete_option(self, key: str) -> None:
projectoptions.update_rev_for_option(self)
super().delete_option(key)
self.option_manager.unset_value(self, key)

def update_rev_for_option(self):
return projectoptions.update_rev_for_option(self)
Expand Down
3 changes: 1 addition & 2 deletions src/sentry/services/hybrid_cloud/organization/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

from sentry import roles
from sentry.db.models import ValidateFunction, Value
from sentry.models.options.option import HasOption
from sentry.roles import team_roles
from sentry.roles.manager import TeamRole
from sentry.services.hybrid_cloud import RpcModel
Expand Down Expand Up @@ -194,7 +193,7 @@ class RpcOrganizationInvite(RpcModel):
email: str = ""


class RpcOrganizationSummary(RpcModel, OrganizationAbsoluteUrlMixin, HasOption):
class RpcOrganizationSummary(RpcModel, OrganizationAbsoluteUrlMixin):
"""
The subset of organization metadata available from the control silo specifically.
"""
Expand Down
3 changes: 1 addition & 2 deletions src/sentry/services/hybrid_cloud/project/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

from sentry.constants import ObjectStatus
from sentry.db.models import ValidateFunction, Value
from sentry.models.options.option import HasOption
from sentry.services.hybrid_cloud import OptionValue, RpcModel


Expand All @@ -22,7 +21,7 @@ class ProjectFilterArgs(TypedDict, total=False):
project_ids: list[int]


class RpcProject(RpcModel, HasOption):
class RpcProject(RpcModel):
id: int = -1
slug: str = ""
name: str = ""
Expand Down