Skip to content

Commit 91cab2c

Browse files
feat(prevent): send consent URL when no-consent
ticket: prevent-275 There are some assumptions in these changes: 1. The URL for the settings of an org depends on the `sentry_org.slug`. 2. `github_org_name` is not necessarily the same as `sentry_org.slug` 3. Constructing such URL is non-trivial without the base URL for the Sentry instance 4. It's OK to return _any_ of the valid sentry orgs for a prevent-AI request So based on those assumptions these changes include the URL for the org settings such that we can slap that URL in the "no consent given" message - that appears if you request `@sentry review` in a PR without giving the consent. We want this to add a clearer Call To Action in that message, making it more useful
1 parent cd644ca commit 91cab2c

File tree

2 files changed

+38
-13
lines changed

2 files changed

+38
-13
lines changed

src/sentry/seer/endpoints/seer_rpc.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,20 +279,25 @@ def get_organization_autofix_consent(*, org_id: int) -> dict:
279279
# Used by the seer GH app to check for permissions before posting to an org
280280
def get_organization_seer_consent_by_org_name(
281281
*, org_name: str, provider: str = "github"
282-
) -> dict[str, bool]:
282+
) -> dict[str, bool | str | None]:
283283
org_integrations = integration_service.get_organization_integrations(
284284
providers=[provider], name=org_name
285285
)
286286

287+
# The URL where an org admin can enable Prevent-AI features
288+
# Only returned if the org is not already consented
289+
consent_url = None
287290
for org_integration in org_integrations:
288291
try:
289292
org = Organization.objects.get(id=org_integration.organization_id)
290293
if _can_use_prevent_ai_features(org):
291294
return {"consent": True}
295+
# If this is the last org we will return this URL as the consent URL
296+
consent_url = org.absolute_url("/settings/organization/")
292297
except Organization.DoesNotExist:
293298
continue
294299

295-
return {"consent": False}
300+
return {"consent": False, "consent_url": consent_url}
296301

297302

298303
def get_attribute_names(*, org_id: int, project_ids: list[int], stats_period: str) -> dict:

tests/sentry/seer/endpoints/test_seer_rpc.py

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ def test_get_organization_seer_consent_by_org_name_no_integrations(self) -> None
7171
"""Test when no organization integrations are found"""
7272
# Test with a non-existent organization name
7373
result = get_organization_seer_consent_by_org_name(org_name="non-existent-org")
74-
assert result == {"consent": False}
74+
assert result == {"consent": False, "consent_url": None}
7575

7676
def test_get_organization_seer_consent_by_org_name_no_consent(self) -> None:
7777
"""Test when organization exists but has no consent"""
@@ -89,7 +89,10 @@ def test_get_organization_seer_consent_by_org_name_no_consent(self) -> None:
8989

9090
result = get_organization_seer_consent_by_org_name(org_name="test-org")
9191

92-
assert result == {"consent": False}
92+
assert result == {
93+
"consent": False,
94+
"consent_url": self.organization.absolute_url("/settings/organization/"),
95+
}
9396

9497
def test_get_organization_seer_consent_by_org_name_with_default_pr_review_enabled(self) -> None:
9598
"""Test when organization has seer acknowledgement"""
@@ -103,7 +106,10 @@ def test_get_organization_seer_consent_by_org_name_with_default_pr_review_enable
103106
result = get_organization_seer_consent_by_org_name(org_name="test-org")
104107

105108
# Should return True since PR review is enabled by default
106-
assert result == {"consent": False}
109+
assert result == {
110+
"consent": False,
111+
"consent_url": self.organization.absolute_url("/settings/organization/"),
112+
}
107113

108114
def test_get_organization_seer_consent_by_org_name_multiple_orgs_one_with_consent(self) -> None:
109115
"""Test when multiple organizations exist, one with consent"""
@@ -158,7 +164,10 @@ def test_get_organization_seer_consent_by_org_name_with_hide_ai_features_enabled
158164
result = get_organization_seer_consent_by_org_name(org_name="test-org")
159165

160166
# Should return False because hide_ai_features=True makes this org not contribute consent
161-
assert result == {"consent": False}
167+
assert result == {
168+
"consent": False,
169+
"consent_url": self.organization.absolute_url("/settings/organization/"),
170+
}
162171

163172
def test_get_organization_seer_consent_by_org_name_with_hide_ai_features_disabled(
164173
self,
@@ -174,11 +183,13 @@ def test_get_organization_seer_consent_by_org_name_with_hide_ai_features_disable
174183
# Explicitly disable hide_ai_features
175184
OrganizationOption.objects.set_value(self.organization, "sentry:hide_ai_features", False)
176185

177-
# PR review is enabled by default, so (NOT hide_ai_features AND pr_review_enabled) = True
178186
result = get_organization_seer_consent_by_org_name(org_name="test-org")
179187

180188
# Should return False because hide_ai_features=False and PR review is disabled by default
181-
assert result == {"consent": False}
189+
assert result == {
190+
"consent": False,
191+
"consent_url": self.organization.absolute_url("/settings/organization/"),
192+
}
182193

183194
def test_get_organization_seer_consent_by_org_name_multiple_orgs_with_hide_ai_features(
184195
self,
@@ -210,14 +221,17 @@ def test_get_organization_seer_consent_by_org_name_multiple_orgs_with_hide_ai_fe
210221
result = get_organization_seer_consent_by_org_name(org_name="test-org")
211222

212223
# Should return False because second org has (NOT hide_ai_features AND pr_review_enabled) = False
213-
assert result == {"consent": False}
224+
assert result == {
225+
"consent": False,
226+
"consent_url": org_with_visible_ai.absolute_url("/settings/organization/"),
227+
}
214228

215229
def test_get_organization_seer_consent_by_org_name_multiple_orgs_all_hide_ai_features(
216230
self,
217231
):
218232
"""Test multiple orgs where all have hide_ai_features=True"""
219-
org1 = self.create_organization(owner=self.user)
220-
org2 = self.create_organization(owner=self.user)
233+
org1 = self.create_organization(owner=self.user, slug="test-org")
234+
org2 = self.create_organization(owner=self.user, slug="test-org")
221235

222236
# Create integrations for both organizations with the same name
223237
self.create_integration(
@@ -240,7 +254,10 @@ def test_get_organization_seer_consent_by_org_name_multiple_orgs_all_hide_ai_fea
240254
result = get_organization_seer_consent_by_org_name(org_name="test-org")
241255

242256
# Should return False because no org can contribute consent (all have hide_ai_features=True)
243-
assert result == {"consent": False}
257+
assert result == {
258+
"consent": False,
259+
"consent_url": org2.absolute_url("/settings/organization/"),
260+
}
244261

245262
def test_get_organization_seer_consent_by_org_name_hide_ai_false_pr_review_false(
246263
self,
@@ -262,7 +279,10 @@ def test_get_organization_seer_consent_by_org_name_hide_ai_false_pr_review_false
262279
result = get_organization_seer_consent_by_org_name(org_name="test-org")
263280

264281
# Should return False because even though hide_ai_features=False, pr_review_enabled=False
265-
assert result == {"consent": False}
282+
assert result == {
283+
"consent": False,
284+
"consent_url": self.organization.absolute_url("/settings/organization/"),
285+
}
266286

267287
@responses.activate
268288
@override_settings(SEER_GHE_ENCRYPT_KEY=TEST_FERNET_KEY)

0 commit comments

Comments
 (0)