From c6a52dea6de2777e23e0a0c4f21402af52cc94fe Mon Sep 17 00:00:00 2001 From: Josh Callender <1569818+saponifi3d@users.noreply.github.com> Date: Wed, 13 Nov 2024 14:26:22 -0800 Subject: [PATCH 01/28] remove the 'required' field from an action, this is a legacy field. --- src/sentry/workflow_engine/models/action.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/sentry/workflow_engine/models/action.py b/src/sentry/workflow_engine/models/action.py index c291e0b0aea7d6..1a055974d3af8e 100644 --- a/src/sentry/workflow_engine/models/action.py +++ b/src/sentry/workflow_engine/models/action.py @@ -24,13 +24,6 @@ class Type(models.TextChoices): Notification = "SendNotificationAction" TriggerWorkflow = "TriggerWorkflowAction" - """ - Required actions cannot be disabled by the user, and will not be displayed in the UI. - These actions will be used internally, to trigger other aspects of the system. - For example, creating a new issue in the Issue Platform or a detector emitting an event. - """ - required = models.BooleanField(default=False) - # The type field is used to denote the type of action we want to trigger type = models.TextField(choices=Type.choices) data = models.JSONField(default=dict) From f5938f134a1b67e2e8e923dde68bb70be8d60a16 Mon Sep 17 00:00:00 2001 From: Josh Callender <1569818+saponifi3d@users.noreply.github.com> Date: Wed, 13 Nov 2024 14:26:45 -0800 Subject: [PATCH 02/28] Remove the temp project_id field and make an association with the project - removing the association with the organization since we can get that through the --- src/sentry/workflow_engine/models/detector.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/sentry/workflow_engine/models/detector.py b/src/sentry/workflow_engine/models/detector.py index 8a17ee38e8ea56..2b42b2f1e977f3 100644 --- a/src/sentry/workflow_engine/models/detector.py +++ b/src/sentry/workflow_engine/models/detector.py @@ -23,7 +23,7 @@ class Detector(DefaultFieldsModel, OwnerModel): __relocation_scope__ = RelocationScope.Organization - organization = FlexibleForeignKey("sentry.Organization") + project = FlexibleForeignKey("sentry.Project") name = models.CharField(max_length=200) # The data sources that the detector is watching @@ -31,7 +31,9 @@ class Detector(DefaultFieldsModel, OwnerModel): "workflow_engine.DataSource", through="workflow_engine.DataSourceDetector" ) - # The conditions that must be met for the detector to be considered 'active' + # If the detector is not enabled, it will not be evaluated. This is how we "snooze" a detector + enabled = models.BooleanField(default=True) + # This will emit an event for the workflow to process workflow_condition_group = FlexibleForeignKey( "workflow_engine.DataConditionGroup", @@ -50,11 +52,6 @@ class Meta(OwnerModel.Meta): ) ] - @property - def project_id(self): - # XXX: Temporary property until we add `project_id` to the model. - return 1 - @property def group_type(self) -> builtins.type[GroupType] | None: return grouptype.registry.get_by_slug(self.type) From 61456628c31ade537d226048d98c1ecbfe93f395 Mon Sep 17 00:00:00 2001 From: Josh Callender <1569818+saponifi3d@users.noreply.github.com> Date: Wed, 13 Nov 2024 14:27:53 -0800 Subject: [PATCH 03/28] Add the ownermodel to the workflow, add 'enabled' flag for snooze --- src/sentry/workflow_engine/models/workflow.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/sentry/workflow_engine/models/workflow.py b/src/sentry/workflow_engine/models/workflow.py index 4cb8fde6721cf4..0d527d31e70853 100644 --- a/src/sentry/workflow_engine/models/workflow.py +++ b/src/sentry/workflow_engine/models/workflow.py @@ -2,12 +2,11 @@ from sentry.backup.scopes import RelocationScope from sentry.db.models import DefaultFieldsModel, FlexibleForeignKey, region_silo_model, sane_repr - -from .data_condition_group import DataConditionGroup +from sentry.models.owner_base import OwnerModel @region_silo_model -class Workflow(DefaultFieldsModel): +class Workflow(DefaultFieldsModel, OwnerModel): """ A workflow is a way to execute actions in a specified order. Workflows are initiated after detectors have been processed, driven by changes to their state. @@ -17,8 +16,13 @@ class Workflow(DefaultFieldsModel): name = models.CharField(max_length=200) organization = FlexibleForeignKey("sentry.Organization") + # If the workflow is not enabled, it will not be evaluated / invoke actions. This is how we "snooze" a workflow + enabled = models.BooleanField(default=True) + # Required as the 'when' condition for the workflow, this evalutes states emitted from the detectors - when_condition_group = FlexibleForeignKey(DataConditionGroup, blank=True, null=True) + when_condition_group = FlexibleForeignKey( + "workflow_engine.DataConditionGroup", blank=True, null=True + ) __repr__ = sane_repr("name", "organization_id") From 9025e7ce3ee85c1c8c9618176c4d48e687e7af50 Mon Sep 17 00:00:00 2001 From: Josh Callender <1569818+saponifi3d@users.noreply.github.com> Date: Wed, 13 Nov 2024 14:31:51 -0800 Subject: [PATCH 04/28] Update the detector model: - Add comparison_delta to the detector, this is used for comparison alerts. - Add description for slack notifications --- src/sentry/workflow_engine/models/detector.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/sentry/workflow_engine/models/detector.py b/src/sentry/workflow_engine/models/detector.py index 2b42b2f1e977f3..cca96b23bdd55c 100644 --- a/src/sentry/workflow_engine/models/detector.py +++ b/src/sentry/workflow_engine/models/detector.py @@ -34,6 +34,13 @@ class Detector(DefaultFieldsModel, OwnerModel): # If the detector is not enabled, it will not be evaluated. This is how we "snooze" a detector enabled = models.BooleanField(default=True) + # Optionally set a description of the detector, this will be used in notifications + description = models.TextField(blank=True, null=True) + + # This represents a time delta, in seconds. If not null, this is used to determine which time + # window to query to compare the result from the current time_window to. + comparison_delta = models.IntegerField(null=True) + # This will emit an event for the workflow to process workflow_condition_group = FlexibleForeignKey( "workflow_engine.DataConditionGroup", From c61e0cad3e60bad399eb8929c1ca7527c0ef6edc Mon Sep 17 00:00:00 2001 From: Cathy Teng Date: Wed, 13 Nov 2024 15:04:46 -0800 Subject: [PATCH 05/28] add environment_id, created_by, frequency, and snoozed fields to Workflow --- src/sentry/workflow_engine/models/workflow.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/sentry/workflow_engine/models/workflow.py b/src/sentry/workflow_engine/models/workflow.py index 0d527d31e70853..b6a07930c00d30 100644 --- a/src/sentry/workflow_engine/models/workflow.py +++ b/src/sentry/workflow_engine/models/workflow.py @@ -1,7 +1,15 @@ +from django.conf import settings from django.db import models from sentry.backup.scopes import RelocationScope -from sentry.db.models import DefaultFieldsModel, FlexibleForeignKey, region_silo_model, sane_repr +from sentry.db.models import ( + BoundedPositiveIntegerField, + DefaultFieldsModel, + FlexibleForeignKey, + region_silo_model, + sane_repr, +) +from sentry.db.models.fields.hybrid_cloud_foreign_key import HybridCloudForeignKey from sentry.models.owner_base import OwnerModel @@ -24,6 +32,13 @@ class Workflow(DefaultFieldsModel, OwnerModel): "workflow_engine.DataConditionGroup", blank=True, null=True ) + environment_id = BoundedPositiveIntegerField(null=True) + created_by = HybridCloudForeignKey( + settings.AUTH_USER_MODEL, null=True, on_delete=models.SET_NULL + ) + frequency = BoundedPositiveIntegerField(default=30) # in minutes + snoozed = models.BooleanField(default=False) + __repr__ = sane_repr("name", "organization_id") class Meta: From 2beeebb7fe1374ca0943ef8b7be4a37f2d9e30fe Mon Sep 17 00:00:00 2001 From: Cathy Teng Date: Wed, 13 Nov 2024 15:05:38 -0800 Subject: [PATCH 06/28] remove snooze, we have 'enabled' --- src/sentry/workflow_engine/models/workflow.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sentry/workflow_engine/models/workflow.py b/src/sentry/workflow_engine/models/workflow.py index b6a07930c00d30..746c741c2ad54c 100644 --- a/src/sentry/workflow_engine/models/workflow.py +++ b/src/sentry/workflow_engine/models/workflow.py @@ -37,7 +37,6 @@ class Workflow(DefaultFieldsModel, OwnerModel): settings.AUTH_USER_MODEL, null=True, on_delete=models.SET_NULL ) frequency = BoundedPositiveIntegerField(default=30) # in minutes - snoozed = models.BooleanField(default=False) __repr__ = sane_repr("name", "organization_id") From f9efc83c90aa6bffdc24c322867d47cbceb9829d Mon Sep 17 00:00:00 2001 From: Cathy Teng Date: Wed, 13 Nov 2024 15:13:46 -0800 Subject: [PATCH 07/28] use SET_NULL for HybridCloudForeignKey cascade --- src/sentry/workflow_engine/models/workflow.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/sentry/workflow_engine/models/workflow.py b/src/sentry/workflow_engine/models/workflow.py index 746c741c2ad54c..7d205fe4bcdf30 100644 --- a/src/sentry/workflow_engine/models/workflow.py +++ b/src/sentry/workflow_engine/models/workflow.py @@ -33,9 +33,7 @@ class Workflow(DefaultFieldsModel, OwnerModel): ) environment_id = BoundedPositiveIntegerField(null=True) - created_by = HybridCloudForeignKey( - settings.AUTH_USER_MODEL, null=True, on_delete=models.SET_NULL - ) + created_by = HybridCloudForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete="SET_NULL") frequency = BoundedPositiveIntegerField(default=30) # in minutes __repr__ = sane_repr("name", "organization_id") From a2b2d00db180e5aeff1e95e18115e7a619354856 Mon Sep 17 00:00:00 2001 From: Josh Callender <1569818+saponifi3d@users.noreply.github.com> Date: Thu, 14 Nov 2024 15:41:48 -0800 Subject: [PATCH 08/28] re-add the organization, it broke a bunch of stuff without it. If a project is deleted, cascade --- src/sentry/testutils/factories.py | 9 ++++++--- src/sentry/workflow_engine/models/detector.py | 3 ++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/sentry/testutils/factories.py b/src/sentry/testutils/factories.py index 4afe946574b83d..1a610e2dad3c4e 100644 --- a/src/sentry/testutils/factories.py +++ b/src/sentry/testutils/factories.py @@ -2130,14 +2130,17 @@ def create_detector( name: str | None = None, owner_user_id: int | None = None, owner_team: Team | None = None, + project: Project | None = None, **kwargs, ) -> Detector: - if organization is None: - organization = Factories.create_organization() if name is None: name = petname.generate(2, " ", letters=10).title() + if project is None: + project = Factories.create_project() + if organization is None: + organization = project.organization return Detector.objects.create( - organization=organization, + project=project, name=name, owner_user_id=owner_user_id, owner_team=owner_team, diff --git a/src/sentry/workflow_engine/models/detector.py b/src/sentry/workflow_engine/models/detector.py index cca96b23bdd55c..3a7bc7e9df8ba0 100644 --- a/src/sentry/workflow_engine/models/detector.py +++ b/src/sentry/workflow_engine/models/detector.py @@ -23,7 +23,8 @@ class Detector(DefaultFieldsModel, OwnerModel): __relocation_scope__ = RelocationScope.Organization - project = FlexibleForeignKey("sentry.Project") + organization = FlexibleForeignKey("sentry.Organization", on_delete=models.CASCADE) + project = FlexibleForeignKey("sentry.Project", on_delete=models.CASCADE) name = models.CharField(max_length=200) # The data sources that the detector is watching From 58b2567acd49a7910a489e1a6006317eae322487 Mon Sep 17 00:00:00 2001 From: Josh Callender <1569818+saponifi3d@users.noreply.github.com> Date: Thu, 14 Nov 2024 16:45:58 -0800 Subject: [PATCH 09/28] Update detector model --- src/sentry/workflow_engine/models/detector.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sentry/workflow_engine/models/detector.py b/src/sentry/workflow_engine/models/detector.py index 3a7bc7e9df8ba0..5241faffd6e7e5 100644 --- a/src/sentry/workflow_engine/models/detector.py +++ b/src/sentry/workflow_engine/models/detector.py @@ -21,10 +21,9 @@ @region_silo_model class Detector(DefaultFieldsModel, OwnerModel): - __relocation_scope__ = RelocationScope.Organization + __relocation_scope__ = RelocationScope.Excluded - organization = FlexibleForeignKey("sentry.Organization", on_delete=models.CASCADE) - project = FlexibleForeignKey("sentry.Project", on_delete=models.CASCADE) + project = FlexibleForeignKey("sentry.Project", on_delete=models.CASCADE, null=True) name = models.CharField(max_length=200) # The data sources that the detector is watching From abba8615b565b992a95a6eb12508fef9b46361f0 Mon Sep 17 00:00:00 2001 From: Josh Callender <1569818+saponifi3d@users.noreply.github.com> Date: Thu, 14 Nov 2024 17:19:16 -0800 Subject: [PATCH 10/28] make this a bounded integer field so it will be droppable correctly --- src/sentry/workflow_engine/models/detector.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sentry/workflow_engine/models/detector.py b/src/sentry/workflow_engine/models/detector.py index 5241faffd6e7e5..ac3f585ed8bbc4 100644 --- a/src/sentry/workflow_engine/models/detector.py +++ b/src/sentry/workflow_engine/models/detector.py @@ -9,6 +9,7 @@ from sentry.backup.scopes import RelocationScope from sentry.db.models import DefaultFieldsModel, FlexibleForeignKey, region_silo_model +from sentry.db.models.fields.bounded import BoundedIntegerField from sentry.issues import grouptype from sentry.issues.grouptype import GroupType from sentry.models.owner_base import OwnerModel @@ -23,6 +24,7 @@ class Detector(DefaultFieldsModel, OwnerModel): __relocation_scope__ = RelocationScope.Excluded + organization = BoundedIntegerField(null=True) project = FlexibleForeignKey("sentry.Project", on_delete=models.CASCADE, null=True) name = models.CharField(max_length=200) From 73c0cda8454dab79c92c12370e0f6da7b2748510 Mon Sep 17 00:00:00 2001 From: Josh Callender <1569818+saponifi3d@users.noreply.github.com> Date: Thu, 14 Nov 2024 17:19:54 -0800 Subject: [PATCH 11/28] remove org from test utils --- src/sentry/testutils/factories.py | 3 --- src/sentry/testutils/helpers/backups.py | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/sentry/testutils/factories.py b/src/sentry/testutils/factories.py index 1a610e2dad3c4e..d8c7d2d247fba3 100644 --- a/src/sentry/testutils/factories.py +++ b/src/sentry/testutils/factories.py @@ -2126,7 +2126,6 @@ def create_data_source( @staticmethod @assume_test_silo_mode(SiloMode.REGION) def create_detector( - organization: Organization | None = None, name: str | None = None, owner_user_id: int | None = None, owner_team: Team | None = None, @@ -2137,8 +2136,6 @@ def create_detector( name = petname.generate(2, " ", letters=10).title() if project is None: project = Factories.create_project() - if organization is None: - organization = project.organization return Detector.objects.create( project=project, name=name, diff --git a/src/sentry/testutils/helpers/backups.py b/src/sentry/testutils/helpers/backups.py index ce1bd24a36e1b1..a5912a71043342 100644 --- a/src/sentry/testutils/helpers/backups.py +++ b/src/sentry/testutils/helpers/backups.py @@ -659,7 +659,7 @@ def create_exhaustive_organization( # Setup a test 'Issue Rule' and 'Automation' workflow = self.create_workflow(organization=org) - detector = self.create_detector(organization=org) + detector = self.create_detector(project=project) self.create_detector_workflow(detector=detector, workflow=workflow) self.create_detector_state(detector=detector) From d8979148fb2b6f03a6b0c5848593aad09500259a Mon Sep 17 00:00:00 2001 From: Josh Callender <1569818+saponifi3d@users.noreply.github.com> Date: Thu, 14 Nov 2024 17:23:14 -0800 Subject: [PATCH 12/28] make required nullable for the first phase of the migration --- src/sentry/workflow_engine/models/action.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sentry/workflow_engine/models/action.py b/src/sentry/workflow_engine/models/action.py index 1a055974d3af8e..ce9b022805c043 100644 --- a/src/sentry/workflow_engine/models/action.py +++ b/src/sentry/workflow_engine/models/action.py @@ -28,6 +28,8 @@ class Type(models.TextChoices): type = models.TextField(choices=Type.choices) data = models.JSONField(default=dict) + required = models.BooleanField(default=False, null=True) + # LEGACY: The integration_id is used to map the integration_id found in the AlertRuleTriggerAction # This allows us to map the way we're saving the notification channels to the action. integration_id = HybridCloudForeignKey( From 14306a0e26f027770c3f9cbaa2e15bd18b807689 Mon Sep 17 00:00:00 2001 From: Josh Callender <1569818+saponifi3d@users.noreply.github.com> Date: Thu, 14 Nov 2024 17:49:51 -0800 Subject: [PATCH 13/28] only make it nullable --- src/sentry/workflow_engine/models/detector.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sentry/workflow_engine/models/detector.py b/src/sentry/workflow_engine/models/detector.py index ac3f585ed8bbc4..0f40dd02ed82db 100644 --- a/src/sentry/workflow_engine/models/detector.py +++ b/src/sentry/workflow_engine/models/detector.py @@ -9,7 +9,6 @@ from sentry.backup.scopes import RelocationScope from sentry.db.models import DefaultFieldsModel, FlexibleForeignKey, region_silo_model -from sentry.db.models.fields.bounded import BoundedIntegerField from sentry.issues import grouptype from sentry.issues.grouptype import GroupType from sentry.models.owner_base import OwnerModel @@ -24,7 +23,9 @@ class Detector(DefaultFieldsModel, OwnerModel): __relocation_scope__ = RelocationScope.Excluded - organization = BoundedIntegerField(null=True) + # TODO - Finish removing this field + organization = FlexibleForeignKey("sentry.Organization", on_delete=models.CASCADE, null=True) + project = FlexibleForeignKey("sentry.Project", on_delete=models.CASCADE, null=True) name = models.CharField(max_length=200) From 1dea1d390fc3c8d2ff805a1855917a007565554e Mon Sep 17 00:00:00 2001 From: Josh Callender <1569818+saponifi3d@users.noreply.github.com> Date: Thu, 14 Nov 2024 17:55:57 -0800 Subject: [PATCH 14/28] update snapshots --- .../test_clean_pks.pysnap | 40 ++++++------------- .../test_default_comparators.pysnap | 7 +++- .../test_success.pysnap | 4 +- 3 files changed, 20 insertions(+), 31 deletions(-) diff --git a/tests/sentry/backup/snapshots/SanitizationExhaustiveTests/test_clean_pks.pysnap b/tests/sentry/backup/snapshots/SanitizationExhaustiveTests/test_clean_pks.pysnap index 3cdb0b1b55e1dd..8d05da9c6fd897 100644 --- a/tests/sentry/backup/snapshots/SanitizationExhaustiveTests/test_clean_pks.pysnap +++ b/tests/sentry/backup/snapshots/SanitizationExhaustiveTests/test_clean_pks.pysnap @@ -1,5 +1,5 @@ --- -created: '2024-11-06T18:15:33.745643+00:00' +created: '2024-11-15T01:54:51.640037+00:00' creator: sentry source: tests/sentry/backup/test_sanitize.py --- @@ -227,33 +227,6 @@ source: tests/sentry/backup/test_sanitize.py sanitized_fields: - date_added - date_updated -- model_name: workflow_engine.detector - ordinal: 1 - sanitized_fields: - - date_added - - date_updated - - name -- model_name: workflow_engine.workflow - ordinal: 1 - sanitized_fields: - - date_added - - date_updated - - name -- model_name: workflow_engine.workflowdataconditiongroup - ordinal: 1 - sanitized_fields: - - date_added - - date_updated -- model_name: workflow_engine.detectorworkflow - ordinal: 1 - sanitized_fields: - - date_added - - date_updated -- model_name: workflow_engine.datasourcedetector - ordinal: 1 - sanitized_fields: - - date_added - - date_updated - model_name: workflow_engine.datacondition ordinal: 1 sanitized_fields: @@ -484,6 +457,12 @@ source: tests/sentry/backup/test_sanitize.py - client_secret - date_added - name +- model_name: workflow_engine.workflow + ordinal: 1 + sanitized_fields: + - date_added + - date_updated + - name - model_name: sentry.useroption ordinal: 1 sanitized_fields: [] @@ -750,6 +729,11 @@ source: tests/sentry/backup/test_sanitize.py - date_added - date_modified - name +- model_name: workflow_engine.workflowdataconditiongroup + ordinal: 1 + sanitized_fields: + - date_added + - date_updated - model_name: sentry.snubaqueryeventtype ordinal: 1 sanitized_fields: [] diff --git a/tests/sentry/backup/snapshots/test_comparators/test_default_comparators.pysnap b/tests/sentry/backup/snapshots/test_comparators/test_default_comparators.pysnap index 7f86fa482d0cc6..e8f0cbec356499 100644 --- a/tests/sentry/backup/snapshots/test_comparators/test_default_comparators.pysnap +++ b/tests/sentry/backup/snapshots/test_comparators/test_default_comparators.pysnap @@ -1,5 +1,5 @@ --- -created: '2024-11-14T18:22:29.534257+00:00' +created: '2024-11-15T01:55:40.666769+00:00' creator: sentry source: tests/sentry/backup/test_comparators.py --- @@ -1721,6 +1721,7 @@ source: tests/sentry/backup/test_comparators.py - organization - owner_team - owner_user_id + - project - workflow_condition_group model_name: workflow_engine.detector - comparators: @@ -1749,7 +1750,11 @@ source: tests/sentry/backup/test_comparators.py - date_updated - class: ForeignKeyComparator fields: + - created_by + - environment_id - organization + - owner_team + - owner_user_id - when_condition_group model_name: workflow_engine.workflow - comparators: diff --git a/tests/sentry/tasks/snapshots/PreprocessingTransferTest/test_success.pysnap b/tests/sentry/tasks/snapshots/PreprocessingTransferTest/test_success.pysnap index 917c0e9f5eb59b..d2835db385c52d 100644 --- a/tests/sentry/tasks/snapshots/PreprocessingTransferTest/test_success.pysnap +++ b/tests/sentry/tasks/snapshots/PreprocessingTransferTest/test_success.pysnap @@ -1,5 +1,5 @@ --- -created: '2024-11-06T18:16:10.211369+00:00' +created: '2024-11-15T01:55:05.869901+00:00' creator: sentry source: tests/sentry/tasks/test_relocation.py --- @@ -96,7 +96,7 @@ steps: - -U - postgres - -c - - TRUNCATE sentry_controloption,sentry_integration,sentry_option,sentry_organization,sentry_organizationintegration,sentry_organizationoptions,sentry_projecttemplate,sentry_projecttemplateoption,sentry_relay,sentry_relayusage,sentry_repository,sentry_team,auth_user,sentry_userip,sentry_userpermission,sentry_userrole,sentry_userrole_users,workflow_engine_dataconditiongroup,workflow_engine_datasource,workflow_engine_detector,workflow_engine_workflow,workflow_engine_workflowdataconditiongroup,workflow_engine_detectorworkflow,workflow_engine_datasourcedetector,workflow_engine_datacondition,sentry_savedsearch,sentry_recentsearch,sentry_project,sentry_orgauthtoken,sentry_organizationmember,sentry_organizationaccessrequest,sentry_monitor,sentry_groupsearchview,sentry_environment,sentry_email,sentry_datasecrecywaiver,sentry_dashboardtombstone,sentry_dashboard,sentry_customdynamicsamplingrule,sentry_projectcounter,sentry_authprovider,sentry_authidentity,auth_authenticator,sentry_apikey,sentry_apiapplication,sentry_useroption,sentry_useremail,sentry_snubaquery,sentry_sentryapp,sentry_rule,sentry_querysubscription,sentry_projectteam,sentry_projectredirect,sentry_projectownership,sentry_projectoptions,sentry_projectkey,sentry_projectintegration,sentry_projectbookmark,sentry_organizationmember_teams,sentry_notificationaction,sentry_neglectedrule,sentry_environmentproject,sentry_dashboardwidget,sentry_dashboardpermissions,sentry_customdynamicsamplingruleproject,sentry_apitoken,sentry_apigrant,sentry_apiauthorization,sentry_alertrule,sentry_snubaqueryeventtype,sentry_sentryappinstallation,sentry_sentryappcomponent,sentry_rulesnooze,sentry_ruleactivity,sentry_notificationactionproject,sentry_dashboardwidgetquery,sentry_dashboardpermissionsteam,sentry_alertruletrigger,sentry_alertruleprojects,sentry_alertruleexcludedprojects,sentry_alertruleactivity,sentry_alertruleactivationcondition,sentry_servicehook,sentry_incident,sentry_dashboardwidgetqueryondemand,sentry_alertruletriggerexclusion,sentry_alertruletriggeraction,sentry_timeseriessnapshot,sentry_pendingincidentsnapshot,sentry_incidenttrigger,sentry_incidentsubscription,sentry_incidentsnapshot,sentry_incidentactivity + - TRUNCATE sentry_controloption,sentry_integration,sentry_option,sentry_organization,sentry_organizationintegration,sentry_organizationoptions,sentry_projecttemplate,sentry_projecttemplateoption,sentry_relay,sentry_relayusage,sentry_repository,sentry_team,auth_user,sentry_userip,sentry_userpermission,sentry_userrole,sentry_userrole_users,workflow_engine_dataconditiongroup,workflow_engine_datasource,workflow_engine_datacondition,sentry_savedsearch,sentry_recentsearch,sentry_project,sentry_orgauthtoken,sentry_organizationmember,sentry_organizationaccessrequest,sentry_monitor,sentry_groupsearchview,sentry_environment,sentry_email,sentry_datasecrecywaiver,sentry_dashboardtombstone,sentry_dashboard,sentry_customdynamicsamplingrule,sentry_projectcounter,sentry_authprovider,sentry_authidentity,auth_authenticator,sentry_apikey,sentry_apiapplication,workflow_engine_workflow,workflow_engine_datasourcedetector,sentry_useroption,sentry_useremail,sentry_snubaquery,sentry_sentryapp,sentry_rule,sentry_querysubscription,sentry_projectteam,sentry_projectredirect,sentry_projectownership,sentry_projectoptions,sentry_projectkey,sentry_projectintegration,sentry_projectbookmark,sentry_organizationmember_teams,sentry_notificationaction,sentry_neglectedrule,sentry_environmentproject,sentry_dashboardwidget,sentry_dashboardpermissions,sentry_customdynamicsamplingruleproject,sentry_apitoken,sentry_apigrant,sentry_apiauthorization,sentry_alertrule,workflow_engine_workflowdataconditiongroup,workflow_engine_detectorworkflow,sentry_snubaqueryeventtype,sentry_sentryappinstallation,sentry_sentryappcomponent,sentry_rulesnooze,sentry_ruleactivity,sentry_notificationactionproject,sentry_dashboardwidgetquery,sentry_dashboardpermissionsteam,sentry_alertruletrigger,sentry_alertruleprojects,sentry_alertruleexcludedprojects,sentry_alertruleactivity,sentry_alertruleactivationcondition,sentry_servicehook,sentry_incident,sentry_dashboardwidgetqueryondemand,sentry_alertruletriggerexclusion,sentry_alertruletriggeraction,sentry_timeseriessnapshot,sentry_pendingincidentsnapshot,sentry_incidenttrigger,sentry_incidentsubscription,sentry_incidentsnapshot,sentry_incidentactivity RESTART IDENTITY CASCADE; id: clear-database name: gcr.io/cloud-builders/docker From 09fdacff73c710a511e3d5dbfaea171c1753d67b Mon Sep 17 00:00:00 2001 From: Josh Callender <1569818+saponifi3d@users.noreply.github.com> Date: Thu, 14 Nov 2024 18:01:03 -0800 Subject: [PATCH 15/28] add comment to remove field --- src/sentry/workflow_engine/models/action.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sentry/workflow_engine/models/action.py b/src/sentry/workflow_engine/models/action.py index ce9b022805c043..6dd13d6c491870 100644 --- a/src/sentry/workflow_engine/models/action.py +++ b/src/sentry/workflow_engine/models/action.py @@ -28,6 +28,7 @@ class Type(models.TextChoices): type = models.TextField(choices=Type.choices) data = models.JSONField(default=dict) + # TODO - finish removing this field required = models.BooleanField(default=False, null=True) # LEGACY: The integration_id is used to map the integration_id found in the AlertRuleTriggerAction From 82d812e0d260b9c2fe0cdc9c69bce1187805ba99 Mon Sep 17 00:00:00 2001 From: Josh Callender <1569818+saponifi3d@users.noreply.github.com> Date: Fri, 15 Nov 2024 15:05:08 -0800 Subject: [PATCH 16/28] change how the factories / fixtures work to fix tests --- src/sentry/testutils/factories.py | 9 +-------- src/sentry/testutils/fixtures.py | 12 ++++++++++-- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/sentry/testutils/factories.py b/src/sentry/testutils/factories.py index d8c7d2d247fba3..9dc7d5513e3c29 100644 --- a/src/sentry/testutils/factories.py +++ b/src/sentry/testutils/factories.py @@ -2127,20 +2127,13 @@ def create_data_source( @assume_test_silo_mode(SiloMode.REGION) def create_detector( name: str | None = None, - owner_user_id: int | None = None, - owner_team: Team | None = None, - project: Project | None = None, **kwargs, ) -> Detector: if name is None: name = petname.generate(2, " ", letters=10).title() - if project is None: - project = Factories.create_project() + return Detector.objects.create( - project=project, name=name, - owner_user_id=owner_user_id, - owner_team=owner_team, **kwargs, ) diff --git a/src/sentry/testutils/fixtures.py b/src/sentry/testutils/fixtures.py index 7cb89c175ff15d..26413e962a48c4 100644 --- a/src/sentry/testutils/fixtures.py +++ b/src/sentry/testutils/fixtures.py @@ -634,8 +634,16 @@ def create_data_condition( condition_group=condition_group, ) - def create_detector(self, *args, **kwargs) -> Detector: - return Factories.create_detector(*args, **kwargs) + def create_detector( + self, + *args, + project=None, + **kwargs, + ) -> Detector: + if project is None: + project = self.create_project(organization=self.organization) + + return Factories.create_detector(*args, project=project, **kwargs) def create_detector_state(self, *args, **kwargs) -> DetectorState: return Factories.create_detector_state(*args, **kwargs) From cbb8d099f9bb3d3b0f7dbf6e93f5cebfbd7d1663 Mon Sep 17 00:00:00 2001 From: Josh Callender <1569818+saponifi3d@users.noreply.github.com> Date: Fri, 15 Nov 2024 15:08:16 -0800 Subject: [PATCH 17/28] Change relocation scope back to organization, that's used for the project as well. --- src/sentry/workflow_engine/models/detector.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sentry/workflow_engine/models/detector.py b/src/sentry/workflow_engine/models/detector.py index 0f40dd02ed82db..2327464e27f239 100644 --- a/src/sentry/workflow_engine/models/detector.py +++ b/src/sentry/workflow_engine/models/detector.py @@ -21,7 +21,7 @@ @region_silo_model class Detector(DefaultFieldsModel, OwnerModel): - __relocation_scope__ = RelocationScope.Excluded + __relocation_scope__ = RelocationScope.Organization # TODO - Finish removing this field organization = FlexibleForeignKey("sentry.Organization", on_delete=models.CASCADE, null=True) From 25f2ab192088fd477740525e2470c66da6bb610f Mon Sep 17 00:00:00 2001 From: Josh Callender <1569818+saponifi3d@users.noreply.github.com> Date: Fri, 15 Nov 2024 15:10:40 -0800 Subject: [PATCH 18/28] updatee the dependenices after changing the relocation scope --- .../backup/model_dependencies/detailed.json | 2 +- .../test_clean_pks.pysnap | 18 +++++++++++++++++- .../test_success.pysnap | 4 ++-- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/fixtures/backup/model_dependencies/detailed.json b/fixtures/backup/model_dependencies/detailed.json index e8f1a408d32fd0..64c19f847798a2 100644 --- a/fixtures/backup/model_dependencies/detailed.json +++ b/fixtures/backup/model_dependencies/detailed.json @@ -6505,7 +6505,7 @@ ] }, "workflow_engine.detector": { - "dangling": false, + "dangling": true, "foreign_keys": { "organization": { "kind": "FlexibleForeignKey", diff --git a/tests/sentry/backup/snapshots/SanitizationExhaustiveTests/test_clean_pks.pysnap b/tests/sentry/backup/snapshots/SanitizationExhaustiveTests/test_clean_pks.pysnap index 8d05da9c6fd897..5cfdb6869774b8 100644 --- a/tests/sentry/backup/snapshots/SanitizationExhaustiveTests/test_clean_pks.pysnap +++ b/tests/sentry/backup/snapshots/SanitizationExhaustiveTests/test_clean_pks.pysnap @@ -1,5 +1,5 @@ --- -created: '2024-11-15T01:54:51.640037+00:00' +created: '2024-11-15T23:09:32.075671+00:00' creator: sentry source: tests/sentry/backup/test_sanitize.py --- @@ -463,6 +463,17 @@ source: tests/sentry/backup/test_sanitize.py - date_added - date_updated - name +- model_name: workflow_engine.detector + ordinal: 1 + sanitized_fields: + - date_added + - date_updated + - name +- model_name: workflow_engine.datasourcedetector + ordinal: 1 + sanitized_fields: + - date_added + - date_updated - model_name: sentry.useroption ordinal: 1 sanitized_fields: [] @@ -734,6 +745,11 @@ source: tests/sentry/backup/test_sanitize.py sanitized_fields: - date_added - date_updated +- model_name: workflow_engine.detectorworkflow + ordinal: 1 + sanitized_fields: + - date_added + - date_updated - model_name: sentry.snubaqueryeventtype ordinal: 1 sanitized_fields: [] diff --git a/tests/sentry/tasks/snapshots/PreprocessingTransferTest/test_success.pysnap b/tests/sentry/tasks/snapshots/PreprocessingTransferTest/test_success.pysnap index d2835db385c52d..973f822b2df6a8 100644 --- a/tests/sentry/tasks/snapshots/PreprocessingTransferTest/test_success.pysnap +++ b/tests/sentry/tasks/snapshots/PreprocessingTransferTest/test_success.pysnap @@ -1,5 +1,5 @@ --- -created: '2024-11-15T01:55:05.869901+00:00' +created: '2024-11-15T23:09:46.023372+00:00' creator: sentry source: tests/sentry/tasks/test_relocation.py --- @@ -96,7 +96,7 @@ steps: - -U - postgres - -c - - TRUNCATE sentry_controloption,sentry_integration,sentry_option,sentry_organization,sentry_organizationintegration,sentry_organizationoptions,sentry_projecttemplate,sentry_projecttemplateoption,sentry_relay,sentry_relayusage,sentry_repository,sentry_team,auth_user,sentry_userip,sentry_userpermission,sentry_userrole,sentry_userrole_users,workflow_engine_dataconditiongroup,workflow_engine_datasource,workflow_engine_datacondition,sentry_savedsearch,sentry_recentsearch,sentry_project,sentry_orgauthtoken,sentry_organizationmember,sentry_organizationaccessrequest,sentry_monitor,sentry_groupsearchview,sentry_environment,sentry_email,sentry_datasecrecywaiver,sentry_dashboardtombstone,sentry_dashboard,sentry_customdynamicsamplingrule,sentry_projectcounter,sentry_authprovider,sentry_authidentity,auth_authenticator,sentry_apikey,sentry_apiapplication,workflow_engine_workflow,workflow_engine_datasourcedetector,sentry_useroption,sentry_useremail,sentry_snubaquery,sentry_sentryapp,sentry_rule,sentry_querysubscription,sentry_projectteam,sentry_projectredirect,sentry_projectownership,sentry_projectoptions,sentry_projectkey,sentry_projectintegration,sentry_projectbookmark,sentry_organizationmember_teams,sentry_notificationaction,sentry_neglectedrule,sentry_environmentproject,sentry_dashboardwidget,sentry_dashboardpermissions,sentry_customdynamicsamplingruleproject,sentry_apitoken,sentry_apigrant,sentry_apiauthorization,sentry_alertrule,workflow_engine_workflowdataconditiongroup,workflow_engine_detectorworkflow,sentry_snubaqueryeventtype,sentry_sentryappinstallation,sentry_sentryappcomponent,sentry_rulesnooze,sentry_ruleactivity,sentry_notificationactionproject,sentry_dashboardwidgetquery,sentry_dashboardpermissionsteam,sentry_alertruletrigger,sentry_alertruleprojects,sentry_alertruleexcludedprojects,sentry_alertruleactivity,sentry_alertruleactivationcondition,sentry_servicehook,sentry_incident,sentry_dashboardwidgetqueryondemand,sentry_alertruletriggerexclusion,sentry_alertruletriggeraction,sentry_timeseriessnapshot,sentry_pendingincidentsnapshot,sentry_incidenttrigger,sentry_incidentsubscription,sentry_incidentsnapshot,sentry_incidentactivity + - TRUNCATE sentry_controloption,sentry_integration,sentry_option,sentry_organization,sentry_organizationintegration,sentry_organizationoptions,sentry_projecttemplate,sentry_projecttemplateoption,sentry_relay,sentry_relayusage,sentry_repository,sentry_team,auth_user,sentry_userip,sentry_userpermission,sentry_userrole,sentry_userrole_users,workflow_engine_dataconditiongroup,workflow_engine_datasource,workflow_engine_datacondition,sentry_savedsearch,sentry_recentsearch,sentry_project,sentry_orgauthtoken,sentry_organizationmember,sentry_organizationaccessrequest,sentry_monitor,sentry_groupsearchview,sentry_environment,sentry_email,sentry_datasecrecywaiver,sentry_dashboardtombstone,sentry_dashboard,sentry_customdynamicsamplingrule,sentry_projectcounter,sentry_authprovider,sentry_authidentity,auth_authenticator,sentry_apikey,sentry_apiapplication,workflow_engine_workflow,workflow_engine_detector,workflow_engine_datasourcedetector,sentry_useroption,sentry_useremail,sentry_snubaquery,sentry_sentryapp,sentry_rule,sentry_querysubscription,sentry_projectteam,sentry_projectredirect,sentry_projectownership,sentry_projectoptions,sentry_projectkey,sentry_projectintegration,sentry_projectbookmark,sentry_organizationmember_teams,sentry_notificationaction,sentry_neglectedrule,sentry_environmentproject,sentry_dashboardwidget,sentry_dashboardpermissions,sentry_customdynamicsamplingruleproject,sentry_apitoken,sentry_apigrant,sentry_apiauthorization,sentry_alertrule,workflow_engine_workflowdataconditiongroup,workflow_engine_detectorworkflow,sentry_snubaqueryeventtype,sentry_sentryappinstallation,sentry_sentryappcomponent,sentry_rulesnooze,sentry_ruleactivity,sentry_notificationactionproject,sentry_dashboardwidgetquery,sentry_dashboardpermissionsteam,sentry_alertruletrigger,sentry_alertruleprojects,sentry_alertruleexcludedprojects,sentry_alertruleactivity,sentry_alertruleactivationcondition,sentry_servicehook,sentry_incident,sentry_dashboardwidgetqueryondemand,sentry_alertruletriggerexclusion,sentry_alertruletriggeraction,sentry_timeseriessnapshot,sentry_pendingincidentsnapshot,sentry_incidenttrigger,sentry_incidentsubscription,sentry_incidentsnapshot,sentry_incidentactivity RESTART IDENTITY CASCADE; id: clear-database name: gcr.io/cloud-builders/docker From 6a345e8407751bb7462e612fb181b8754b327b71 Mon Sep 17 00:00:00 2001 From: Josh Callender <1569818+saponifi3d@users.noreply.github.com> Date: Fri, 15 Nov 2024 16:34:26 -0800 Subject: [PATCH 19/28] create an association with the project rather than using a hardcoded 1 --- tests/sentry/workflow_engine/processors/test_detector.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/sentry/workflow_engine/processors/test_detector.py b/tests/sentry/workflow_engine/processors/test_detector.py index 07ad0a2e5ecfcd..e85704090fbf3c 100644 --- a/tests/sentry/workflow_engine/processors/test_detector.py +++ b/tests/sentry/workflow_engine/processors/test_detector.py @@ -86,7 +86,9 @@ def tearDown(self): def create_detector_and_conditions(self, type: str | None = None): if type is None: type = "handler_with_state" + self.project = self.create_project() detector = self.create_detector( + project=self.project, workflow_condition_group=self.create_data_condition_group(), type=type, ) @@ -464,7 +466,7 @@ def test_above_below_threshold(self): is_active=False, result=StatusChangeMessage( fingerprint=[f"{handler.detector.id}:val1"], - project_id=1, + project_id=self.project.id, new_status=1, new_substatus=None, ), @@ -613,7 +615,7 @@ def test_status_change(self): DetectorPriorityLevel.OK, result=StatusChangeMessage( fingerprint=[f"{handler.detector.id}:group_key"], - project_id=1, + project_id=self.project.id, new_status=1, new_substatus=None, ), From b334df003b8e1510a2add00373d86e4f2b77c280 Mon Sep 17 00:00:00 2001 From: Josh Callender <1569818+saponifi3d@users.noreply.github.com> Date: Mon, 18 Nov 2024 21:25:55 -0800 Subject: [PATCH 20/28] seems okay, but need to figure out the rest of the details in the json config mixin --- src/sentry/workflow_engine/models/detector.py | 15 ++++++++----- .../models/json_config_mixin.py | 22 +++++++++++++++++++ src/sentry/workflow_engine/models/workflow.py | 14 ++++-------- 3 files changed, 36 insertions(+), 15 deletions(-) create mode 100644 src/sentry/workflow_engine/models/json_config_mixin.py diff --git a/src/sentry/workflow_engine/models/detector.py b/src/sentry/workflow_engine/models/detector.py index 2327464e27f239..1298432999ebf6 100644 --- a/src/sentry/workflow_engine/models/detector.py +++ b/src/sentry/workflow_engine/models/detector.py @@ -4,14 +4,17 @@ import logging from typing import TYPE_CHECKING, Any +from django.conf import settings from django.db import models from django.db.models import UniqueConstraint from sentry.backup.scopes import RelocationScope from sentry.db.models import DefaultFieldsModel, FlexibleForeignKey, region_silo_model +from sentry.db.models.fields.hybrid_cloud_foreign_key import HybridCloudForeignKey from sentry.issues import grouptype from sentry.issues.grouptype import GroupType from sentry.models.owner_base import OwnerModel +from sentry.workflow_engine.models.json_config_mixin import JsonConfigMixin if TYPE_CHECKING: from sentry.workflow_engine.processors.detector import DetectorHandler @@ -20,7 +23,7 @@ @region_silo_model -class Detector(DefaultFieldsModel, OwnerModel): +class Detector(DefaultFieldsModel, OwnerModel, JsonConfigMixin): __relocation_scope__ = RelocationScope.Organization # TODO - Finish removing this field @@ -40,10 +43,6 @@ class Detector(DefaultFieldsModel, OwnerModel): # Optionally set a description of the detector, this will be used in notifications description = models.TextField(blank=True, null=True) - # This represents a time delta, in seconds. If not null, this is used to determine which time - # window to query to compare the result from the current time_window to. - comparison_delta = models.IntegerField(null=True) - # This will emit an event for the workflow to process workflow_condition_group = FlexibleForeignKey( "workflow_engine.DataConditionGroup", @@ -52,8 +51,14 @@ class Detector(DefaultFieldsModel, OwnerModel): unique=True, on_delete=models.SET_NULL, ) + + # The type of detector that is being used, this is used to determine the class + # to load for the detector type = models.CharField(max_length=200) + # The user that created the detector + created_by = HybridCloudForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete="SET_NULL") + class Meta(OwnerModel.Meta): constraints = OwnerModel.Meta.constraints + [ UniqueConstraint( diff --git a/src/sentry/workflow_engine/models/json_config_mixin.py b/src/sentry/workflow_engine/models/json_config_mixin.py new file mode 100644 index 00000000000000..22c438abdae619 --- /dev/null +++ b/src/sentry/workflow_engine/models/json_config_mixin.py @@ -0,0 +1,22 @@ +from abc import abstractproperty +from typing import Any + +from django.db import models +from jsonschema import ValidationError, validate + + +class JsonConfigMixin(models.Model): + config = models.JSONField(default=None, blank=True, null=True) + + @abstractproperty + def CONFIG_SCHEMA(self) -> dict[str, Any]: + pass + + def validate_config(self) -> None: + try: + validate(self.config, self.CONFIG_SCHEMA) + except ValidationError as e: + raise ValidationError(f"Invalid config: {e.message}") + + class Meta: + abstract = True diff --git a/src/sentry/workflow_engine/models/workflow.py b/src/sentry/workflow_engine/models/workflow.py index 7d205fe4bcdf30..f09caebc0854a6 100644 --- a/src/sentry/workflow_engine/models/workflow.py +++ b/src/sentry/workflow_engine/models/workflow.py @@ -2,19 +2,14 @@ from django.db import models from sentry.backup.scopes import RelocationScope -from sentry.db.models import ( - BoundedPositiveIntegerField, - DefaultFieldsModel, - FlexibleForeignKey, - region_silo_model, - sane_repr, -) +from sentry.db.models import DefaultFieldsModel, FlexibleForeignKey, region_silo_model, sane_repr from sentry.db.models.fields.hybrid_cloud_foreign_key import HybridCloudForeignKey from sentry.models.owner_base import OwnerModel +from sentry.workflow_engine.models.json_config_mixin import JsonConfigMixin @region_silo_model -class Workflow(DefaultFieldsModel, OwnerModel): +class Workflow(DefaultFieldsModel, OwnerModel, JsonConfigMixin): """ A workflow is a way to execute actions in a specified order. Workflows are initiated after detectors have been processed, driven by changes to their state. @@ -32,9 +27,8 @@ class Workflow(DefaultFieldsModel, OwnerModel): "workflow_engine.DataConditionGroup", blank=True, null=True ) - environment_id = BoundedPositiveIntegerField(null=True) + environment = FlexibleForeignKey("sentry.Environment", db_constraint=False) created_by = HybridCloudForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete="SET_NULL") - frequency = BoundedPositiveIntegerField(default=30) # in minutes __repr__ = sane_repr("name", "organization_id") From 1e13d6155821ac773b1a5f234ba3cf1554ef2d03 Mon Sep 17 00:00:00 2001 From: Josh Callender <1569818+saponifi3d@users.noreply.github.com> Date: Tue, 19 Nov 2024 11:07:39 -0800 Subject: [PATCH 21/28] Update workflow / detector models, and change to have json_config export a base class --- src/sentry/workflow_engine/models/detector.py | 5 ++- .../{json_config_mixin.py => json_config.py} | 4 +- src/sentry/workflow_engine/models/workflow.py | 9 +++-- .../workflow_engine/models/test_workflow.py | 39 +++++++++++++++++++ 4 files changed, 50 insertions(+), 7 deletions(-) rename src/sentry/workflow_engine/models/{json_config_mixin.py => json_config.py} (86%) diff --git a/src/sentry/workflow_engine/models/detector.py b/src/sentry/workflow_engine/models/detector.py index 1298432999ebf6..4c8affbaf916c4 100644 --- a/src/sentry/workflow_engine/models/detector.py +++ b/src/sentry/workflow_engine/models/detector.py @@ -14,7 +14,8 @@ from sentry.issues import grouptype from sentry.issues.grouptype import GroupType from sentry.models.owner_base import OwnerModel -from sentry.workflow_engine.models.json_config_mixin import JsonConfigMixin + +from .json_config import JSONConfigBase if TYPE_CHECKING: from sentry.workflow_engine.processors.detector import DetectorHandler @@ -23,7 +24,7 @@ @region_silo_model -class Detector(DefaultFieldsModel, OwnerModel, JsonConfigMixin): +class Detector(DefaultFieldsModel, OwnerModel, JSONConfigBase): __relocation_scope__ = RelocationScope.Organization # TODO - Finish removing this field diff --git a/src/sentry/workflow_engine/models/json_config_mixin.py b/src/sentry/workflow_engine/models/json_config.py similarity index 86% rename from src/sentry/workflow_engine/models/json_config_mixin.py rename to src/sentry/workflow_engine/models/json_config.py index 22c438abdae619..63c8e7c88580ad 100644 --- a/src/sentry/workflow_engine/models/json_config_mixin.py +++ b/src/sentry/workflow_engine/models/json_config.py @@ -5,14 +5,14 @@ from jsonschema import ValidationError, validate -class JsonConfigMixin(models.Model): +class JSONConfigBase(models.Model): config = models.JSONField(default=None, blank=True, null=True) @abstractproperty def CONFIG_SCHEMA(self) -> dict[str, Any]: pass - def validate_config(self) -> None: + def validate_config(self) -> bool: try: validate(self.config, self.CONFIG_SCHEMA) except ValidationError as e: diff --git a/src/sentry/workflow_engine/models/workflow.py b/src/sentry/workflow_engine/models/workflow.py index f09caebc0854a6..7799d0413ce03e 100644 --- a/src/sentry/workflow_engine/models/workflow.py +++ b/src/sentry/workflow_engine/models/workflow.py @@ -5,11 +5,12 @@ from sentry.db.models import DefaultFieldsModel, FlexibleForeignKey, region_silo_model, sane_repr from sentry.db.models.fields.hybrid_cloud_foreign_key import HybridCloudForeignKey from sentry.models.owner_base import OwnerModel -from sentry.workflow_engine.models.json_config_mixin import JsonConfigMixin + +from .json_config import JSONConfigBase @region_silo_model -class Workflow(DefaultFieldsModel, OwnerModel, JsonConfigMixin): +class Workflow(DefaultFieldsModel, OwnerModel, JSONConfigBase): """ A workflow is a way to execute actions in a specified order. Workflows are initiated after detectors have been processed, driven by changes to their state. @@ -27,7 +28,9 @@ class Workflow(DefaultFieldsModel, OwnerModel, JsonConfigMixin): "workflow_engine.DataConditionGroup", blank=True, null=True ) - environment = FlexibleForeignKey("sentry.Environment", db_constraint=False) + environment = FlexibleForeignKey( + "sentry.Environment", db_constraint=False, blank=True, null=True + ) created_by = HybridCloudForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete="SET_NULL") __repr__ = sane_repr("name", "organization_id") diff --git a/tests/sentry/workflow_engine/models/test_workflow.py b/tests/sentry/workflow_engine/models/test_workflow.py index 10cafc1b24f2de..ee284b24623fbb 100644 --- a/tests/sentry/workflow_engine/models/test_workflow.py +++ b/tests/sentry/workflow_engine/models/test_workflow.py @@ -1,7 +1,29 @@ +import pytest +from jsonschema import ValidationError + from sentry.testutils.cases import TestCase from sentry.workflow_engine.models import Workflow +class IssueAlertWorkflow(Workflow): + class Meta: + # These are added to make the tests work as expected + app_label = "workflow_engine" + managed = False + + # This will be needed in sub-classes + proxy = True + + # Define a JSON Schema for the test + CONFIG_SCHEMA = { + "type": "object", + "properties": { + "frequency": {"type": "number"}, + }, + "required": ["frequency"], + } + + class WorkflowTest(TestCase): def setUp(self): self.org = self.create_organization() @@ -17,3 +39,20 @@ def test_create_simple_workflow(self): assert workflow.name == "test_workflow" assert workflow.organization == self.org + + def test_create_workflow_with_config__valid(self): + workflow = IssueAlertWorkflow( + name="test_workflow", organization=self.org, config={"frequency": 1} + ) + + assert workflow.config == {"frequency": 1} + assert workflow.validate_config() is None + + def test_create_workflow_with_config__invalid(self): + workflow = IssueAlertWorkflow( + name="test_workflow", organization=self.org, config={"freq": 2} + ) + + assert workflow.config == {"freq": 2} + with pytest.raises(ValidationError): + assert workflow.validate_config() From 52acf94833907ebb2ee01514522bd2ad00a0d284 Mon Sep 17 00:00:00 2001 From: Josh Callender <1569818+saponifi3d@users.noreply.github.com> Date: Tue, 19 Nov 2024 12:49:50 -0800 Subject: [PATCH 22/28] Update json field to be dict by default --- src/sentry/workflow_engine/models/json_config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sentry/workflow_engine/models/json_config.py b/src/sentry/workflow_engine/models/json_config.py index 63c8e7c88580ad..1ffce1af2671da 100644 --- a/src/sentry/workflow_engine/models/json_config.py +++ b/src/sentry/workflow_engine/models/json_config.py @@ -6,7 +6,7 @@ class JSONConfigBase(models.Model): - config = models.JSONField(default=None, blank=True, null=True) + config = models.JSONField(default=dict, blank=True, null=True) @abstractproperty def CONFIG_SCHEMA(self) -> dict[str, Any]: From 343a7c76acc29aeb770161796b192b63624290f9 Mon Sep 17 00:00:00 2001 From: Josh Callender <1569818+saponifi3d@users.noreply.github.com> Date: Tue, 19 Nov 2024 12:06:41 -0800 Subject: [PATCH 23/28] Add an error to the base models -- helps with typing errors and will throw loudly --- src/sentry/workflow_engine/models/detector.py | 4 ++++ src/sentry/workflow_engine/models/json_config.py | 2 +- src/sentry/workflow_engine/models/workflow.py | 7 +++++++ tests/sentry/workflow_engine/models/test_workflow.py | 4 ++-- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/sentry/workflow_engine/models/detector.py b/src/sentry/workflow_engine/models/detector.py index 4c8affbaf916c4..421c51c3819bda 100644 --- a/src/sentry/workflow_engine/models/detector.py +++ b/src/sentry/workflow_engine/models/detector.py @@ -60,6 +60,10 @@ class Detector(DefaultFieldsModel, OwnerModel, JSONConfigBase): # The user that created the detector created_by = HybridCloudForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete="SET_NULL") + @property + def CONFIG_SCHEMA(self) -> dict[str, Any]: + raise NotImplementedError('Subclasses must define a "CONFIG_SCHEMA" attribute') + class Meta(OwnerModel.Meta): constraints = OwnerModel.Meta.constraints + [ UniqueConstraint( diff --git a/src/sentry/workflow_engine/models/json_config.py b/src/sentry/workflow_engine/models/json_config.py index 1ffce1af2671da..5562c34fb2e8e4 100644 --- a/src/sentry/workflow_engine/models/json_config.py +++ b/src/sentry/workflow_engine/models/json_config.py @@ -12,7 +12,7 @@ class JSONConfigBase(models.Model): def CONFIG_SCHEMA(self) -> dict[str, Any]: pass - def validate_config(self) -> bool: + def validate_config(self) -> None: try: validate(self.config, self.CONFIG_SCHEMA) except ValidationError as e: diff --git a/src/sentry/workflow_engine/models/workflow.py b/src/sentry/workflow_engine/models/workflow.py index 7799d0413ce03e..561c1f4cf4c3dd 100644 --- a/src/sentry/workflow_engine/models/workflow.py +++ b/src/sentry/workflow_engine/models/workflow.py @@ -1,3 +1,5 @@ +from typing import Any + from django.conf import settings from django.db import models @@ -31,8 +33,13 @@ class Workflow(DefaultFieldsModel, OwnerModel, JSONConfigBase): environment = FlexibleForeignKey( "sentry.Environment", db_constraint=False, blank=True, null=True ) + created_by = HybridCloudForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete="SET_NULL") + @property + def CONFIG_SCHEMA(self) -> dict[str, Any]: + raise NotImplementedError('Subclasses must define a "CONFIG_SCHEMA" attribute') + __repr__ = sane_repr("name", "organization_id") class Meta: diff --git a/tests/sentry/workflow_engine/models/test_workflow.py b/tests/sentry/workflow_engine/models/test_workflow.py index ee284b24623fbb..af7aca38dbd6f0 100644 --- a/tests/sentry/workflow_engine/models/test_workflow.py +++ b/tests/sentry/workflow_engine/models/test_workflow.py @@ -45,8 +45,8 @@ def test_create_workflow_with_config__valid(self): name="test_workflow", organization=self.org, config={"frequency": 1} ) + workflow.validate_config() assert workflow.config == {"frequency": 1} - assert workflow.validate_config() is None def test_create_workflow_with_config__invalid(self): workflow = IssueAlertWorkflow( @@ -55,4 +55,4 @@ def test_create_workflow_with_config__invalid(self): assert workflow.config == {"freq": 2} with pytest.raises(ValidationError): - assert workflow.validate_config() + workflow.validate_config() From d071c24c224ffd5ac5bd1ef1d8a6604571d7ef97 Mon Sep 17 00:00:00 2001 From: Josh Callender <1569818+saponifi3d@users.noreply.github.com> Date: Wed, 20 Nov 2024 16:46:17 -0800 Subject: [PATCH 24/28] Remove the test for the mixin cause it seems like it's problematic. just trying a thing for CI since i can't reproduce it locally --- .../workflow_engine/models/test_workflow.py | 39 ------------------- 1 file changed, 39 deletions(-) diff --git a/tests/sentry/workflow_engine/models/test_workflow.py b/tests/sentry/workflow_engine/models/test_workflow.py index af7aca38dbd6f0..10cafc1b24f2de 100644 --- a/tests/sentry/workflow_engine/models/test_workflow.py +++ b/tests/sentry/workflow_engine/models/test_workflow.py @@ -1,29 +1,7 @@ -import pytest -from jsonschema import ValidationError - from sentry.testutils.cases import TestCase from sentry.workflow_engine.models import Workflow -class IssueAlertWorkflow(Workflow): - class Meta: - # These are added to make the tests work as expected - app_label = "workflow_engine" - managed = False - - # This will be needed in sub-classes - proxy = True - - # Define a JSON Schema for the test - CONFIG_SCHEMA = { - "type": "object", - "properties": { - "frequency": {"type": "number"}, - }, - "required": ["frequency"], - } - - class WorkflowTest(TestCase): def setUp(self): self.org = self.create_organization() @@ -39,20 +17,3 @@ def test_create_simple_workflow(self): assert workflow.name == "test_workflow" assert workflow.organization == self.org - - def test_create_workflow_with_config__valid(self): - workflow = IssueAlertWorkflow( - name="test_workflow", organization=self.org, config={"frequency": 1} - ) - - workflow.validate_config() - assert workflow.config == {"frequency": 1} - - def test_create_workflow_with_config__invalid(self): - workflow = IssueAlertWorkflow( - name="test_workflow", organization=self.org, config={"freq": 2} - ) - - assert workflow.config == {"freq": 2} - with pytest.raises(ValidationError): - workflow.validate_config() From eef52e43b28b22530eddcdda174442d39f0f0e29 Mon Sep 17 00:00:00 2001 From: Josh Callender <1569818+saponifi3d@users.noreply.github.com> Date: Thu, 21 Nov 2024 13:04:26 -0800 Subject: [PATCH 25/28] PR Feedback --- src/sentry/workflow_engine/models/detector.py | 6 +++--- src/sentry/workflow_engine/models/json_config.py | 2 +- src/sentry/workflow_engine/models/workflow.py | 12 ++++-------- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/sentry/workflow_engine/models/detector.py b/src/sentry/workflow_engine/models/detector.py index 421c51c3819bda..6a0ffeadc5f8f6 100644 --- a/src/sentry/workflow_engine/models/detector.py +++ b/src/sentry/workflow_engine/models/detector.py @@ -39,10 +39,10 @@ class Detector(DefaultFieldsModel, OwnerModel, JSONConfigBase): ) # If the detector is not enabled, it will not be evaluated. This is how we "snooze" a detector - enabled = models.BooleanField(default=True) + enabled = models.BooleanField(db_default=True) # Optionally set a description of the detector, this will be used in notifications - description = models.TextField(blank=True, null=True) + description = models.TextField(null=True) # This will emit an event for the workflow to process workflow_condition_group = FlexibleForeignKey( @@ -58,7 +58,7 @@ class Detector(DefaultFieldsModel, OwnerModel, JSONConfigBase): type = models.CharField(max_length=200) # The user that created the detector - created_by = HybridCloudForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete="SET_NULL") + created_by_id = HybridCloudForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete="SET_NULL") @property def CONFIG_SCHEMA(self) -> dict[str, Any]: diff --git a/src/sentry/workflow_engine/models/json_config.py b/src/sentry/workflow_engine/models/json_config.py index 5562c34fb2e8e4..1b353ccf18cbcb 100644 --- a/src/sentry/workflow_engine/models/json_config.py +++ b/src/sentry/workflow_engine/models/json_config.py @@ -6,7 +6,7 @@ class JSONConfigBase(models.Model): - config = models.JSONField(default=dict, blank=True, null=True) + config = models.JSONField(db_default={}) @abstractproperty def CONFIG_SCHEMA(self) -> dict[str, Any]: diff --git a/src/sentry/workflow_engine/models/workflow.py b/src/sentry/workflow_engine/models/workflow.py index 561c1f4cf4c3dd..d2f6e5fd5f0e01 100644 --- a/src/sentry/workflow_engine/models/workflow.py +++ b/src/sentry/workflow_engine/models/workflow.py @@ -23,18 +23,14 @@ class Workflow(DefaultFieldsModel, OwnerModel, JSONConfigBase): organization = FlexibleForeignKey("sentry.Organization") # If the workflow is not enabled, it will not be evaluated / invoke actions. This is how we "snooze" a workflow - enabled = models.BooleanField(default=True) + enabled = models.BooleanField(db_default=True) # Required as the 'when' condition for the workflow, this evalutes states emitted from the detectors - when_condition_group = FlexibleForeignKey( - "workflow_engine.DataConditionGroup", blank=True, null=True - ) + when_condition_group = FlexibleForeignKey("workflow_engine.DataConditionGroup", null=True) - environment = FlexibleForeignKey( - "sentry.Environment", db_constraint=False, blank=True, null=True - ) + environment = FlexibleForeignKey("sentry.Environment", null=True) - created_by = HybridCloudForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete="SET_NULL") + created_by_id = HybridCloudForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete="SET_NULL") @property def CONFIG_SCHEMA(self) -> dict[str, Any]: From 24e9f9bd49f03da0f999cdda0438557e912b4620 Mon Sep 17 00:00:00 2001 From: Josh Callender <1569818+saponifi3d@users.noreply.github.com> Date: Thu, 21 Nov 2024 13:07:02 -0800 Subject: [PATCH 26/28] GENERATED - SQL Migration --- migrations_lockfile.txt | 2 +- .../0014_model_additions_for_milestones.py | 120 ++++++++++++++++++ 2 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 src/sentry/workflow_engine/migrations/0014_model_additions_for_milestones.py diff --git a/migrations_lockfile.txt b/migrations_lockfile.txt index 2d5deeb60aa756..0b551bac149069 100644 --- a/migrations_lockfile.txt +++ b/migrations_lockfile.txt @@ -13,4 +13,4 @@ replays: 0004_index_together sentry: 0792_add_unique_index_apiauthorization social_auth: 0002_default_auto_field uptime: 0018_add_trace_sampling_field_to_uptime -workflow_engine: 0013_related_name_conditions_on_dcg +workflow_engine: 0014_model_additions_for_milestones diff --git a/src/sentry/workflow_engine/migrations/0014_model_additions_for_milestones.py b/src/sentry/workflow_engine/migrations/0014_model_additions_for_milestones.py new file mode 100644 index 00000000000000..00ab4ba74e3098 --- /dev/null +++ b/src/sentry/workflow_engine/migrations/0014_model_additions_for_milestones.py @@ -0,0 +1,120 @@ +# Generated by Django 5.1.1 on 2024-11-21 21:05 + +import django.db.models.deletion +from django.db import migrations, models + +import sentry.db.models.fields.foreignkey +import sentry.db.models.fields.hybrid_cloud_foreign_key +from sentry.new_migrations.migrations import CheckedMigration + + +class Migration(CheckedMigration): + # This flag is used to mark that a migration shouldn't be automatically run in production. + # This should only be used for operations where it's safe to run the migration after your + # code has deployed. So this should not be used for most operations that alter the schema + # of a table. + # Here are some things that make sense to mark as post deployment: + # - Large data migrations. Typically we want these to be run manually so that they can be + # monitored and not block the deploy for a long period of time while they run. + # - Adding indexes to large tables. Since this can take a long time, we'd generally prefer to + # run this outside deployments so that we don't block them. Note that while adding an index + # is a schema change, it's completely safe to run the operation after the code has deployed. + # Once deployed, run these manually via: https://develop.sentry.dev/database-migrations/#migration-deployment + + is_post_deployment = False + + dependencies = [ + ("sentry", "0792_add_unique_index_apiauthorization"), + ("workflow_engine", "0013_related_name_conditions_on_dcg"), + ] + + operations = [ + migrations.AddField( + model_name="detector", + name="config", + field=models.JSONField(db_default={}), + ), + migrations.AddField( + model_name="detector", + name="created_by_id", + field=sentry.db.models.fields.hybrid_cloud_foreign_key.HybridCloudForeignKey( + "sentry.User", db_index=True, null=True, on_delete="SET_NULL" + ), + ), + migrations.AddField( + model_name="detector", + name="description", + field=models.TextField(null=True), + ), + migrations.AddField( + model_name="detector", + name="enabled", + field=models.BooleanField(db_default=True), + ), + migrations.AddField( + model_name="detector", + name="project", + field=sentry.db.models.fields.foreignkey.FlexibleForeignKey( + null=True, on_delete=django.db.models.deletion.CASCADE, to="sentry.project" + ), + ), + migrations.AddField( + model_name="workflow", + name="config", + field=models.JSONField(db_default={}), + ), + migrations.AddField( + model_name="workflow", + name="created_by_id", + field=sentry.db.models.fields.hybrid_cloud_foreign_key.HybridCloudForeignKey( + "sentry.User", db_index=True, null=True, on_delete="SET_NULL" + ), + ), + migrations.AddField( + model_name="workflow", + name="enabled", + field=models.BooleanField(db_default=True), + ), + migrations.AddField( + model_name="workflow", + name="environment", + field=sentry.db.models.fields.foreignkey.FlexibleForeignKey( + null=True, on_delete=django.db.models.deletion.CASCADE, to="sentry.environment" + ), + ), + migrations.AddField( + model_name="workflow", + name="owner_team", + field=sentry.db.models.fields.foreignkey.FlexibleForeignKey( + null=True, on_delete=django.db.models.deletion.SET_NULL, to="sentry.team" + ), + ), + migrations.AddField( + model_name="workflow", + name="owner_user_id", + field=sentry.db.models.fields.hybrid_cloud_foreign_key.HybridCloudForeignKey( + "sentry.User", db_index=True, null=True, on_delete="SET_NULL" + ), + ), + migrations.AlterField( + model_name="action", + name="required", + field=models.BooleanField(default=False, null=True), + ), + migrations.AlterField( + model_name="detector", + name="organization", + field=sentry.db.models.fields.foreignkey.FlexibleForeignKey( + null=True, on_delete=django.db.models.deletion.CASCADE, to="sentry.organization" + ), + ), + migrations.AlterField( + model_name="workflow", + name="when_condition_group", + field=sentry.db.models.fields.foreignkey.FlexibleForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="workflow_engine.dataconditiongroup", + ), + ), + ] From 1bca30a28940e0af50146a4fcb4b7e796fdfce44 Mon Sep 17 00:00:00 2001 From: Josh Callender <1569818+saponifi3d@users.noreply.github.com> Date: Thu, 21 Nov 2024 13:08:19 -0800 Subject: [PATCH 27/28] GENERATED - Update Model Dependencies --- .../backup/model_dependencies/detailed.json | 32 ++++++++++++++++++- fixtures/backup/model_dependencies/flat.json | 4 +++ .../backup/model_dependencies/sorted.json | 12 +++---- .../backup/model_dependencies/truncate.json | 12 +++---- 4 files changed, 47 insertions(+), 13 deletions(-) diff --git a/fixtures/backup/model_dependencies/detailed.json b/fixtures/backup/model_dependencies/detailed.json index 64c19f847798a2..484150578d6b61 100644 --- a/fixtures/backup/model_dependencies/detailed.json +++ b/fixtures/backup/model_dependencies/detailed.json @@ -6507,10 +6507,15 @@ "workflow_engine.detector": { "dangling": true, "foreign_keys": { + "created_by_id": { + "kind": "HybridCloudForeignKey", + "model": "sentry.user", + "nullable": true + }, "organization": { "kind": "FlexibleForeignKey", "model": "sentry.organization", - "nullable": false + "nullable": true }, "owner_team": { "kind": "FlexibleForeignKey", @@ -6522,6 +6527,11 @@ "model": "sentry.user", "nullable": true }, + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.project", + "nullable": true + }, "workflow_condition_group": { "kind": "FlexibleForeignKey", "model": "workflow_engine.dataconditiongroup", @@ -6591,11 +6601,31 @@ "workflow_engine.workflow": { "dangling": false, "foreign_keys": { + "created_by_id": { + "kind": "HybridCloudForeignKey", + "model": "sentry.user", + "nullable": true + }, + "environment": { + "kind": "FlexibleForeignKey", + "model": "sentry.environment", + "nullable": true + }, "organization": { "kind": "FlexibleForeignKey", "model": "sentry.organization", "nullable": false }, + "owner_team": { + "kind": "FlexibleForeignKey", + "model": "sentry.team", + "nullable": true + }, + "owner_user_id": { + "kind": "HybridCloudForeignKey", + "model": "sentry.user", + "nullable": true + }, "when_condition_group": { "kind": "FlexibleForeignKey", "model": "workflow_engine.dataconditiongroup", diff --git a/fixtures/backup/model_dependencies/flat.json b/fixtures/backup/model_dependencies/flat.json index 42e4fe237389f3..19a7288b784fd6 100644 --- a/fixtures/backup/model_dependencies/flat.json +++ b/fixtures/backup/model_dependencies/flat.json @@ -899,6 +899,7 @@ ], "workflow_engine.detector": [ "sentry.organization", + "sentry.project", "sentry.team", "sentry.user", "workflow_engine.dataconditiongroup" @@ -911,7 +912,10 @@ "workflow_engine.workflow" ], "workflow_engine.workflow": [ + "sentry.environment", "sentry.organization", + "sentry.team", + "sentry.user", "workflow_engine.dataconditiongroup" ], "workflow_engine.workflowdataconditiongroup": [ diff --git a/fixtures/backup/model_dependencies/sorted.json b/fixtures/backup/model_dependencies/sorted.json index 66ced698878d1a..ed161f7396d898 100644 --- a/fixtures/backup/model_dependencies/sorted.json +++ b/fixtures/backup/model_dependencies/sorted.json @@ -53,12 +53,6 @@ "workflow_engine.dataconditiongroup", "workflow_engine.dataconditiongroupaction", "workflow_engine.datasource", - "workflow_engine.detector", - "workflow_engine.detectorstate", - "workflow_engine.workflow", - "workflow_engine.workflowdataconditiongroup", - "workflow_engine.detectorworkflow", - "workflow_engine.datasourcedetector", "workflow_engine.datacondition", "sentry.savedsearch", "sentry.rollbackuser", @@ -119,6 +113,9 @@ "flags.flagwebhooksigningsecretmodel", "flags.flagauditlogmodel", "feedback.feedback", + "workflow_engine.workflow", + "workflow_engine.detector", + "workflow_engine.datasourcedetector", "uptime.projectuptimesubscription", "sentry.userreport", "sentry.useroption", @@ -205,6 +202,9 @@ "sentry.apiauthorization", "sentry.alertrule", "hybridcloud.apitokenreplica", + "workflow_engine.workflowdataconditiongroup", + "workflow_engine.detectorworkflow", + "workflow_engine.detectorstate", "sentry.teamkeytransaction", "sentry.snubaqueryeventtype", "sentry.sentryappinstallation", diff --git a/fixtures/backup/model_dependencies/truncate.json b/fixtures/backup/model_dependencies/truncate.json index 8f0aa950ece716..ca18f7967a33b4 100644 --- a/fixtures/backup/model_dependencies/truncate.json +++ b/fixtures/backup/model_dependencies/truncate.json @@ -53,12 +53,6 @@ "workflow_engine_dataconditiongroup", "workflow_engine_dataconditiongroupaction", "workflow_engine_datasource", - "workflow_engine_detector", - "workflow_engine_detectorstate", - "workflow_engine_workflow", - "workflow_engine_workflowdataconditiongroup", - "workflow_engine_detectorworkflow", - "workflow_engine_datasourcedetector", "workflow_engine_datacondition", "sentry_savedsearch", "sentry_rollbackuser", @@ -119,6 +113,9 @@ "flags_webhooksigningsecret", "flags_audit_log", "feedback_feedback", + "workflow_engine_workflow", + "workflow_engine_detector", + "workflow_engine_datasourcedetector", "uptime_projectuptimesubscription", "sentry_userreport", "sentry_useroption", @@ -205,6 +202,9 @@ "sentry_apiauthorization", "sentry_alertrule", "hybridcloud_apitokenreplica", + "workflow_engine_workflowdataconditiongroup", + "workflow_engine_detectorworkflow", + "workflow_engine_detectorstate", "sentry_performanceteamkeytransaction", "sentry_snubaqueryeventtype", "sentry_sentryappinstallation", From f9fc70c7805fcaa6fb628cf21f4da361a307ea4f Mon Sep 17 00:00:00 2001 From: Josh Callender <1569818+saponifi3d@users.noreply.github.com> Date: Thu, 21 Nov 2024 13:12:02 -0800 Subject: [PATCH 28/28] GENERATED - Update snapshots --- .../test_comparators/test_default_comparators.pysnap | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/sentry/backup/snapshots/test_comparators/test_default_comparators.pysnap b/tests/sentry/backup/snapshots/test_comparators/test_default_comparators.pysnap index e8f0cbec356499..e7a8cbb8f59fd5 100644 --- a/tests/sentry/backup/snapshots/test_comparators/test_default_comparators.pysnap +++ b/tests/sentry/backup/snapshots/test_comparators/test_default_comparators.pysnap @@ -1,5 +1,5 @@ --- -created: '2024-11-15T01:55:40.666769+00:00' +created: '2024-11-21T21:10:04.656611+00:00' creator: sentry source: tests/sentry/backup/test_comparators.py --- @@ -1718,6 +1718,7 @@ source: tests/sentry/backup/test_comparators.py - date_updated - class: ForeignKeyComparator fields: + - created_by_id - organization - owner_team - owner_user_id @@ -1750,8 +1751,8 @@ source: tests/sentry/backup/test_comparators.py - date_updated - class: ForeignKeyComparator fields: - - created_by - - environment_id + - created_by_id + - environment - organization - owner_team - owner_user_id