Skip to content

Commit

Permalink
feat(billing): Add quotas.backend.on_role_change
Browse files Browse the repository at this point in the history
  • Loading branch information
dashed committed Feb 22, 2025
1 parent 8cc67b4 commit b894824
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 1 deletion.
11 changes: 10 additions & 1 deletion src/sentry/api/endpoints/organization_member/details.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from rest_framework.response import Response
from rest_framework.serializers import ValidationError

from sentry import audit_log, features, ratelimits, roles
from sentry import audit_log, features, quotas, ratelimits, roles
from sentry.api.api_owners import ApiOwner
from sentry.api.api_publish_status import ApiPublishStatus
from sentry.api.base import region_silo_endpoint
Expand Down Expand Up @@ -355,8 +355,17 @@ def put(
)
return Response({"detail": message}, status=400)

previous_role = member.role
self._change_org_role(member, assigned_org_role)

# Run any Subscription logic that needs to happen when a role is changed.
quotas.backend.on_role_change(
organization=organization,
organization_member=member,
previous_role=previous_role,
new_role=assigned_org_role,
)

self.create_audit_entry(
request=request,
organization=organization,
Expand Down
20 changes: 20 additions & 0 deletions src/sentry/quotas/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

if TYPE_CHECKING:
from sentry.models.organization import Organization
from sentry.models.organizationmember import OrganizationMember
from sentry.models.project import Project
from sentry.models.projectkey import ProjectKey
from sentry.monitors.models import Monitor
Expand Down Expand Up @@ -705,3 +706,22 @@ def should_emit_profile_duration_outcome(
Determines if the profile duration outcome should be emitted.
"""
return True

def on_role_change(
self,
organization: Organization,
organization_member: OrganizationMember,
previous_role: str,
new_role: str,
) -> None:
"""
Called when an organization member's role is changed.
This is used to run any Subscription logic that needs to happen when a role is changed.
Args:
organization: The organization the member belongs to
organization_member: The member whose role is being changed
previous_role: The member's role before the change
new_role: The member's new role after the change
"""
pass
39 changes: 39 additions & 0 deletions tests/sentry/api/endpoints/test_organization_member_details.py
Original file line number Diff line number Diff line change
Expand Up @@ -856,6 +856,45 @@ def test_can_promote_team_member_to_role_where_team_roles_enabled(self, mock_get
orgRole="manager",
)

@patch("sentry.quotas.base.Quota.on_role_change")
def test_on_role_change_called_when_role_updated(self, mock_on_role_change):
member = self.create_user("[email protected]")
member_om = self.create_member(
organization=self.organization, user=member, role="member", teams=[]
)

with outbox_runner():
self.get_success_response(self.organization.slug, member_om.id, role="manager")

mock_on_role_change.assert_called_once_with(
organization=self.organization,
organization_member=member_om,
previous_role="member",
new_role="manager",
)

@patch("sentry.quotas.base.Quota.on_role_change")
def test_on_role_change_not_called_when_role_unchanged(self, mock_on_role_change):
member = self.create_user("[email protected]")
member_om = self.create_member(
organization=self.organization, user=member, role="member", teams=[]
)

# Update something else but keep role the same
self.get_success_response(self.organization.slug, member_om.id, teams=[])

mock_on_role_change.assert_not_called()

@patch("sentry.quotas.base.Quota.on_role_change")
def test_on_role_change_not_called_when_reinviting(self, mock_on_role_change):
member_om = self.create_member(
organization=self.organization, email="[email protected]", role="member"
)

self.get_success_response(self.organization.slug, member_om.id, reinvite=1)

mock_on_role_change.assert_not_called()


class DeleteOrganizationMemberTest(OrganizationMemberTestBase):
method = "delete"
Expand Down

0 comments on commit b894824

Please sign in to comment.