Skip to content

Commit e42180f

Browse files
committed
feat(investigations): add platform integration
1 parent d3411e1 commit e42180f

6 files changed

Lines changed: 290 additions & 1 deletion

File tree

src/sentry/backup/comparators.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1005,6 +1005,30 @@ def get_default_comparators() -> dict[str, list[JSONScrubbingComparator]]:
10051005
"insights.insightsstarredsegment": [
10061006
DateUpdatedComparator("date_updated", "date_added")
10071007
],
1008+
"investigations.investigation": [
1009+
DateUpdatedComparator("date_updated", "date_added"),
1010+
UUID4Comparator("uuid"),
1011+
],
1012+
"investigations.investigationcell": [
1013+
DateUpdatedComparator("date_updated", "date_added"),
1014+
UUID4Comparator("uuid"),
1015+
IgnoredComparator("current_execution"),
1016+
],
1017+
"investigations.investigationcellcomment": [
1018+
DateUpdatedComparator("date_updated", "date_added"),
1019+
UUID4Comparator("uuid"),
1020+
],
1021+
"investigations.investigationfavoriteuser": [
1022+
DateUpdatedComparator("date_updated", "date_added")
1023+
],
1024+
"investigations.investigationcellexecution": [
1025+
DateUpdatedComparator("date_updated", "date_added"),
1026+
UUID4Comparator("uuid", "request_id"),
1027+
],
1028+
"investigations.investigationparameter": [
1029+
DateUpdatedComparator("date_updated", "date_added"),
1030+
UUID4Comparator("uuid"),
1031+
],
10081032
"monitors.monitor": [UUID4Comparator("guid")],
10091033
"replays.organizationmemberreplayaccess": [
10101034
DateUpdatedComparator("date_updated", "date_added")

src/sentry/features/temporary.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ def register_temporary_features(manager: FeatureManager) -> None:
112112
manager.add("organizations:expose-migrated-discover-queries", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=True)
113113
# Enable GenAI features such as Autofix and Issue Summary
114114
manager.add("organizations:gen-ai-features", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=True)
115+
# Enable organization investigation notebooks.
116+
manager.add("organizations:investigations", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=True)
115117
# Enable the 'translate' functionality for GenAI on the explore > traces page
116118
manager.add("organizations:gen-ai-search-agent-translate", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=True)
117119
# Enable AI search bar on the explore > metrics tab

src/sentry/testutils/factories.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,21 @@
7979
from sentry.integrations.services.integration import RpcIntegration
8080
from sentry.integrations.types import ExternalProviders
8181
from sentry.integrations.utils.hostname import instance_hostname
82+
from sentry.investigations.models import (
83+
Investigation,
84+
InvestigationCell,
85+
InvestigationCellComment,
86+
InvestigationCellDependency,
87+
InvestigationCellExecution,
88+
InvestigationCellExecutionProject,
89+
InvestigationCellParameter,
90+
InvestigationCellReaction,
91+
InvestigationCommentReaction,
92+
InvestigationCommentTeamMention,
93+
InvestigationCommentUserMention,
94+
InvestigationParameter,
95+
InvestigationPermissions,
96+
)
8297
from sentry.issue_detection.performance_problem import PerformanceProblem
8398
from sentry.issues.action_log.types import GroupActionType, GroupActorType
8499
from sentry.issues.grouptype import get_group_type_by_type_id
@@ -408,6 +423,83 @@ def _set_sample_rate_from_error_sampling(normalized_data: MutableMapping[str, An
408423

409424
# TODO(dcramer): consider moving to something more scalable like factoryboy
410425
class Factories:
426+
@staticmethod
427+
@assume_test_silo_mode(SiloMode.CELL)
428+
def create_investigation(organization, created_by=None, **kwargs):
429+
return Investigation.objects.create(
430+
organization=organization,
431+
created_by_id=created_by.id if created_by else None,
432+
**kwargs,
433+
)
434+
435+
@staticmethod
436+
@assume_test_silo_mode(SiloMode.CELL)
437+
def create_investigation_permissions(investigation, **kwargs):
438+
return InvestigationPermissions.objects.create(investigation=investigation, **kwargs)
439+
440+
@staticmethod
441+
@assume_test_silo_mode(SiloMode.CELL)
442+
def create_investigation_cell(investigation, position=0, kind="text", **kwargs):
443+
return InvestigationCell.objects.create(
444+
investigation=investigation, position=position, kind=kind, **kwargs
445+
)
446+
447+
@staticmethod
448+
@assume_test_silo_mode(SiloMode.CELL)
449+
def create_investigation_cell_dependency(cell, depends_on):
450+
return InvestigationCellDependency.objects.create(cell=cell, depends_on=depends_on)
451+
452+
@staticmethod
453+
@assume_test_silo_mode(SiloMode.CELL)
454+
def create_investigation_parameter(investigation, **kwargs):
455+
return InvestigationParameter.objects.create(investigation=investigation, **kwargs)
456+
457+
@staticmethod
458+
@assume_test_silo_mode(SiloMode.CELL)
459+
def create_investigation_cell_parameter(cell, parameter, **kwargs):
460+
return InvestigationCellParameter.objects.create(cell=cell, parameter=parameter, **kwargs)
461+
462+
@staticmethod
463+
@assume_test_silo_mode(SiloMode.CELL)
464+
def create_investigation_cell_execution(cell, **kwargs):
465+
return InvestigationCellExecution.objects.create(cell=cell, **kwargs)
466+
467+
@staticmethod
468+
@assume_test_silo_mode(SiloMode.CELL)
469+
def create_investigation_cell_execution_project(execution, project):
470+
return InvestigationCellExecutionProject.objects.create(
471+
execution=execution, project=project
472+
)
473+
474+
@staticmethod
475+
@assume_test_silo_mode(SiloMode.CELL)
476+
def create_investigation_cell_comment(cell, author=None, **kwargs):
477+
return InvestigationCellComment.objects.create(
478+
cell=cell, author_id=author.id if author else None, **kwargs
479+
)
480+
481+
@staticmethod
482+
@assume_test_silo_mode(SiloMode.CELL)
483+
def create_investigation_cell_reaction(cell, user, **kwargs):
484+
return InvestigationCellReaction.objects.create(cell=cell, user_id=user.id, **kwargs)
485+
486+
@staticmethod
487+
@assume_test_silo_mode(SiloMode.CELL)
488+
def create_investigation_comment_reaction(comment, user, **kwargs):
489+
return InvestigationCommentReaction.objects.create(
490+
comment=comment, user_id=user.id, **kwargs
491+
)
492+
493+
@staticmethod
494+
@assume_test_silo_mode(SiloMode.CELL)
495+
def create_investigation_comment_user_mention(comment, user):
496+
return InvestigationCommentUserMention.objects.create(comment=comment, user_id=user.id)
497+
498+
@staticmethod
499+
@assume_test_silo_mode(SiloMode.CELL)
500+
def create_investigation_comment_team_mention(comment, team):
501+
return InvestigationCommentTeamMention.objects.create(comment=comment, team=team)
502+
411503
@staticmethod
412504
@assume_test_silo_mode(SiloMode.CELL)
413505
def create_organization(name=None, owner=None, cell: Cell | str | None = None, **kwargs):

src/sentry/testutils/fixtures.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,45 @@ def organization_integration(self):
184184
def create_organization(self, *args, **kwargs):
185185
return Factories.create_organization(*args, **kwargs)
186186

187+
def create_investigation(self, *args, **kwargs):
188+
return Factories.create_investigation(*args, **kwargs)
189+
190+
def create_investigation_permissions(self, *args, **kwargs):
191+
return Factories.create_investigation_permissions(*args, **kwargs)
192+
193+
def create_investigation_cell(self, *args, **kwargs):
194+
return Factories.create_investigation_cell(*args, **kwargs)
195+
196+
def create_investigation_cell_dependency(self, *args, **kwargs):
197+
return Factories.create_investigation_cell_dependency(*args, **kwargs)
198+
199+
def create_investigation_parameter(self, *args, **kwargs):
200+
return Factories.create_investigation_parameter(*args, **kwargs)
201+
202+
def create_investigation_cell_parameter(self, *args, **kwargs):
203+
return Factories.create_investigation_cell_parameter(*args, **kwargs)
204+
205+
def create_investigation_cell_execution(self, *args, **kwargs):
206+
return Factories.create_investigation_cell_execution(*args, **kwargs)
207+
208+
def create_investigation_cell_execution_project(self, *args, **kwargs):
209+
return Factories.create_investigation_cell_execution_project(*args, **kwargs)
210+
211+
def create_investigation_cell_comment(self, *args, **kwargs):
212+
return Factories.create_investigation_cell_comment(*args, **kwargs)
213+
214+
def create_investigation_cell_reaction(self, *args, **kwargs):
215+
return Factories.create_investigation_cell_reaction(*args, **kwargs)
216+
217+
def create_investigation_comment_reaction(self, *args, **kwargs):
218+
return Factories.create_investigation_comment_reaction(*args, **kwargs)
219+
220+
def create_investigation_comment_user_mention(self, *args, **kwargs):
221+
return Factories.create_investigation_comment_user_mention(*args, **kwargs)
222+
223+
def create_investigation_comment_team_mention(self, *args, **kwargs):
224+
return Factories.create_investigation_comment_team_mention(*args, **kwargs)
225+
187226
def create_member(self, *args, **kwargs):
188227
return Factories.create_member(*args, **kwargs)
189228

src/sentry/testutils/helpers/backups.py

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,24 @@
5858
from sentry.integrations.models.data_forwarder_project import DataForwarderProject
5959
from sentry.integrations.models.integration import Integration
6060
from sentry.integrations.models.organization_integration import OrganizationIntegration
61+
from sentry.investigations.models import (
62+
Investigation,
63+
InvestigationCell,
64+
InvestigationCellComment,
65+
InvestigationCellDependency,
66+
InvestigationCellExecution,
67+
InvestigationCellExecutionProject,
68+
InvestigationCellParameter,
69+
InvestigationCellReaction,
70+
InvestigationCommentReaction,
71+
InvestigationCommentTeamMention,
72+
InvestigationCommentUserMention,
73+
InvestigationFavoriteUser,
74+
InvestigationParameter,
75+
InvestigationPermissions,
76+
InvestigationPermissionsTeam,
77+
InvestigationProject,
78+
)
6179
from sentry.models.activity import Activity
6280
from sentry.models.apiauthorization import ApiAuthorization
6381
from sentry.models.apidevicecode import ApiDeviceCode
@@ -841,6 +859,105 @@ def create_exhaustive_organization(
841859
overrides={"write_key": "test_override_write_key"},
842860
)
843861

862+
investigation = Investigation.objects.create(
863+
organization=org,
864+
created_by_id=owner_id,
865+
title=f"Investigation in {slug}",
866+
status="archived",
867+
template_key="breached_metric",
868+
template_version=2,
869+
source_type="breached_metric",
870+
source_ref={"groupId": str(group.id)},
871+
filters={"environment": ["production"]},
872+
version=2,
873+
)
874+
InvestigationProject.objects.create(investigation=investigation, project=project)
875+
InvestigationFavoriteUser.objects.create(investigation=investigation, user_id=owner_id)
876+
investigation_permissions = InvestigationPermissions.objects.create(
877+
investigation=investigation,
878+
is_editable_by_everyone=False,
879+
)
880+
InvestigationPermissionsTeam.objects.create(
881+
permissions=investigation_permissions, team=team
882+
)
883+
parameter = InvestigationParameter.objects.create(
884+
investigation=investigation,
885+
key="environment",
886+
label="Environment",
887+
description="Environment filter",
888+
type="string",
889+
required=True,
890+
constraints={"maxLength": 64},
891+
default_value="development",
892+
saved_value="production",
893+
source="user",
894+
position=1,
895+
version=2,
896+
)
897+
upstream_cell = InvestigationCell.objects.create(
898+
investigation=investigation,
899+
created_by_id=owner_id,
900+
last_edited_by_id=owner_id,
901+
position=1,
902+
kind="text",
903+
title="Upstream",
904+
content="Upstream evidence",
905+
prompt="Create evidence",
906+
generated_content="Generated evidence",
907+
config={"language": "markdown"},
908+
display={"type": "markdown"},
909+
version=2,
910+
stale_at=timezone.now(),
911+
deleted_at=timezone.now(),
912+
)
913+
cell = InvestigationCell.objects.create(
914+
investigation=investigation,
915+
created_by_id=owner_id,
916+
last_edited_by_id=owner_id,
917+
position=2,
918+
kind="query",
919+
title="Query",
920+
content="count()",
921+
prompt="Build a query",
922+
generated_content="count()",
923+
config={"dataset": "spans"},
924+
display={"type": "table"},
925+
version=2,
926+
stale_at=timezone.now(),
927+
deleted_at=timezone.now(),
928+
)
929+
InvestigationCellDependency.objects.create(cell=cell, depends_on=upstream_cell)
930+
InvestigationCellParameter.objects.create(cell=cell, parameter=parameter)
931+
execution = InvestigationCellExecution.objects.create(
932+
cell=cell,
933+
triggered_by_id=owner_id,
934+
executor="code_mode",
935+
status="completed",
936+
cell_version=2,
937+
input_snapshot={"environment": "production"},
938+
input_fingerprint="f" * 64,
939+
result_schema_version=2,
940+
result={"rows": [[1]]},
941+
error={"previous": "transient"},
942+
started_at=timezone.now(),
943+
completed_at=timezone.now(),
944+
)
945+
InvestigationCellExecutionProject.objects.create(execution=execution, project=project)
946+
cell.current_execution = execution
947+
cell.save(update_fields=["current_execution"])
948+
comment = InvestigationCellComment.objects.create(
949+
cell=cell,
950+
author_id=owner_id,
951+
body="Please review this result",
952+
deleted_at=timezone.now(),
953+
)
954+
InvestigationCellReaction.objects.create(cell=cell, user_id=owner_id, reaction="heart")
955+
InvestigationCommentReaction.objects.create(
956+
comment=comment, user_id=owner_id, reaction="eyes"
957+
)
958+
InvestigationCommentUserMention.objects.create(comment=comment, user_id=owner_id)
959+
InvestigationCommentTeamMention.objects.create(comment=comment, team=team)
960+
844961
return org
845962

846963
@assume_test_silo_mode(SiloMode.CONTROL)

tests/sentry/backup/test_imports.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@
3333
)
3434
from sentry.backup.scopes import ExportScope, ImportScope, RelocationScope
3535
from sentry.backup.services.import_export.model import RpcImportErrorKind
36+
from sentry.investigations.models import (
37+
Investigation,
38+
InvestigationCell,
39+
InvestigationCellComment,
40+
InvestigationCellExecution,
41+
InvestigationParameter,
42+
)
3643
from sentry.models.apitoken import DEFAULT_EXPIRATION, ApiToken, generate_token
3744
from sentry.models.importchunk import (
3845
ControlImportChunk,
@@ -1463,7 +1470,15 @@ def test_colliding_member_invite_token(self, expected_models: list[type[Model]])
14631470
with open(tmp_path, "rb") as tmp_file:
14641471
verify_models_in_output(expected_models, orjson.loads(tmp_file.read()))
14651472

1466-
@expect_models(COLLISION_TESTED, Monitor)
1473+
@expect_models(
1474+
COLLISION_TESTED,
1475+
Monitor,
1476+
Investigation,
1477+
InvestigationCell,
1478+
InvestigationCellComment,
1479+
InvestigationCellExecution,
1480+
InvestigationParameter,
1481+
)
14671482
def test_colliding_monitor(self, expected_models: list[type[Model]]) -> None:
14681483
owner = self.create_exhaustive_user("owner")
14691484
invited = self.create_exhaustive_user("invited")

0 commit comments

Comments
 (0)