diff --git a/src/sentry/api/endpoints/project_key_details.py b/src/sentry/api/endpoints/project_key_details.py index 975dcb87a311ac..8fafefb73cc81b 100644 --- a/src/sentry/api/endpoints/project_key_details.py +++ b/src/sentry/api/endpoints/project_key_details.py @@ -1,21 +1,18 @@ from __future__ import absolute_import from django.db.models import F -from rest_framework import serializers, status +from rest_framework import status from rest_framework.response import Response from sentry import features from sentry.api.base import DocSection from sentry.api.bases.project import ProjectEndpoint from sentry.api.exceptions import ResourceDoesNotExist -from sentry.api.fields.empty_integer import EmptyIntegerField from sentry.api.serializers import serialize +from sentry.api.serializers.rest_framework import ProjectKeySerializer from sentry.models import AuditLogEntryEvent, ProjectKey, ProjectKeyStatus from sentry.utils.apidocs import scenario, attach_scenarios -from sentry.loader.browsersdkversion import ( - get_default_sdk_version_for_project, - get_browser_sdk_version_choices, -) +from sentry.loader.browsersdkversion import get_default_sdk_version_for_project @scenario("DeleteClientKey") @@ -39,20 +36,6 @@ def update_key_scenario(runner): ) -class RateLimitSerializer(serializers.Serializer): - count = EmptyIntegerField(min_value=0, required=False, allow_null=True) - window = EmptyIntegerField(min_value=0, max_value=60 * 60 * 24, required=False, allow_null=True) - - -class KeySerializer(serializers.Serializer): - name = serializers.CharField(max_length=200, required=False, allow_blank=True, allow_null=True) - isActive = serializers.BooleanField(required=False) - rateLimit = RateLimitSerializer(allow_null=True) - browserSdkVersion = serializers.ChoiceField( - choices=get_browser_sdk_version_choices(), required=False - ) - - class ProjectKeyDetailsEndpoint(ProjectEndpoint): doc_section = DocSection.PROJECTS @@ -88,7 +71,7 @@ def put(self, request, project, key_id): except ProjectKey.DoesNotExist: raise ResourceDoesNotExist - serializer = KeySerializer(data=request.data, partial=True) + serializer = ProjectKeySerializer(data=request.data, partial=True) default_version = get_default_sdk_version_for_project(project) if serializer.is_valid(): diff --git a/src/sentry/api/endpoints/project_keys.py b/src/sentry/api/endpoints/project_keys.py index 1534d9db1b45e6..b52e03ea813fdc 100644 --- a/src/sentry/api/endpoints/project_keys.py +++ b/src/sentry/api/endpoints/project_keys.py @@ -1,12 +1,14 @@ from __future__ import absolute_import from django.db.models import F -from rest_framework import serializers, status +from rest_framework import status from rest_framework.response import Response +from sentry import features from sentry.api.base import DocSection from sentry.api.bases.project import ProjectEndpoint from sentry.api.serializers import serialize +from sentry.api.serializers.rest_framework import ProjectKeySerializer from sentry.models import AuditLogEntryEvent, ProjectKey, ProjectKeyStatus from sentry.utils.apidocs import scenario, attach_scenarios @@ -27,12 +29,6 @@ def create_key_scenario(runner): ) -class KeySerializer(serializers.Serializer): - name = serializers.CharField(max_length=64, required=False, allow_blank=True, allow_null=True) - public = serializers.RegexField(r"^[a-f0-9]{32}$", required=False, allow_null=True) - secret = serializers.RegexField(r"^[a-f0-9]{32}$", required=False, allow_null=True) - - class ProjectKeysEndpoint(ProjectEndpoint): doc_section = DocSection.PROJECTS @@ -82,16 +78,27 @@ def post(self, request, project): belong to. :param string name: the name for the new key. """ - serializer = KeySerializer(data=request.data) + serializer = ProjectKeySerializer(data=request.data) if serializer.is_valid(): result = serializer.validated_data + rate_limit_count = None + rate_limit_window = None + + if features.has("projects:rate-limits", project): + ratelimit = result.get("rateLimit", -1) + if ratelimit != -1 and (ratelimit["count"] and ratelimit["window"]): + rate_limit_count = result["rateLimit"]["count"] + rate_limit_window = result["rateLimit"]["window"] + key = ProjectKey.objects.create( project=project, label=result.get("name"), public_key=result.get("public"), secret_key=result.get("secret"), + rate_limit_count=rate_limit_count, + rate_limit_window=rate_limit_window, ) self.create_audit_entry( diff --git a/src/sentry/api/serializers/rest_framework/project_key.py b/src/sentry/api/serializers/rest_framework/project_key.py new file mode 100644 index 00000000000000..88961471a33793 --- /dev/null +++ b/src/sentry/api/serializers/rest_framework/project_key.py @@ -0,0 +1,21 @@ +from __future__ import absolute_import + +from rest_framework import serializers +from sentry.api.fields.empty_integer import EmptyIntegerField +from sentry.loader.browsersdkversion import get_browser_sdk_version_choices + + +class RateLimitSerializer(serializers.Serializer): + count = EmptyIntegerField(min_value=0, required=False, allow_null=True) + window = EmptyIntegerField(min_value=0, max_value=60 * 60 * 24, required=False, allow_null=True) + + +class ProjectKeySerializer(serializers.Serializer): + name = serializers.CharField(max_length=64, required=False, allow_blank=True, allow_null=True) + public = serializers.RegexField(r"^[a-f0-9]{32}$", required=False, allow_null=True) + secret = serializers.RegexField(r"^[a-f0-9]{32}$", required=False, allow_null=True) + rateLimit = RateLimitSerializer(required=False, allow_null=True) + isActive = serializers.BooleanField(required=False) + browserSdkVersion = serializers.ChoiceField( + choices=get_browser_sdk_version_choices(), required=False + ) diff --git a/tests/sentry/api/endpoints/test_project_key_details.py b/tests/sentry/api/endpoints/test_project_key_details.py index 7d2c3bb73add18..0029de7085fd1d 100644 --- a/tests/sentry/api/endpoints/test_project_key_details.py +++ b/tests/sentry/api/endpoints/test_project_key_details.py @@ -2,7 +2,7 @@ from django.core.urlresolvers import reverse -from sentry.models import ProjectKey +from sentry.models import ProjectKey, ProjectKeyStatus from sentry.testutils import APITestCase @@ -98,6 +98,24 @@ def test_simple_rate_limit(self): assert key.rate_limit_count == 1 assert key.rate_limit_window == 60 + def test_deactivate(self): + project = self.create_project() + key = ProjectKey.objects.get_or_create(project=project)[0] + self.login_as(user=self.user) + url = reverse( + "sentry-api-0-project-key-details", + kwargs={ + "organization_slug": project.organization.slug, + "project_slug": project.slug, + "key_id": key.public_key, + }, + ) + response = self.client.put(url, {"isActive": False, "name": "hello world"}) + assert response.status_code == 200 + key = ProjectKey.objects.get(id=key.id) + assert key.label == "hello world" + assert key.status == ProjectKeyStatus.INACTIVE + class DeleteProjectKeyTest(APITestCase): def test_simple(self): diff --git a/tests/sentry/api/endpoints/test_project_keys.py b/tests/sentry/api/endpoints/test_project_keys.py index 43b3ea9780fe37..caecaf721c1ada 100644 --- a/tests/sentry/api/endpoints/test_project_keys.py +++ b/tests/sentry/api/endpoints/test_project_keys.py @@ -29,10 +29,14 @@ def test_simple(self): "sentry-api-0-project-keys", kwargs={"organization_slug": project.organization.slug, "project_slug": project.slug}, ) - resp = self.client.post(url, data={"name": "hello world"}) + resp = self.client.post( + url, data={"name": "hello world", "rateLimit": {"count": 10, "window": 60}} + ) assert resp.status_code == 201, resp.content key = ProjectKey.objects.get(public_key=resp.data["public"]) assert key.label == "hello world" + assert key.rate_limit_count == 10 + assert key.rate_limit_window == 60 def test_minimal_args(self): project = self.create_project()