diff --git a/src/sentry/api/endpoints/api_tokens.py b/src/sentry/api/endpoints/api_tokens.py index 54ce949977af83..504f225bfec511 100644 --- a/src/sentry/api/endpoints/api_tokens.py +++ b/src/sentry/api/endpoints/api_tokens.py @@ -3,6 +3,7 @@ from django.utils.decorators import method_decorator from django.views.decorators.cache import never_cache from rest_framework import serializers +from rest_framework.fields import CharField from rest_framework.permissions import IsAuthenticated from rest_framework.request import Request from rest_framework.response import Response @@ -21,6 +22,7 @@ class ApiTokenSerializer(serializers.Serializer): + name = CharField(max_length=255, allow_blank=True, required=False) scopes = MultipleChoiceField(required=True, choices=settings.SENTRY_SCOPES) @@ -62,6 +64,7 @@ def post(self, request: Request) -> Response: token = ApiToken.objects.create( user_id=request.user.id, + name=result.get("name", None), scope_list=result["scopes"], refresh_token=None, expires_at=None, diff --git a/src/sentry/api/serializers/models/apitoken.py b/src/sentry/api/serializers/models/apitoken.py index b6609b6cf5b91d..a58fb4ee7327bb 100644 --- a/src/sentry/api/serializers/models/apitoken.py +++ b/src/sentry/api/serializers/models/apitoken.py @@ -21,6 +21,7 @@ def serialize(self, obj, attrs, user, **kwargs): data = { "id": str(obj.id), "scopes": obj.get_scopes(), + "name": obj.name, "application": attrs["application"], "expiresAt": obj.expires_at, "dateCreated": obj.date_added, diff --git a/tests/sentry/api/endpoints/test_api_tokens.py b/tests/sentry/api/endpoints/test_api_tokens.py index 234020746c7cad..459195121a5f32 100644 --- a/tests/sentry/api/endpoints/test_api_tokens.py +++ b/tests/sentry/api/endpoints/test_api_tokens.py @@ -81,6 +81,42 @@ def test_invalid_choice(self): assert response.status_code == 400 assert not ApiToken.objects.filter(user=self.user).exists() + def test_with_name(self): + self.login_as(self.user) + url = reverse("sentry-api-0-api-tokens") + response = self.client.post( + url, + data={"name": "testname1", "scopes": ["event:read"]}, + ) + assert response.status_code == 201 + + token = ApiToken.objects.get(user=self.user) + assert token.name == "testname1" + + response = self.client.get(url) + assert response.status_code == 200, response.content + assert len(response.data) == 1 + + assert response.data[0]["name"] == "testname1" + + def test_without_name(self): + self.login_as(self.user) + url = reverse("sentry-api-0-api-tokens") + response = self.client.post( + url, + data={"scopes": ["event:read"]}, + ) + assert response.status_code == 201 + + token = ApiToken.objects.get(user=self.user) + assert token.name is None + + response = self.client.get(url) + assert response.status_code == 200, response.content + assert len(response.data) == 1 + + assert response.data[0]["name"] is None + @control_silo_test class ApiTokensDeleteTest(APITestCase):