Skip to content

Commit

Permalink
feat(project-create): Add origin to analytics event (#86088)
Browse files Browse the repository at this point in the history
Pass origin of new project to the `project.created` analytics event

- part of #85341
  • Loading branch information
ArthurKnaus authored Feb 28, 2025
1 parent a9548cd commit e6c385f
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 8 deletions.
1 change: 1 addition & 0 deletions src/sentry/analytics/events/project_created.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class ProjectCreatedEvent(analytics.Event):
analytics.Attribute("user_id", required=False),
analytics.Attribute("default_user_id"),
analytics.Attribute("organization_id"),
analytics.Attribute("origin", required=False),
analytics.Attribute("project_id"),
analytics.Attribute("platform", required=False),
analytics.Attribute("updated_empty_state", required=False),
Expand Down
7 changes: 4 additions & 3 deletions src/sentry/api/endpoints/organization_projects_experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,14 +184,14 @@ def post(self, request: Request, organization: Organization) -> Response:
"organization": team.organization,
"target_object": project.id,
}

if request.data.get("origin"):
origin = request.data.get("origin")
if origin:
self.create_audit_entry(
**common_audit_data,
event=audit_log.get_event_id("PROJECT_ADD_WITH_ORIGIN"),
data={
**project.get_audit_log_data(),
"origin": request.data.get("origin"),
"origin": origin,
},
)
else:
Expand All @@ -205,6 +205,7 @@ def post(self, request: Request, organization: Organization) -> Response:
project=project,
user=request.user,
default_rules=result.get("default_rules", True),
origin=origin,
sender=self,
)
self.create_audit_entry(
Expand Down
6 changes: 4 additions & 2 deletions src/sentry/api/endpoints/team_projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,13 +230,14 @@ def post(self, request: Request, team: Team) -> Response:
"target_object": project.id,
}

if request.data.get("origin"):
origin = request.data.get("origin")
if origin:
self.create_audit_entry(
**common_audit_data,
event=audit_log.get_event_id("PROJECT_ADD_WITH_ORIGIN"),
data={
**project.get_audit_log_data(),
"origin": request.data.get("origin"),
"origin": origin,
},
)
else:
Expand All @@ -250,6 +251,7 @@ def post(self, request: Request, team: Team) -> Response:
project=project,
user=request.user,
default_rules=result.get("default_rules", True),
origin=origin,
sender=self,
)

Expand Down
3 changes: 2 additions & 1 deletion src/sentry/receivers/onboarding.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@


@project_created.connect(weak=False)
def record_new_project(project, user=None, user_id=None, **kwargs):
def record_new_project(project, user=None, user_id=None, origin=None, **kwargs):

scope = sentry_sdk.get_current_scope()
scope.set_extra("project_id", project.id)
Expand Down Expand Up @@ -90,6 +90,7 @@ def record_new_project(project, user=None, user_id=None, **kwargs):
user_id=user_id,
default_user_id=default_user_id,
organization_id=project.organization_id,
origin=origin,
project_id=project.id,
platform=project.platform,
updated_empty_state=features.has(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import re
from unittest.mock import patch
from unittest import mock
from unittest.mock import Mock, patch

from django.utils.text import slugify

Expand All @@ -14,6 +15,7 @@
from sentry.models.project import Project
from sentry.models.rule import Rule
from sentry.models.team import Team
from sentry.signals import project_created
from sentry.testutils.cases import APITestCase
from sentry.testutils.helpers.features import with_feature

Expand Down Expand Up @@ -282,3 +284,36 @@ def test_disable_member_project_creation(self):
name="foo",
status_code=201,
)

@with_feature(["organizations:team-roles"])
@patch(
"sentry.api.endpoints.organization_projects_experiment.OrganizationProjectsExperimentEndpoint.create_audit_entry"
)
def test_create_project_with_origin(self, create_audit_entry):
signal_handler = Mock()
project_created.connect(signal_handler)

response = self.get_success_response(
self.organization.slug,
name="foo",
origin="ui",
status_code=201,
)

project = Project.objects.get(id=response.data["id"])
# Verify audit log was created
create_audit_entry.assert_any_call(
request=mock.ANY,
organization=self.organization,
target_object=project.id,
event=1154,
data={
**project.get_audit_log_data(),
"origin": "ui",
},
)

# Verify origin is passed to project_created signal
assert signal_handler.call_count == 1
assert signal_handler.call_args[1]["origin"] == "ui"
project_created.disconnect(signal_handler)
10 changes: 9 additions & 1 deletion tests/sentry/api/endpoints/test_team_projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from sentry.models.project import Project
from sentry.models.rule import Rule
from sentry.notifications.types import FallthroughChoiceType
from sentry.signals import alert_rule_created
from sentry.signals import alert_rule_created, project_created
from sentry.slug.errors import DEFAULT_SLUG_ERROR_MESSAGE
from sentry.testutils.cases import APITestCase
from sentry.testutils.helpers.options import override_options
Expand Down Expand Up @@ -356,6 +356,9 @@ def test_builtin_symbol_sources_not_electron(self):

@patch("sentry.api.endpoints.team_projects.TeamProjectsEndpoint.create_audit_entry")
def test_create_project_with_origin(self, create_audit_entry):
signal_handler = Mock()
project_created.connect(signal_handler)

response = self.get_success_response(
self.organization.slug,
self.team.slug,
Expand All @@ -379,3 +382,8 @@ def test_create_project_with_origin(self, create_audit_entry):
"origin": "ui",
},
)

# Verify origin is passed to project_created signal
assert signal_handler.call_count == 1
assert signal_handler.call_args[1]["origin"] == "ui"
project_created.disconnect(signal_handler)
27 changes: 27 additions & 0 deletions tests/sentry/receivers/test_onboarding.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,32 @@ def test_project_created(self):
)
assert task is not None

@patch("sentry.analytics.record")
def test_project_created_with_origin(self, record_analytics):
project = self.create_project()
project_created.send(
project=project, user=self.user, default_rules=False, sender=type(project), origin="ui"
)

task = OrganizationOnboardingTask.objects.get(
organization=self.organization,
task=OnboardingTask.FIRST_PROJECT,
status=OnboardingTaskStatus.COMPLETE,
)
assert task is not None

# Verify origin is passed to analytics event
record_analytics.assert_called_with(
"project.created",
user_id=self.user.id,
default_user_id=self.organization.default_owner_id,
organization_id=self.organization.id,
project_id=project.id,
platform=project.platform,
updated_empty_state=False,
origin="ui",
)

def test_first_event_received(self):
now = timezone.now()
project = self.create_project(first_event=now, platform="javascript")
Expand Down Expand Up @@ -834,6 +860,7 @@ def test_new_onboarding_complete(self, record_analytics):
project_id=project.id,
platform=project.platform,
updated_empty_state=False,
origin=None,
)

# Set up tracing
Expand Down

0 comments on commit e6c385f

Please sign in to comment.