Skip to content

Commit ab5fd93

Browse files
authored
hybridcloud(apitoken): add async delete method on apitoken model (#86532)
Continuation of #86069. Use the newly created services in the model to process deletions.
1 parent 5fe0c3e commit ab5fd93

File tree

2 files changed

+52
-1
lines changed

2 files changed

+52
-1
lines changed

src/sentry/models/apitoken.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import hashlib
44
import secrets
5-
from collections.abc import Collection
5+
from collections.abc import Collection, Mapping
66
from datetime import timedelta
77
from typing import Any, ClassVar
88

@@ -259,6 +259,21 @@ def handle_async_replication(self, region_name: str, shard_identifier: int) -> N
259259
region_name=region_name,
260260
)
261261

262+
@classmethod
263+
def handle_async_deletion(
264+
cls,
265+
identifier: int,
266+
region_name: str,
267+
shard_identifier: int,
268+
payload: Mapping[str, Any] | None,
269+
) -> None:
270+
from sentry.hybridcloud.services.replica import region_replica_service
271+
272+
region_replica_service.delete_replicated_api_token(
273+
apitoken_id=identifier,
274+
region_name=region_name,
275+
)
276+
262277
@classmethod
263278
def from_grant(cls, grant: ApiGrant):
264279
with transaction.atomic(router.db_for_write(cls)):

tests/sentry/models/test_apitoken.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import hashlib
22
from datetime import timedelta
3+
from unittest import mock
34

45
import pytest
56
from django.utils import timezone
@@ -150,6 +151,41 @@ def test_replica_string_serialization(self):
150151
== f"replica_token_id={replica.id}, token_id={token.id} is swug"
151152
)
152153

154+
def test_delete_token_removes_replica(self):
155+
user = self.create_user()
156+
157+
with outbox_runner():
158+
token = ApiToken.objects.create(user_id=user.id, token_type=AuthTokenType.USER)
159+
token.save()
160+
161+
# Verify replica exists
162+
with assume_test_silo_mode(SiloMode.REGION):
163+
assert ApiTokenReplica.objects.filter(apitoken_id=token.id).exists()
164+
165+
# Delete token and verify replica is removed
166+
with outbox_runner():
167+
token.delete()
168+
169+
with assume_test_silo_mode(SiloMode.REGION):
170+
assert not ApiTokenReplica.objects.filter(apitoken_id=token.id).exists()
171+
172+
@mock.patch(
173+
"sentry.hybridcloud.services.replica.region_replica_service.delete_replicated_api_token"
174+
)
175+
def test_handle_async_deletion_called(self, mock_delete_replica):
176+
user = self.create_user()
177+
token = ApiToken.objects.create(user_id=user.id, token_type=AuthTokenType.USER)
178+
token_id = token.id
179+
180+
# Delete token and verify handle_async_deletion was called
181+
with outbox_runner():
182+
token.delete()
183+
184+
mock_delete_replica.assert_called_once_with(
185+
apitoken_id=token_id,
186+
region_name=mock.ANY,
187+
)
188+
153189

154190
@control_silo_test
155191
class ApiTokenInternalIntegrationTest(TestCase):

0 commit comments

Comments
 (0)