Skip to content

Commit d6f6862

Browse files
snigdhasDominikB2014
authored andcommitted
Add 'projects' query param for the release deploys endpoint (#35916)
Add 'projects' query param for the release deploys endpoint
1 parent 46ea5f6 commit d6f6862

File tree

2 files changed

+123
-20
lines changed

2 files changed

+123
-20
lines changed

src/sentry/api/endpoints/release_deploys.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55
from rest_framework.response import Response
66

77
from sentry.api.bases.organization import OrganizationReleasesBaseEndpoint
8-
from sentry.api.exceptions import ResourceDoesNotExist
8+
from sentry.api.exceptions import ParameterValidationError, ResourceDoesNotExist
99
from sentry.api.paginator import OffsetPaginator
1010
from sentry.api.serializers import serialize
11+
from sentry.api.serializers.rest_framework.project import ProjectField
1112
from sentry.models import Deploy, Environment, Release, ReleaseProjectEnvironment
1213
from sentry.signals import deploy_created
1314

@@ -18,6 +19,9 @@ class DeploySerializer(serializers.Serializer):
1819
url = serializers.URLField(required=False, allow_blank=True, allow_null=True)
1920
dateStarted = serializers.DateTimeField(required=False, allow_null=True)
2021
dateFinished = serializers.DateTimeField(required=False, allow_null=True)
22+
projects = serializers.ListField(
23+
child=ProjectField(scope="project:read"), required=False, allow_empty=False
24+
)
2125

2226
def validate_environment(self, value):
2327
if not Environment.is_valid_name(value):
@@ -65,6 +69,9 @@ def post(self, request: Request, organization, version) -> Response:
6569
:pparam string version: the version identifier of the release.
6670
:param string environment: the environment you're deploying to
6771
:param string name: the optional name of the deploy
72+
:param list projects: the optional list of project slugs to
73+
create a deploy within. If not provided, deploys
74+
are created for all of the release's projects.
6875
:param url url: the optional url that points to the deploy
6976
:param datetime dateStarted: an optional date that indicates when
7077
the deploy started
@@ -80,11 +87,21 @@ def post(self, request: Request, organization, version) -> Response:
8087
if not self.has_release_permission(request, organization, release):
8188
raise ResourceDoesNotExist
8289

83-
serializer = DeploySerializer(data=request.data)
90+
serializer = DeploySerializer(
91+
data=request.data, context={"organization": organization, "access": request.access}
92+
)
8493

8594
if serializer.is_valid():
86-
projects = list(release.projects.all())
8795
result = serializer.validated_data
96+
release_projects = list(release.projects.all())
97+
projects = result.get("projects", release_projects)
98+
invalid_projects = {project.slug for project in projects} - {
99+
project.slug for project in release_projects
100+
}
101+
if len(invalid_projects) > 0:
102+
raise ParameterValidationError(
103+
f"Invalid projects ({', '.join(invalid_projects)}) for release {release.version}"
104+
)
88105

89106
env = Environment.objects.get_or_create(
90107
name=result["environment"], organization_id=organization.id

tests/sentry/api/endpoints/test_release_deploys.py

Lines changed: 103 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -46,24 +46,31 @@ def test_simple(self):
4646

4747

4848
class ReleaseDeploysCreateTest(APITestCase):
49+
def setUp(self):
50+
user = self.create_user(is_staff=False, is_superuser=False)
51+
self.org = self.create_organization()
52+
self.org.save()
53+
54+
team = self.create_team(organization=self.org)
55+
self.project = self.create_project(name="foo", organization=self.org, teams=[team])
56+
57+
self.create_member(teams=[team], user=user, organization=self.org)
58+
self.login_as(user=user)
59+
4960
def test_simple(self):
50-
project = self.create_project(name="foo")
51-
release = Release.objects.create(
52-
organization_id=project.organization_id, version="1", total_deploys=0
53-
)
54-
release.add_project(project)
61+
release = Release.objects.create(organization_id=self.org.id, version="1", total_deploys=0)
62+
release.add_project(self.project)
5563

56-
environment = Environment.objects.create(
57-
organization_id=project.organization_id, name="production"
58-
)
64+
environment = Environment.objects.create(organization_id=self.org.id, name="production")
5965

6066
url = reverse(
6167
"sentry-api-0-organization-release-deploys",
62-
kwargs={"organization_slug": project.organization.slug, "version": release.version},
68+
kwargs={
69+
"organization_slug": self.org.slug,
70+
"version": release.version,
71+
},
6372
)
6473

65-
self.login_as(user=self.user)
66-
6774
response = self.client.post(
6875
url, data={"name": "foo", "environment": "production", "url": "https://www.example.com"}
6976
)
@@ -84,23 +91,102 @@ def test_simple(self):
8491
assert release.last_deploy_id == deploy.id
8592

8693
rpe = ReleaseProjectEnvironment.objects.get(
87-
project=project, release=release, environment=environment
94+
project=self.project, release=release, environment=environment
95+
)
96+
assert rpe.last_deploy_id == deploy.id
97+
98+
def test_with_project_slugs(self):
99+
project_bar = self.create_project(organization=self.org, name="bar")
100+
release = Release.objects.create(organization_id=self.org.id, version="1", total_deploys=0)
101+
release.add_project(self.project)
102+
release.add_project(project_bar)
103+
104+
environment = Environment.objects.create(organization_id=self.org.id, name="production")
105+
106+
url = reverse(
107+
"sentry-api-0-organization-release-deploys",
108+
kwargs={
109+
"organization_slug": self.org.slug,
110+
"version": release.version,
111+
},
112+
)
113+
114+
response = self.client.post(
115+
url,
116+
data={
117+
"name": "foo_bar",
118+
"environment": "production",
119+
"url": "https://www.example.com",
120+
"projects": [self.project.slug, project_bar.slug],
121+
},
122+
)
123+
assert response.status_code == 201, response.content
124+
assert response.data["name"] == "foo_bar"
125+
assert response.data["url"] == "https://www.example.com"
126+
assert response.data["environment"] == "production"
127+
128+
deploy = Deploy.objects.get(id=response.data["id"])
129+
130+
assert deploy.name == "foo_bar"
131+
assert deploy.environment_id == environment.id
132+
assert deploy.url == "https://www.example.com"
133+
assert deploy.release == release
134+
135+
release = Release.objects.get(id=release.id)
136+
assert release.total_deploys == 1
137+
assert release.last_deploy_id == deploy.id
138+
139+
rpe = ReleaseProjectEnvironment.objects.get(
140+
project=self.project, release=release, environment=environment
141+
)
142+
assert rpe.last_deploy_id == deploy.id
143+
144+
rpe = ReleaseProjectEnvironment.objects.get(
145+
project=project_bar, release=release, environment=environment
88146
)
89147
assert rpe.last_deploy_id == deploy.id
90148

149+
def test_with_invalid_project_slug(self):
150+
bar_project = self.create_project(organization=self.org, name="bar")
151+
release = Release.objects.create(organization_id=self.org.id, version="1", total_deploys=0)
152+
release.add_project(self.project)
153+
154+
url = reverse(
155+
"sentry-api-0-organization-release-deploys",
156+
kwargs={
157+
"organization_slug": self.org.slug,
158+
"version": release.version,
159+
},
160+
)
161+
162+
response = self.client.post(
163+
url,
164+
data={
165+
"name": "foo",
166+
"environment": "production",
167+
"url": "https://www.example.com",
168+
"projects": [bar_project.slug],
169+
},
170+
)
171+
assert response.status_code == 400, response.content
172+
assert response.data["detail"]["code"] == "parameter-validation-error"
173+
assert "Invalid projects" in response.data["detail"]["message"]
174+
assert 0 == Deploy.objects.count()
175+
91176
def test_environment_validation_failure(self):
92-
project = self.create_project(name="example")
93177
release = Release.objects.create(
94-
organization_id=project.organization_id, version="123", total_deploys=0
178+
organization_id=self.org.id, version="123", total_deploys=0
95179
)
96-
release.add_project(project)
180+
release.add_project(self.project)
97181

98182
url = reverse(
99183
"sentry-api-0-organization-release-deploys",
100-
kwargs={"organization_slug": project.organization.slug, "version": release.version},
184+
kwargs={
185+
"organization_slug": self.org.slug,
186+
"version": release.version,
187+
},
101188
)
102189

103-
self.login_as(user=self.user)
104190
response = self.client.post(
105191
url, data={"name": "foo", "environment": "bad/name", "url": "https://www.example.com"}
106192
)

0 commit comments

Comments
 (0)