diff --git a/bin/generate-model-dependency-fixtures b/bin/generate-model-dependency-fixtures new file mode 100755 index 00000000000000..168cd09436bd32 --- /dev/null +++ b/bin/generate-model-dependency-fixtures @@ -0,0 +1,53 @@ +#!/usr/bin/env python +from __future__ import annotations + +from sentry.runner import configure + +configure() + +import click + +from sentry.backup.dependencies import DependenciesJSONEncoder, dependencies, sorted_dependencies +from sentry.testutils.factories import get_fixture_path # noqa + +encoder = DependenciesJSONEncoder( + sort_keys=True, + ensure_ascii=True, + check_circular=True, + allow_nan=True, + indent=2, + encoding="utf-8", +) + + +@click.command() +def main(): + """Used for generating fixtures for the model dependency map.""" + + click.echo("\nThis script can take up to 30 seconds, please be patient...\n") + + # Do all of the calculation before any file writes, so that we don't end up with partial + # overwrites. + detailed = dependencies() + flat = {k: v.flatten() for k, v in detailed.items()} + sorted = sorted_dependencies() + + det_path = get_fixture_path("backup", "model_dependencies", "detailed.json") + with open(det_path, "w+") as fixture: + fixture.write(encoder.encode(detailed)) + + flat_path = get_fixture_path("backup", "model_dependencies", "flat.json") + with open(flat_path, "w+") as fixture: + fixture.write(encoder.encode(flat)) + + det_path = get_fixture_path("backup", "model_dependencies", "sorted.json") + with open(det_path, "w+") as fixture: + fixture.write(encoder.encode(sorted)) + + click.echo( + f"\nSuccess! The dependency mapping fixtures at {[det_path, flat_path]} were updated.\n" + ) + + +if __name__ == "__main__": + main() diff --git a/fixtures/backup/model_dependencies/detailed.json b/fixtures/backup/model_dependencies/detailed.json new file mode 100644 index 00000000000000..7b388a7abf8e9b --- /dev/null +++ b/fixtures/backup/model_dependencies/detailed.json @@ -0,0 +1,2579 @@ +{ + "nodestore.Node": { + "model": "nodestore.Node", + "relations": {}, + "silos": [ + "REGION" + ] + }, + "replays.ReplayRecordingSegment": { + "model": "replays.ReplayRecordingSegment", + "relations": {}, + "silos": [ + "REGION" + ] + }, + "sentry.Activity": { + "model": "sentry.Activity", + "relations": { + "group": { + "kind": "FlexibleForeignKey", + "model": "sentry.Group" + }, + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.Actor": { + "model": "sentry.Actor", + "relations": {}, + "silos": [ + "REGION" + ] + }, + "sentry.AlertRule": { + "model": "sentry.AlertRule", + "relations": { + "excluded_projects": { + "kind": "ManyToManyField", + "model": "sentry.Project" + }, + "organization": { + "kind": "FlexibleForeignKey", + "model": "sentry.Organization" + }, + "owner": { + "kind": "FlexibleForeignKey", + "model": "sentry.Actor" + }, + "snuba_query": { + "kind": "FlexibleForeignKey", + "model": "sentry.SnubaQuery" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.AlertRuleActivity": { + "model": "sentry.AlertRuleActivity", + "relations": { + "alert_rule": { + "kind": "FlexibleForeignKey", + "model": "sentry.AlertRule" + }, + "previous_alert_rule": { + "kind": "FlexibleForeignKey", + "model": "sentry.AlertRule" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.AlertRuleExcludedProjects": { + "model": "sentry.AlertRuleExcludedProjects", + "relations": { + "alert_rule": { + "kind": "FlexibleForeignKey", + "model": "sentry.AlertRule" + }, + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.AlertRuleTrigger": { + "model": "sentry.AlertRuleTrigger", + "relations": { + "alert_rule": { + "kind": "FlexibleForeignKey", + "model": "sentry.AlertRule" + }, + "triggered_incidents": { + "kind": "ManyToManyField", + "model": "sentry.Incident" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.AlertRuleTriggerAction": { + "model": "sentry.AlertRuleTriggerAction", + "relations": { + "alert_rule_trigger": { + "kind": "FlexibleForeignKey", + "model": "sentry.AlertRuleTrigger" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.AlertRuleTriggerExclusion": { + "model": "sentry.AlertRuleTriggerExclusion", + "relations": { + "alert_rule_trigger": { + "kind": "FlexibleForeignKey", + "model": "sentry.AlertRuleTrigger" + }, + "query_subscription": { + "kind": "FlexibleForeignKey", + "model": "sentry.QuerySubscription" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.ApiApplication": { + "model": "sentry.ApiApplication", + "relations": { + "owner": { + "kind": "FlexibleForeignKey", + "model": "sentry.User" + } + }, + "silos": [ + "CONTROL" + ] + }, + "sentry.ApiAuthorization": { + "model": "sentry.ApiAuthorization", + "relations": { + "application": { + "kind": "FlexibleForeignKey", + "model": "sentry.ApiApplication" + }, + "user": { + "kind": "FlexibleForeignKey", + "model": "sentry.User" + } + }, + "silos": [ + "CONTROL" + ] + }, + "sentry.ApiGrant": { + "model": "sentry.ApiGrant", + "relations": { + "application": { + "kind": "FlexibleForeignKey", + "model": "sentry.ApiApplication" + }, + "user": { + "kind": "FlexibleForeignKey", + "model": "sentry.User" + } + }, + "silos": [ + "CONTROL" + ] + }, + "sentry.ApiKey": { + "model": "sentry.ApiKey", + "relations": {}, + "silos": [ + "CONTROL" + ] + }, + "sentry.ApiToken": { + "model": "sentry.ApiToken", + "relations": { + "application": { + "kind": "FlexibleForeignKey", + "model": "sentry.ApiApplication" + }, + "user": { + "kind": "FlexibleForeignKey", + "model": "sentry.User" + } + }, + "silos": [ + "CONTROL" + ] + }, + "sentry.AppConnectBuild": { + "model": "sentry.AppConnectBuild", + "relations": { + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.ArtifactBundle": { + "model": "sentry.ArtifactBundle", + "relations": { + "file": { + "kind": "FlexibleForeignKey", + "model": "sentry.File" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.ArtifactBundleFlatFileIndex": { + "model": "sentry.ArtifactBundleFlatFileIndex", + "relations": { + "flat_file_index": { + "kind": "FlexibleForeignKey", + "model": "sentry.File" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.ArtifactBundleIndex": { + "model": "sentry.ArtifactBundleIndex", + "relations": { + "artifact_bundle": { + "kind": "FlexibleForeignKey", + "model": "sentry.ArtifactBundle" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.AssistantActivity": { + "model": "sentry.AssistantActivity", + "relations": { + "user": { + "kind": "FlexibleForeignKey", + "model": "sentry.User" + } + }, + "silos": [ + "CONTROL" + ] + }, + "sentry.AuditLogEntry": { + "model": "sentry.AuditLogEntry", + "relations": { + "actor": { + "kind": "FlexibleForeignKey", + "model": "sentry.User" + }, + "actor_key": { + "kind": "FlexibleForeignKey", + "model": "sentry.ApiKey" + }, + "target_user": { + "kind": "FlexibleForeignKey", + "model": "sentry.User" + } + }, + "silos": [ + "CONTROL" + ] + }, + "sentry.AuthIdentity": { + "model": "sentry.AuthIdentity", + "relations": { + "auth_provider": { + "kind": "FlexibleForeignKey", + "model": "sentry.AuthProvider" + }, + "user": { + "kind": "FlexibleForeignKey", + "model": "sentry.User" + } + }, + "silos": [ + "CONTROL" + ] + }, + "sentry.AuthProvider": { + "model": "sentry.AuthProvider", + "relations": {}, + "silos": [ + "CONTROL" + ] + }, + "sentry.AuthProviderDefaultTeams": { + "model": "sentry.AuthProviderDefaultTeams", + "relations": {}, + "silos": [ + "CONTROL" + ] + }, + "sentry.Authenticator": { + "model": "sentry.Authenticator", + "relations": { + "user": { + "kind": "FlexibleForeignKey", + "model": "sentry.User" + } + }, + "silos": [ + "CONTROL" + ] + }, + "sentry.Broadcast": { + "model": "sentry.Broadcast", + "relations": {}, + "silos": [ + "CONTROL" + ] + }, + "sentry.BroadcastSeen": { + "model": "sentry.BroadcastSeen", + "relations": { + "broadcast": { + "kind": "FlexibleForeignKey", + "model": "sentry.Broadcast" + }, + "user": { + "kind": "FlexibleForeignKey", + "model": "sentry.User" + } + }, + "silos": [ + "CONTROL" + ] + }, + "sentry.Commit": { + "model": "sentry.Commit", + "relations": { + "author": { + "kind": "FlexibleForeignKey", + "model": "sentry.CommitAuthor" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.CommitAuthor": { + "model": "sentry.CommitAuthor", + "relations": {}, + "silos": [ + "REGION" + ] + }, + "sentry.CommitFileChange": { + "model": "sentry.CommitFileChange", + "relations": { + "commit": { + "kind": "FlexibleForeignKey", + "model": "sentry.Commit" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.ControlFile": { + "model": "sentry.ControlFile", + "relations": { + "blobs": { + "kind": "ManyToManyField", + "model": "sentry.ControlFileBlob" + } + }, + "silos": [ + "CONTROL" + ] + }, + "sentry.ControlFileBlob": { + "model": "sentry.ControlFileBlob", + "relations": {}, + "silos": [ + "CONTROL" + ] + }, + "sentry.ControlFileBlobIndex": { + "model": "sentry.ControlFileBlobIndex", + "relations": { + "blob": { + "kind": "FlexibleForeignKey", + "model": "sentry.ControlFileBlob" + }, + "file": { + "kind": "FlexibleForeignKey", + "model": "sentry.ControlFile" + } + }, + "silos": [ + "CONTROL" + ] + }, + "sentry.ControlFileBlobOwner": { + "model": "sentry.ControlFileBlobOwner", + "relations": { + "blob": { + "kind": "FlexibleForeignKey", + "model": "sentry.ControlFileBlob" + } + }, + "silos": [ + "CONTROL" + ] + }, + "sentry.ControlOption": { + "model": "sentry.ControlOption", + "relations": {}, + "silos": [ + "CONTROL" + ] + }, + "sentry.ControlOutbox": { + "model": "sentry.ControlOutbox", + "relations": {}, + "silos": [ + "CONTROL" + ] + }, + "sentry.ControlTombstone": { + "model": "sentry.ControlTombstone", + "relations": {}, + "silos": [ + "CONTROL" + ] + }, + "sentry.Counter": { + "model": "sentry.Counter", + "relations": { + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.Dashboard": { + "model": "sentry.Dashboard", + "relations": { + "organization": { + "kind": "FlexibleForeignKey", + "model": "sentry.Organization" + }, + "projects": { + "kind": "ManyToManyField", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.DashboardProject": { + "model": "sentry.DashboardProject", + "relations": { + "dashboard": { + "kind": "FlexibleForeignKey", + "model": "sentry.Dashboard" + }, + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.DashboardTombstone": { + "model": "sentry.DashboardTombstone", + "relations": { + "organization": { + "kind": "FlexibleForeignKey", + "model": "sentry.Organization" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.DashboardWidget": { + "model": "sentry.DashboardWidget", + "relations": { + "dashboard": { + "kind": "FlexibleForeignKey", + "model": "sentry.Dashboard" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.DashboardWidgetQuery": { + "model": "sentry.DashboardWidgetQuery", + "relations": { + "widget": { + "kind": "FlexibleForeignKey", + "model": "sentry.DashboardWidget" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.DebugIdArtifactBundle": { + "model": "sentry.DebugIdArtifactBundle", + "relations": { + "artifact_bundle": { + "kind": "FlexibleForeignKey", + "model": "sentry.ArtifactBundle" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.DeletedOrganization": { + "model": "sentry.DeletedOrganization", + "relations": {}, + "silos": [ + "REGION" + ] + }, + "sentry.DeletedProject": { + "model": "sentry.DeletedProject", + "relations": {}, + "silos": [ + "REGION" + ] + }, + "sentry.DeletedTeam": { + "model": "sentry.DeletedTeam", + "relations": {}, + "silos": [ + "REGION" + ] + }, + "sentry.Deploy": { + "model": "sentry.Deploy", + "relations": { + "release": { + "kind": "FlexibleForeignKey", + "model": "sentry.Release" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.DiscoverSavedQuery": { + "model": "sentry.DiscoverSavedQuery", + "relations": { + "organization": { + "kind": "FlexibleForeignKey", + "model": "sentry.Organization" + }, + "projects": { + "kind": "ManyToManyField", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.DiscoverSavedQueryProject": { + "model": "sentry.DiscoverSavedQueryProject", + "relations": { + "discover_saved_query": { + "kind": "FlexibleForeignKey", + "model": "sentry.DiscoverSavedQuery" + }, + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.Distribution": { + "model": "sentry.Distribution", + "relations": { + "release": { + "kind": "FlexibleForeignKey", + "model": "sentry.Release" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.DocIntegration": { + "model": "sentry.DocIntegration", + "relations": {}, + "silos": [ + "CONTROL" + ] + }, + "sentry.DocIntegrationAvatar": { + "model": "sentry.DocIntegrationAvatar", + "relations": { + "doc_integration": { + "kind": "FlexibleForeignKey", + "model": "sentry.DocIntegration" + } + }, + "silos": [ + "CONTROL" + ] + }, + "sentry.Email": { + "model": "sentry.Email", + "relations": {}, + "silos": [ + "CONTROL" + ] + }, + "sentry.Environment": { + "model": "sentry.Environment", + "relations": { + "projects": { + "kind": "ManyToManyField", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.EnvironmentProject": { + "model": "sentry.EnvironmentProject", + "relations": { + "environment": { + "kind": "FlexibleForeignKey", + "model": "sentry.Environment" + }, + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.EventAttachment": { + "model": "sentry.EventAttachment", + "relations": {}, + "silos": [ + "REGION" + ] + }, + "sentry.EventProcessingIssue": { + "model": "sentry.EventProcessingIssue", + "relations": { + "processing_issue": { + "kind": "FlexibleForeignKey", + "model": "sentry.ProcessingIssue" + }, + "raw_event": { + "kind": "FlexibleForeignKey", + "model": "sentry.RawEvent" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.EventUser": { + "model": "sentry.EventUser", + "relations": {}, + "silos": [ + "REGION" + ] + }, + "sentry.ExportedData": { + "model": "sentry.ExportedData", + "relations": { + "organization": { + "kind": "FlexibleForeignKey", + "model": "sentry.Organization" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.ExportedDataBlob": { + "model": "sentry.ExportedDataBlob", + "relations": { + "data_export": { + "kind": "FlexibleForeignKey", + "model": "sentry.ExportedData" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.ExternalActor": { + "model": "sentry.ExternalActor", + "relations": { + "actor": { + "kind": "FlexibleForeignKey", + "model": "sentry.Actor" + }, + "organization": { + "kind": "FlexibleForeignKey", + "model": "sentry.Organization" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.ExternalIssue": { + "model": "sentry.ExternalIssue", + "relations": { + "organization": { + "kind": "FlexibleForeignKey", + "model": "sentry.Organization" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.FeatureAdoption": { + "model": "sentry.FeatureAdoption", + "relations": { + "organization": { + "kind": "FlexibleForeignKey", + "model": "sentry.Organization" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.File": { + "model": "sentry.File", + "relations": { + "blob": { + "kind": "FlexibleForeignKey", + "model": "sentry.FileBlob" + }, + "blobs": { + "kind": "ManyToManyField", + "model": "sentry.FileBlob" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.FileBlob": { + "model": "sentry.FileBlob", + "relations": {}, + "silos": [ + "REGION" + ] + }, + "sentry.FileBlobIndex": { + "model": "sentry.FileBlobIndex", + "relations": { + "blob": { + "kind": "FlexibleForeignKey", + "model": "sentry.FileBlob" + }, + "file": { + "kind": "FlexibleForeignKey", + "model": "sentry.File" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.FileBlobOwner": { + "model": "sentry.FileBlobOwner", + "relations": { + "blob": { + "kind": "FlexibleForeignKey", + "model": "sentry.FileBlob" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.FlatFileIndexState": { + "model": "sentry.FlatFileIndexState", + "relations": { + "artifact_bundle": { + "kind": "FlexibleForeignKey", + "model": "sentry.ArtifactBundle" + }, + "flat_file_index": { + "kind": "FlexibleForeignKey", + "model": "sentry.ArtifactBundleFlatFileIndex" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.Group": { + "model": "sentry.Group", + "relations": { + "first_release": { + "kind": "FlexibleForeignKey", + "model": "sentry.Release" + }, + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.GroupAssignee": { + "model": "sentry.GroupAssignee", + "relations": { + "group": { + "kind": "FlexibleForeignKey", + "model": "sentry.Group" + }, + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + }, + "team": { + "kind": "FlexibleForeignKey", + "model": "sentry.Team" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.GroupBookmark": { + "model": "sentry.GroupBookmark", + "relations": { + "group": { + "kind": "FlexibleForeignKey", + "model": "sentry.Group" + }, + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.GroupCommitResolution": { + "model": "sentry.GroupCommitResolution", + "relations": {}, + "silos": [ + "REGION" + ] + }, + "sentry.GroupEmailThread": { + "model": "sentry.GroupEmailThread", + "relations": { + "group": { + "kind": "FlexibleForeignKey", + "model": "sentry.Group" + }, + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.GroupEnvironment": { + "model": "sentry.GroupEnvironment", + "relations": { + "environment": { + "kind": "FlexibleForeignKey", + "model": "sentry.Environment" + }, + "first_release": { + "kind": "FlexibleForeignKey", + "model": "sentry.Release" + }, + "group": { + "kind": "FlexibleForeignKey", + "model": "sentry.Group" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.GroupHash": { + "model": "sentry.GroupHash", + "relations": { + "group": { + "kind": "FlexibleForeignKey", + "model": "sentry.Group" + }, + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.GroupHistory": { + "model": "sentry.GroupHistory", + "relations": { + "actor": { + "kind": "FlexibleForeignKey", + "model": "sentry.Actor" + }, + "group": { + "kind": "FlexibleForeignKey", + "model": "sentry.Group" + }, + "organization": { + "kind": "FlexibleForeignKey", + "model": "sentry.Organization" + }, + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + }, + "release": { + "kind": "FlexibleForeignKey", + "model": "sentry.Release" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.GroupInbox": { + "model": "sentry.GroupInbox", + "relations": { + "group": { + "kind": "FlexibleForeignKey", + "model": "sentry.Group" + }, + "organization": { + "kind": "FlexibleForeignKey", + "model": "sentry.Organization" + }, + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.GroupLink": { + "model": "sentry.GroupLink", + "relations": { + "group": { + "kind": "FlexibleForeignKey", + "model": "sentry.Group" + }, + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.GroupMeta": { + "model": "sentry.GroupMeta", + "relations": { + "group": { + "kind": "FlexibleForeignKey", + "model": "sentry.Group" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.GroupOwner": { + "model": "sentry.GroupOwner", + "relations": { + "group": { + "kind": "FlexibleForeignKey", + "model": "sentry.Group" + }, + "organization": { + "kind": "FlexibleForeignKey", + "model": "sentry.Organization" + }, + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + }, + "team": { + "kind": "FlexibleForeignKey", + "model": "sentry.Team" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.GroupRedirect": { + "model": "sentry.GroupRedirect", + "relations": {}, + "silos": [ + "REGION" + ] + }, + "sentry.GroupRelease": { + "model": "sentry.GroupRelease", + "relations": {}, + "silos": [ + "REGION" + ] + }, + "sentry.GroupResolution": { + "model": "sentry.GroupResolution", + "relations": { + "group": { + "kind": "FlexibleForeignKey", + "model": "sentry.Group" + }, + "release": { + "kind": "FlexibleForeignKey", + "model": "sentry.Release" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.GroupRuleStatus": { + "model": "sentry.GroupRuleStatus", + "relations": { + "group": { + "kind": "FlexibleForeignKey", + "model": "sentry.Group" + }, + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + }, + "rule": { + "kind": "FlexibleForeignKey", + "model": "sentry.Rule" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.GroupSeen": { + "model": "sentry.GroupSeen", + "relations": { + "group": { + "kind": "FlexibleForeignKey", + "model": "sentry.Group" + }, + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.GroupShare": { + "model": "sentry.GroupShare", + "relations": { + "group": { + "kind": "FlexibleForeignKey", + "model": "sentry.Group" + }, + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.GroupSnooze": { + "model": "sentry.GroupSnooze", + "relations": { + "group": { + "kind": "FlexibleForeignKey", + "model": "sentry.Group" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.GroupSubscription": { + "model": "sentry.GroupSubscription", + "relations": { + "group": { + "kind": "FlexibleForeignKey", + "model": "sentry.Group" + }, + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.GroupTombstone": { + "model": "sentry.GroupTombstone", + "relations": { + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.Identity": { + "model": "sentry.Identity", + "relations": { + "idp": { + "kind": "FlexibleForeignKey", + "model": "sentry.IdentityProvider" + }, + "user": { + "kind": "FlexibleForeignKey", + "model": "sentry.User" + } + }, + "silos": [ + "CONTROL" + ] + }, + "sentry.IdentityProvider": { + "model": "sentry.IdentityProvider", + "relations": {}, + "silos": [ + "CONTROL" + ] + }, + "sentry.Incident": { + "model": "sentry.Incident", + "relations": { + "alert_rule": { + "kind": "FlexibleForeignKey", + "model": "sentry.AlertRule" + }, + "organization": { + "kind": "FlexibleForeignKey", + "model": "sentry.Organization" + }, + "projects": { + "kind": "ManyToManyField", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.IncidentActivity": { + "model": "sentry.IncidentActivity", + "relations": { + "incident": { + "kind": "FlexibleForeignKey", + "model": "sentry.Incident" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.IncidentProject": { + "model": "sentry.IncidentProject", + "relations": { + "incident": { + "kind": "FlexibleForeignKey", + "model": "sentry.Incident" + }, + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.IncidentSeen": { + "model": "sentry.IncidentSeen", + "relations": { + "incident": { + "kind": "FlexibleForeignKey", + "model": "sentry.Incident" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.IncidentSnapshot": { + "model": "sentry.IncidentSnapshot", + "relations": { + "event_stats_snapshot": { + "kind": "FlexibleForeignKey", + "model": "sentry.TimeSeriesSnapshot" + }, + "incident": { + "kind": "OneToOneCascadeDeletes", + "model": "sentry.Incident" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.IncidentSubscription": { + "model": "sentry.IncidentSubscription", + "relations": { + "incident": { + "kind": "FlexibleForeignKey", + "model": "sentry.Incident" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.IncidentTrigger": { + "model": "sentry.IncidentTrigger", + "relations": { + "alert_rule_trigger": { + "kind": "FlexibleForeignKey", + "model": "sentry.AlertRuleTrigger" + }, + "incident": { + "kind": "FlexibleForeignKey", + "model": "sentry.Incident" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.Integration": { + "model": "sentry.Integration", + "relations": {}, + "silos": [ + "CONTROL" + ] + }, + "sentry.IntegrationExternalProject": { + "model": "sentry.IntegrationExternalProject", + "relations": {}, + "silos": [ + "CONTROL" + ] + }, + "sentry.IntegrationFeature": { + "model": "sentry.IntegrationFeature", + "relations": {}, + "silos": [ + "CONTROL" + ] + }, + "sentry.LatestAppConnectBuildsCheck": { + "model": "sentry.LatestAppConnectBuildsCheck", + "relations": { + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.LatestRepoReleaseEnvironment": { + "model": "sentry.LatestRepoReleaseEnvironment", + "relations": {}, + "silos": [ + "REGION" + ] + }, + "sentry.LostPasswordHash": { + "model": "sentry.LostPasswordHash", + "relations": { + "user": { + "kind": "FlexibleForeignKey", + "model": "sentry.User" + } + }, + "silos": [ + "CONTROL" + ] + }, + "sentry.MetricsKeyIndexer": { + "model": "sentry.MetricsKeyIndexer", + "relations": {}, + "silos": [ + "REGION" + ] + }, + "sentry.Monitor": { + "model": "sentry.Monitor", + "relations": {}, + "silos": [ + "REGION" + ] + }, + "sentry.MonitorCheckIn": { + "model": "sentry.MonitorCheckIn", + "relations": { + "location": { + "kind": "FlexibleForeignKey", + "model": "sentry.MonitorLocation" + }, + "monitor": { + "kind": "FlexibleForeignKey", + "model": "sentry.Monitor" + }, + "monitor_environment": { + "kind": "FlexibleForeignKey", + "model": "sentry.MonitorEnvironment" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.MonitorEnvironment": { + "model": "sentry.MonitorEnvironment", + "relations": { + "environment": { + "kind": "FlexibleForeignKey", + "model": "sentry.Environment" + }, + "monitor": { + "kind": "FlexibleForeignKey", + "model": "sentry.Monitor" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.MonitorLocation": { + "model": "sentry.MonitorLocation", + "relations": {}, + "silos": [ + "REGION" + ] + }, + "sentry.NotificationAction": { + "model": "sentry.NotificationAction", + "relations": { + "organization": { + "kind": "FlexibleForeignKey", + "model": "sentry.Organization" + }, + "projects": { + "kind": "ManyToManyField", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.NotificationActionProject": { + "model": "sentry.NotificationActionProject", + "relations": { + "action": { + "kind": "FlexibleForeignKey", + "model": "sentry.NotificationAction" + }, + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.NotificationSetting": { + "model": "sentry.NotificationSetting", + "relations": { + "user": { + "kind": "FlexibleForeignKey", + "model": "sentry.User" + } + }, + "silos": [ + "CONTROL" + ] + }, + "sentry.Option": { + "model": "sentry.Option", + "relations": {}, + "silos": [ + "REGION" + ] + }, + "sentry.OrgAuthToken": { + "model": "sentry.OrgAuthToken", + "relations": { + "created_by": { + "kind": "FlexibleForeignKey", + "model": "sentry.User" + } + }, + "silos": [ + "CONTROL" + ] + }, + "sentry.Organization": { + "model": "sentry.Organization", + "relations": {}, + "silos": [ + "REGION" + ] + }, + "sentry.OrganizationAccessRequest": { + "model": "sentry.OrganizationAccessRequest", + "relations": { + "member": { + "kind": "FlexibleForeignKey", + "model": "sentry.OrganizationMember" + }, + "team": { + "kind": "FlexibleForeignKey", + "model": "sentry.Team" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.OrganizationAvatar": { + "model": "sentry.OrganizationAvatar", + "relations": { + "organization": { + "kind": "FlexibleForeignKey", + "model": "sentry.Organization" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.OrganizationIntegration": { + "model": "sentry.OrganizationIntegration", + "relations": { + "integration": { + "kind": "FlexibleForeignKey", + "model": "sentry.Integration" + } + }, + "silos": [ + "CONTROL" + ] + }, + "sentry.OrganizationMapping": { + "model": "sentry.OrganizationMapping", + "relations": {}, + "silos": [ + "CONTROL" + ] + }, + "sentry.OrganizationMember": { + "model": "sentry.OrganizationMember", + "relations": { + "organization": { + "kind": "FlexibleForeignKey", + "model": "sentry.Organization" + }, + "teams": { + "kind": "ManyToManyField", + "model": "sentry.Team" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.OrganizationMemberMapping": { + "model": "sentry.OrganizationMemberMapping", + "relations": { + "inviter": { + "kind": "FlexibleForeignKey", + "model": "sentry.User" + }, + "user": { + "kind": "FlexibleForeignKey", + "model": "sentry.User" + } + }, + "silos": [ + "CONTROL" + ] + }, + "sentry.OrganizationMemberTeam": { + "model": "sentry.OrganizationMemberTeam", + "relations": { + "organizationmember": { + "kind": "FlexibleForeignKey", + "model": "sentry.OrganizationMember" + }, + "team": { + "kind": "FlexibleForeignKey", + "model": "sentry.Team" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.OrganizationOnboardingTask": { + "model": "sentry.OrganizationOnboardingTask", + "relations": { + "organization": { + "kind": "FlexibleForeignKey", + "model": "sentry.Organization" + }, + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.OrganizationOption": { + "model": "sentry.OrganizationOption", + "relations": { + "organization": { + "kind": "FlexibleForeignKey", + "model": "sentry.Organization" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.PagerDutyService": { + "model": "sentry.PagerDutyService", + "relations": { + "organization_integration": { + "kind": "FlexibleForeignKey", + "model": "sentry.OrganizationIntegration" + } + }, + "silos": [ + "CONTROL" + ] + }, + "sentry.PendingIncidentSnapshot": { + "model": "sentry.PendingIncidentSnapshot", + "relations": { + "incident": { + "kind": "OneToOneCascadeDeletes", + "model": "sentry.Incident" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.PerfStringIndexer": { + "model": "sentry.PerfStringIndexer", + "relations": {}, + "silos": [ + "REGION" + ] + }, + "sentry.PlatformExternalIssue": { + "model": "sentry.PlatformExternalIssue", + "relations": { + "group": { + "kind": "FlexibleForeignKey", + "model": "sentry.Group" + }, + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.ProcessingIssue": { + "model": "sentry.ProcessingIssue", + "relations": { + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.ProguardArtifactRelease": { + "model": "sentry.ProguardArtifactRelease", + "relations": { + "project_debug_file": { + "kind": "FlexibleForeignKey", + "model": "sentry.ProjectDebugFile" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.Project": { + "model": "sentry.Project", + "relations": { + "organization": { + "kind": "FlexibleForeignKey", + "model": "sentry.Organization" + }, + "teams": { + "kind": "ManyToManyField", + "model": "sentry.Team" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.ProjectArtifactBundle": { + "model": "sentry.ProjectArtifactBundle", + "relations": { + "artifact_bundle": { + "kind": "FlexibleForeignKey", + "model": "sentry.ArtifactBundle" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.ProjectAvatar": { + "model": "sentry.ProjectAvatar", + "relations": { + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.ProjectBookmark": { + "model": "sentry.ProjectBookmark", + "relations": { + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.ProjectCodeOwners": { + "model": "sentry.ProjectCodeOwners", + "relations": { + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + }, + "repository_project_path_config": { + "kind": "FlexibleForeignKey", + "model": "sentry.RepositoryProjectPathConfig" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.ProjectDebugFile": { + "model": "sentry.ProjectDebugFile", + "relations": { + "file": { + "kind": "FlexibleForeignKey", + "model": "sentry.File" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.ProjectIntegration": { + "model": "sentry.ProjectIntegration", + "relations": { + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.ProjectKey": { + "model": "sentry.ProjectKey", + "relations": { + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.ProjectOption": { + "model": "sentry.ProjectOption", + "relations": { + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.ProjectOwnership": { + "model": "sentry.ProjectOwnership", + "relations": { + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.ProjectPlatform": { + "model": "sentry.ProjectPlatform", + "relations": {}, + "silos": [ + "REGION" + ] + }, + "sentry.ProjectRedirect": { + "model": "sentry.ProjectRedirect", + "relations": { + "organization": { + "kind": "FlexibleForeignKey", + "model": "sentry.Organization" + }, + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.ProjectTeam": { + "model": "sentry.ProjectTeam", + "relations": { + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + }, + "team": { + "kind": "FlexibleForeignKey", + "model": "sentry.Team" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.ProjectTransactionThreshold": { + "model": "sentry.ProjectTransactionThreshold", + "relations": { + "organization": { + "kind": "FlexibleForeignKey", + "model": "sentry.Organization" + }, + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.ProjectTransactionThresholdOverride": { + "model": "sentry.ProjectTransactionThresholdOverride", + "relations": { + "organization": { + "kind": "FlexibleForeignKey", + "model": "sentry.Organization" + }, + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.PromptsActivity": { + "model": "sentry.PromptsActivity", + "relations": {}, + "silos": [ + "REGION" + ] + }, + "sentry.PullRequest": { + "model": "sentry.PullRequest", + "relations": { + "author": { + "kind": "FlexibleForeignKey", + "model": "sentry.CommitAuthor" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.PullRequestComment": { + "model": "sentry.PullRequestComment", + "relations": { + "pull_request": { + "kind": "FlexibleForeignKey", + "model": "sentry.PullRequest" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.PullRequestCommit": { + "model": "sentry.PullRequestCommit", + "relations": { + "commit": { + "kind": "FlexibleForeignKey", + "model": "sentry.Commit" + }, + "pull_request": { + "kind": "FlexibleForeignKey", + "model": "sentry.PullRequest" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.QuerySubscription": { + "model": "sentry.QuerySubscription", + "relations": { + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + }, + "snuba_query": { + "kind": "FlexibleForeignKey", + "model": "sentry.SnubaQuery" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.RawEvent": { + "model": "sentry.RawEvent", + "relations": { + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.RecentSearch": { + "model": "sentry.RecentSearch", + "relations": { + "organization": { + "kind": "FlexibleForeignKey", + "model": "sentry.Organization" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.RegionOutbox": { + "model": "sentry.RegionOutbox", + "relations": {}, + "silos": [ + "REGION" + ] + }, + "sentry.RegionScheduledDeletion": { + "model": "sentry.RegionScheduledDeletion", + "relations": {}, + "silos": [ + "REGION" + ] + }, + "sentry.RegionTombstone": { + "model": "sentry.RegionTombstone", + "relations": {}, + "silos": [ + "REGION" + ] + }, + "sentry.Relay": { + "model": "sentry.Relay", + "relations": {}, + "silos": [ + "REGION" + ] + }, + "sentry.RelayUsage": { + "model": "sentry.RelayUsage", + "relations": {}, + "silos": [ + "REGION" + ] + }, + "sentry.Release": { + "model": "sentry.Release", + "relations": { + "organization": { + "kind": "FlexibleForeignKey", + "model": "sentry.Organization" + }, + "projects": { + "kind": "ManyToManyField", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.ReleaseActivity": { + "model": "sentry.ReleaseActivity", + "relations": { + "release": { + "kind": "FlexibleForeignKey", + "model": "sentry.Release" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.ReleaseArtifactBundle": { + "model": "sentry.ReleaseArtifactBundle", + "relations": { + "artifact_bundle": { + "kind": "FlexibleForeignKey", + "model": "sentry.ArtifactBundle" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.ReleaseCommit": { + "model": "sentry.ReleaseCommit", + "relations": { + "commit": { + "kind": "FlexibleForeignKey", + "model": "sentry.Commit" + }, + "release": { + "kind": "FlexibleForeignKey", + "model": "sentry.Release" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.ReleaseEnvironment": { + "model": "sentry.ReleaseEnvironment", + "relations": { + "environment": { + "kind": "FlexibleForeignKey", + "model": "sentry.Environment" + }, + "organization": { + "kind": "FlexibleForeignKey", + "model": "sentry.Organization" + }, + "release": { + "kind": "FlexibleForeignKey", + "model": "sentry.Release" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.ReleaseFile": { + "model": "sentry.ReleaseFile", + "relations": { + "file": { + "kind": "FlexibleForeignKey", + "model": "sentry.File" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.ReleaseHeadCommit": { + "model": "sentry.ReleaseHeadCommit", + "relations": { + "commit": { + "kind": "FlexibleForeignKey", + "model": "sentry.Commit" + }, + "release": { + "kind": "FlexibleForeignKey", + "model": "sentry.Release" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.ReleaseProject": { + "model": "sentry.ReleaseProject", + "relations": { + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + }, + "release": { + "kind": "FlexibleForeignKey", + "model": "sentry.Release" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.ReleaseProjectEnvironment": { + "model": "sentry.ReleaseProjectEnvironment", + "relations": { + "environment": { + "kind": "FlexibleForeignKey", + "model": "sentry.Environment" + }, + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + }, + "release": { + "kind": "FlexibleForeignKey", + "model": "sentry.Release" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.Repository": { + "model": "sentry.Repository", + "relations": {}, + "silos": [ + "REGION" + ] + }, + "sentry.RepositoryProjectPathConfig": { + "model": "sentry.RepositoryProjectPathConfig", + "relations": { + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + }, + "repository": { + "kind": "FlexibleForeignKey", + "model": "sentry.Repository" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.ReprocessingReport": { + "model": "sentry.ReprocessingReport", + "relations": { + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.Rule": { + "model": "sentry.Rule", + "relations": { + "owner": { + "kind": "FlexibleForeignKey", + "model": "sentry.Actor" + }, + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.RuleActivity": { + "model": "sentry.RuleActivity", + "relations": { + "rule": { + "kind": "FlexibleForeignKey", + "model": "sentry.Rule" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.RuleFireHistory": { + "model": "sentry.RuleFireHistory", + "relations": { + "group": { + "kind": "FlexibleForeignKey", + "model": "sentry.Group" + }, + "project": { + "kind": "FlexibleForeignKey", + "model": "sentry.Project" + }, + "rule": { + "kind": "FlexibleForeignKey", + "model": "sentry.Rule" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.RuleSnooze": { + "model": "sentry.RuleSnooze", + "relations": { + "alert_rule": { + "kind": "FlexibleForeignKey", + "model": "sentry.AlertRule" + }, + "rule": { + "kind": "FlexibleForeignKey", + "model": "sentry.Rule" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.SavedSearch": { + "model": "sentry.SavedSearch", + "relations": { + "organization": { + "kind": "FlexibleForeignKey", + "model": "sentry.Organization" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.ScheduledDeletion": { + "model": "sentry.ScheduledDeletion", + "relations": {}, + "silos": [ + "CONTROL" + ] + }, + "sentry.SentryApp": { + "model": "sentry.SentryApp", + "relations": { + "application": { + "kind": "DefaultOneToOneField", + "model": "sentry.ApiApplication" + }, + "creator_user": { + "kind": "FlexibleForeignKey", + "model": "sentry.User" + }, + "proxy_user": { + "kind": "DefaultOneToOneField", + "model": "sentry.User" + } + }, + "silos": [ + "CONTROL" + ] + }, + "sentry.SentryAppAvatar": { + "model": "sentry.SentryAppAvatar", + "relations": { + "sentry_app": { + "kind": "FlexibleForeignKey", + "model": "sentry.SentryApp" + } + }, + "silos": [ + "CONTROL" + ] + }, + "sentry.SentryAppComponent": { + "model": "sentry.SentryAppComponent", + "relations": { + "sentry_app": { + "kind": "FlexibleForeignKey", + "model": "sentry.SentryApp" + } + }, + "silos": [ + "CONTROL" + ] + }, + "sentry.SentryAppInstallation": { + "model": "sentry.SentryAppInstallation", + "relations": { + "api_grant": { + "kind": "DefaultOneToOneField", + "model": "sentry.ApiGrant" + }, + "api_token": { + "kind": "DefaultOneToOneField", + "model": "sentry.ApiToken" + }, + "sentry_app": { + "kind": "FlexibleForeignKey", + "model": "sentry.SentryApp" + } + }, + "silos": [ + "CONTROL" + ] + }, + "sentry.SentryAppInstallationForProvider": { + "model": "sentry.SentryAppInstallationForProvider", + "relations": { + "sentry_app_installation": { + "kind": "FlexibleForeignKey", + "model": "sentry.SentryAppInstallation" + } + }, + "silos": [ + "CONTROL" + ] + }, + "sentry.SentryAppInstallationToken": { + "model": "sentry.SentryAppInstallationToken", + "relations": { + "api_token": { + "kind": "FlexibleForeignKey", + "model": "sentry.ApiToken" + }, + "sentry_app_installation": { + "kind": "FlexibleForeignKey", + "model": "sentry.SentryAppInstallation" + } + }, + "silos": [ + "CONTROL" + ] + }, + "sentry.SentryFunction": { + "model": "sentry.SentryFunction", + "relations": { + "organization": { + "kind": "FlexibleForeignKey", + "model": "sentry.Organization" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.ServiceHook": { + "model": "sentry.ServiceHook", + "relations": {}, + "silos": [ + "REGION" + ] + }, + "sentry.ServiceHookProject": { + "model": "sentry.ServiceHookProject", + "relations": { + "service_hook": { + "kind": "FlexibleForeignKey", + "model": "sentry.ServiceHook" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.SnubaQuery": { + "model": "sentry.SnubaQuery", + "relations": { + "environment": { + "kind": "FlexibleForeignKey", + "model": "sentry.Environment" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.SnubaQueryEventType": { + "model": "sentry.SnubaQueryEventType", + "relations": { + "snuba_query": { + "kind": "FlexibleForeignKey", + "model": "sentry.SnubaQuery" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.StringIndexer": { + "model": "sentry.StringIndexer", + "relations": {}, + "silos": [ + "REGION" + ] + }, + "sentry.Team": { + "model": "sentry.Team", + "relations": { + "actor": { + "kind": "FlexibleForeignKey", + "model": "sentry.Actor" + }, + "organization": { + "kind": "FlexibleForeignKey", + "model": "sentry.Organization" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.TeamAvatar": { + "model": "sentry.TeamAvatar", + "relations": { + "team": { + "kind": "FlexibleForeignKey", + "model": "sentry.Team" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.TeamKeyTransaction": { + "model": "sentry.TeamKeyTransaction", + "relations": { + "organization": { + "kind": "FlexibleForeignKey", + "model": "sentry.Organization" + }, + "project_team": { + "kind": "FlexibleForeignKey", + "model": "sentry.ProjectTeam" + } + }, + "silos": [ + "REGION" + ] + }, + "sentry.TimeSeriesSnapshot": { + "model": "sentry.TimeSeriesSnapshot", + "relations": {}, + "silos": [ + "REGION" + ] + }, + "sentry.User": { + "model": "sentry.User", + "relations": {}, + "silos": [ + "CONTROL" + ] + }, + "sentry.UserAvatar": { + "model": "sentry.UserAvatar", + "relations": { + "user": { + "kind": "FlexibleForeignKey", + "model": "sentry.User" + } + }, + "silos": [ + "CONTROL" + ] + }, + "sentry.UserEmail": { + "model": "sentry.UserEmail", + "relations": { + "user": { + "kind": "FlexibleForeignKey", + "model": "sentry.User" + } + }, + "silos": [ + "CONTROL" + ] + }, + "sentry.UserIP": { + "model": "sentry.UserIP", + "relations": { + "user": { + "kind": "FlexibleForeignKey", + "model": "sentry.User" + } + }, + "silos": [ + "CONTROL" + ] + }, + "sentry.UserOption": { + "model": "sentry.UserOption", + "relations": { + "user": { + "kind": "FlexibleForeignKey", + "model": "sentry.User" + } + }, + "silos": [ + "CONTROL" + ] + }, + "sentry.UserPermission": { + "model": "sentry.UserPermission", + "relations": { + "user": { + "kind": "FlexibleForeignKey", + "model": "sentry.User" + } + }, + "silos": [ + "CONTROL" + ] + }, + "sentry.UserReport": { + "model": "sentry.UserReport", + "relations": {}, + "silos": [ + "REGION" + ] + }, + "sentry.UserRole": { + "model": "sentry.UserRole", + "relations": { + "users": { + "kind": "ManyToManyField", + "model": "sentry.User" + } + }, + "silos": [ + "CONTROL" + ] + }, + "sentry.UserRoleUser": { + "model": "sentry.UserRoleUser", + "relations": { + "role": { + "kind": "FlexibleForeignKey", + "model": "sentry.UserRole" + }, + "user": { + "kind": "FlexibleForeignKey", + "model": "sentry.User" + } + }, + "silos": [ + "CONTROL" + ] + }, + "sessions.Session": { + "model": "sessions.Session", + "relations": {}, + "silos": [ + "MONOLITH" + ] + }, + "sites.Site": { + "model": "sites.Site", + "relations": {}, + "silos": [ + "MONOLITH" + ] + }, + "social_auth.UserSocialAuth": { + "model": "social_auth.UserSocialAuth", + "relations": { + "user": { + "kind": "DefaultForeignKey", + "model": "sentry.User" + } + }, + "silos": [ + "CONTROL" + ] + } +} \ No newline at end of file diff --git a/fixtures/backup/model_dependencies/flat.json b/fixtures/backup/model_dependencies/flat.json new file mode 100644 index 00000000000000..5d7e1418203d87 --- /dev/null +++ b/fixtures/backup/model_dependencies/flat.json @@ -0,0 +1,603 @@ +{ + "nodestore.Node": [], + "replays.ReplayRecordingSegment": [], + "sentry.Activity": [ + "sentry.Group", + "sentry.Project" + ], + "sentry.Actor": [], + "sentry.AlertRule": [ + "sentry.Actor", + "sentry.Organization", + "sentry.Project", + "sentry.SnubaQuery" + ], + "sentry.AlertRuleActivity": [ + "sentry.AlertRule" + ], + "sentry.AlertRuleExcludedProjects": [ + "sentry.AlertRule", + "sentry.Project" + ], + "sentry.AlertRuleTrigger": [ + "sentry.AlertRule", + "sentry.Incident" + ], + "sentry.AlertRuleTriggerAction": [ + "sentry.AlertRuleTrigger" + ], + "sentry.AlertRuleTriggerExclusion": [ + "sentry.AlertRuleTrigger", + "sentry.QuerySubscription" + ], + "sentry.ApiApplication": [ + "sentry.User" + ], + "sentry.ApiAuthorization": [ + "sentry.ApiApplication", + "sentry.User" + ], + "sentry.ApiGrant": [ + "sentry.ApiApplication", + "sentry.User" + ], + "sentry.ApiKey": [], + "sentry.ApiToken": [ + "sentry.ApiApplication", + "sentry.User" + ], + "sentry.AppConnectBuild": [ + "sentry.Project" + ], + "sentry.ArtifactBundle": [ + "sentry.File" + ], + "sentry.ArtifactBundleFlatFileIndex": [ + "sentry.File" + ], + "sentry.ArtifactBundleIndex": [ + "sentry.ArtifactBundle" + ], + "sentry.AssistantActivity": [ + "sentry.User" + ], + "sentry.AuditLogEntry": [ + "sentry.ApiKey", + "sentry.User" + ], + "sentry.AuthIdentity": [ + "sentry.AuthProvider", + "sentry.User" + ], + "sentry.AuthProvider": [], + "sentry.AuthProviderDefaultTeams": [], + "sentry.Authenticator": [ + "sentry.User" + ], + "sentry.Broadcast": [], + "sentry.BroadcastSeen": [ + "sentry.Broadcast", + "sentry.User" + ], + "sentry.Commit": [ + "sentry.CommitAuthor" + ], + "sentry.CommitAuthor": [], + "sentry.CommitFileChange": [ + "sentry.Commit" + ], + "sentry.ControlFile": [ + "sentry.ControlFileBlob" + ], + "sentry.ControlFileBlob": [], + "sentry.ControlFileBlobIndex": [ + "sentry.ControlFile", + "sentry.ControlFileBlob" + ], + "sentry.ControlFileBlobOwner": [ + "sentry.ControlFileBlob" + ], + "sentry.ControlOption": [], + "sentry.ControlOutbox": [], + "sentry.ControlTombstone": [], + "sentry.Counter": [ + "sentry.Project" + ], + "sentry.Dashboard": [ + "sentry.Organization", + "sentry.Project" + ], + "sentry.DashboardProject": [ + "sentry.Dashboard", + "sentry.Project" + ], + "sentry.DashboardTombstone": [ + "sentry.Organization" + ], + "sentry.DashboardWidget": [ + "sentry.Dashboard" + ], + "sentry.DashboardWidgetQuery": [ + "sentry.DashboardWidget" + ], + "sentry.DebugIdArtifactBundle": [ + "sentry.ArtifactBundle" + ], + "sentry.DeletedOrganization": [], + "sentry.DeletedProject": [], + "sentry.DeletedTeam": [], + "sentry.Deploy": [ + "sentry.Release" + ], + "sentry.DiscoverSavedQuery": [ + "sentry.Organization", + "sentry.Project" + ], + "sentry.DiscoverSavedQueryProject": [ + "sentry.DiscoverSavedQuery", + "sentry.Project" + ], + "sentry.Distribution": [ + "sentry.Release" + ], + "sentry.DocIntegration": [], + "sentry.DocIntegrationAvatar": [ + "sentry.DocIntegration" + ], + "sentry.Email": [], + "sentry.Environment": [ + "sentry.Project" + ], + "sentry.EnvironmentProject": [ + "sentry.Environment", + "sentry.Project" + ], + "sentry.EventAttachment": [], + "sentry.EventProcessingIssue": [ + "sentry.ProcessingIssue", + "sentry.RawEvent" + ], + "sentry.EventUser": [], + "sentry.ExportedData": [ + "sentry.Organization" + ], + "sentry.ExportedDataBlob": [ + "sentry.ExportedData" + ], + "sentry.ExternalActor": [ + "sentry.Actor", + "sentry.Organization" + ], + "sentry.ExternalIssue": [ + "sentry.Organization" + ], + "sentry.FeatureAdoption": [ + "sentry.Organization" + ], + "sentry.File": [ + "sentry.FileBlob" + ], + "sentry.FileBlob": [], + "sentry.FileBlobIndex": [ + "sentry.File", + "sentry.FileBlob" + ], + "sentry.FileBlobOwner": [ + "sentry.FileBlob" + ], + "sentry.FlatFileIndexState": [ + "sentry.ArtifactBundle", + "sentry.ArtifactBundleFlatFileIndex" + ], + "sentry.Group": [ + "sentry.Project", + "sentry.Release" + ], + "sentry.GroupAssignee": [ + "sentry.Group", + "sentry.Project", + "sentry.Team" + ], + "sentry.GroupBookmark": [ + "sentry.Group", + "sentry.Project" + ], + "sentry.GroupCommitResolution": [], + "sentry.GroupEmailThread": [ + "sentry.Group", + "sentry.Project" + ], + "sentry.GroupEnvironment": [ + "sentry.Environment", + "sentry.Group", + "sentry.Release" + ], + "sentry.GroupHash": [ + "sentry.Group", + "sentry.Project" + ], + "sentry.GroupHistory": [ + "sentry.Actor", + "sentry.Group", + "sentry.Organization", + "sentry.Project", + "sentry.Release" + ], + "sentry.GroupInbox": [ + "sentry.Group", + "sentry.Organization", + "sentry.Project" + ], + "sentry.GroupLink": [ + "sentry.Group", + "sentry.Project" + ], + "sentry.GroupMeta": [ + "sentry.Group" + ], + "sentry.GroupOwner": [ + "sentry.Group", + "sentry.Organization", + "sentry.Project", + "sentry.Team" + ], + "sentry.GroupRedirect": [], + "sentry.GroupRelease": [], + "sentry.GroupResolution": [ + "sentry.Group", + "sentry.Release" + ], + "sentry.GroupRuleStatus": [ + "sentry.Group", + "sentry.Project", + "sentry.Rule" + ], + "sentry.GroupSeen": [ + "sentry.Group", + "sentry.Project" + ], + "sentry.GroupShare": [ + "sentry.Group", + "sentry.Project" + ], + "sentry.GroupSnooze": [ + "sentry.Group" + ], + "sentry.GroupSubscription": [ + "sentry.Group", + "sentry.Project" + ], + "sentry.GroupTombstone": [ + "sentry.Project" + ], + "sentry.Identity": [ + "sentry.IdentityProvider", + "sentry.User" + ], + "sentry.IdentityProvider": [], + "sentry.Incident": [ + "sentry.AlertRule", + "sentry.Organization", + "sentry.Project" + ], + "sentry.IncidentActivity": [ + "sentry.Incident" + ], + "sentry.IncidentProject": [ + "sentry.Incident", + "sentry.Project" + ], + "sentry.IncidentSeen": [ + "sentry.Incident" + ], + "sentry.IncidentSnapshot": [ + "sentry.Incident", + "sentry.TimeSeriesSnapshot" + ], + "sentry.IncidentSubscription": [ + "sentry.Incident" + ], + "sentry.IncidentTrigger": [ + "sentry.AlertRuleTrigger", + "sentry.Incident" + ], + "sentry.Integration": [], + "sentry.IntegrationExternalProject": [], + "sentry.IntegrationFeature": [], + "sentry.LatestAppConnectBuildsCheck": [ + "sentry.Project" + ], + "sentry.LatestRepoReleaseEnvironment": [], + "sentry.LostPasswordHash": [ + "sentry.User" + ], + "sentry.MetricsKeyIndexer": [], + "sentry.Monitor": [], + "sentry.MonitorCheckIn": [ + "sentry.Monitor", + "sentry.MonitorEnvironment", + "sentry.MonitorLocation" + ], + "sentry.MonitorEnvironment": [ + "sentry.Environment", + "sentry.Monitor" + ], + "sentry.MonitorLocation": [], + "sentry.NotificationAction": [ + "sentry.Organization", + "sentry.Project" + ], + "sentry.NotificationActionProject": [ + "sentry.NotificationAction", + "sentry.Project" + ], + "sentry.NotificationSetting": [ + "sentry.User" + ], + "sentry.Option": [], + "sentry.OrgAuthToken": [ + "sentry.User" + ], + "sentry.Organization": [], + "sentry.OrganizationAccessRequest": [ + "sentry.OrganizationMember", + "sentry.Team" + ], + "sentry.OrganizationAvatar": [ + "sentry.Organization" + ], + "sentry.OrganizationIntegration": [ + "sentry.Integration" + ], + "sentry.OrganizationMapping": [], + "sentry.OrganizationMember": [ + "sentry.Organization", + "sentry.Team" + ], + "sentry.OrganizationMemberMapping": [ + "sentry.User" + ], + "sentry.OrganizationMemberTeam": [ + "sentry.OrganizationMember", + "sentry.Team" + ], + "sentry.OrganizationOnboardingTask": [ + "sentry.Organization", + "sentry.Project" + ], + "sentry.OrganizationOption": [ + "sentry.Organization" + ], + "sentry.PagerDutyService": [ + "sentry.OrganizationIntegration" + ], + "sentry.PendingIncidentSnapshot": [ + "sentry.Incident" + ], + "sentry.PerfStringIndexer": [], + "sentry.PlatformExternalIssue": [ + "sentry.Group", + "sentry.Project" + ], + "sentry.ProcessingIssue": [ + "sentry.Project" + ], + "sentry.ProguardArtifactRelease": [ + "sentry.ProjectDebugFile" + ], + "sentry.Project": [ + "sentry.Organization", + "sentry.Team" + ], + "sentry.ProjectArtifactBundle": [ + "sentry.ArtifactBundle" + ], + "sentry.ProjectAvatar": [ + "sentry.Project" + ], + "sentry.ProjectBookmark": [ + "sentry.Project" + ], + "sentry.ProjectCodeOwners": [ + "sentry.Project", + "sentry.RepositoryProjectPathConfig" + ], + "sentry.ProjectDebugFile": [ + "sentry.File" + ], + "sentry.ProjectIntegration": [ + "sentry.Project" + ], + "sentry.ProjectKey": [ + "sentry.Project" + ], + "sentry.ProjectOption": [ + "sentry.Project" + ], + "sentry.ProjectOwnership": [ + "sentry.Project" + ], + "sentry.ProjectPlatform": [], + "sentry.ProjectRedirect": [ + "sentry.Organization", + "sentry.Project" + ], + "sentry.ProjectTeam": [ + "sentry.Project", + "sentry.Team" + ], + "sentry.ProjectTransactionThreshold": [ + "sentry.Organization", + "sentry.Project" + ], + "sentry.ProjectTransactionThresholdOverride": [ + "sentry.Organization", + "sentry.Project" + ], + "sentry.PromptsActivity": [], + "sentry.PullRequest": [ + "sentry.CommitAuthor" + ], + "sentry.PullRequestComment": [ + "sentry.PullRequest" + ], + "sentry.PullRequestCommit": [ + "sentry.Commit", + "sentry.PullRequest" + ], + "sentry.QuerySubscription": [ + "sentry.Project", + "sentry.SnubaQuery" + ], + "sentry.RawEvent": [ + "sentry.Project" + ], + "sentry.RecentSearch": [ + "sentry.Organization" + ], + "sentry.RegionOutbox": [], + "sentry.RegionScheduledDeletion": [], + "sentry.RegionTombstone": [], + "sentry.Relay": [], + "sentry.RelayUsage": [], + "sentry.Release": [ + "sentry.Organization", + "sentry.Project" + ], + "sentry.ReleaseActivity": [ + "sentry.Release" + ], + "sentry.ReleaseArtifactBundle": [ + "sentry.ArtifactBundle" + ], + "sentry.ReleaseCommit": [ + "sentry.Commit", + "sentry.Release" + ], + "sentry.ReleaseEnvironment": [ + "sentry.Environment", + "sentry.Organization", + "sentry.Release" + ], + "sentry.ReleaseFile": [ + "sentry.File" + ], + "sentry.ReleaseHeadCommit": [ + "sentry.Commit", + "sentry.Release" + ], + "sentry.ReleaseProject": [ + "sentry.Project", + "sentry.Release" + ], + "sentry.ReleaseProjectEnvironment": [ + "sentry.Environment", + "sentry.Project", + "sentry.Release" + ], + "sentry.Repository": [], + "sentry.RepositoryProjectPathConfig": [ + "sentry.Project", + "sentry.Repository" + ], + "sentry.ReprocessingReport": [ + "sentry.Project" + ], + "sentry.Rule": [ + "sentry.Actor", + "sentry.Project" + ], + "sentry.RuleActivity": [ + "sentry.Rule" + ], + "sentry.RuleFireHistory": [ + "sentry.Group", + "sentry.Project", + "sentry.Rule" + ], + "sentry.RuleSnooze": [ + "sentry.AlertRule", + "sentry.Rule" + ], + "sentry.SavedSearch": [ + "sentry.Organization" + ], + "sentry.ScheduledDeletion": [], + "sentry.SentryApp": [ + "sentry.ApiApplication", + "sentry.User" + ], + "sentry.SentryAppAvatar": [ + "sentry.SentryApp" + ], + "sentry.SentryAppComponent": [ + "sentry.SentryApp" + ], + "sentry.SentryAppInstallation": [ + "sentry.ApiGrant", + "sentry.ApiToken", + "sentry.SentryApp" + ], + "sentry.SentryAppInstallationForProvider": [ + "sentry.SentryAppInstallation" + ], + "sentry.SentryAppInstallationToken": [ + "sentry.ApiToken", + "sentry.SentryAppInstallation" + ], + "sentry.SentryFunction": [ + "sentry.Organization" + ], + "sentry.ServiceHook": [], + "sentry.ServiceHookProject": [ + "sentry.ServiceHook" + ], + "sentry.SnubaQuery": [ + "sentry.Environment" + ], + "sentry.SnubaQueryEventType": [ + "sentry.SnubaQuery" + ], + "sentry.StringIndexer": [], + "sentry.Team": [ + "sentry.Actor", + "sentry.Organization" + ], + "sentry.TeamAvatar": [ + "sentry.Team" + ], + "sentry.TeamKeyTransaction": [ + "sentry.Organization", + "sentry.ProjectTeam" + ], + "sentry.TimeSeriesSnapshot": [], + "sentry.User": [], + "sentry.UserAvatar": [ + "sentry.User" + ], + "sentry.UserEmail": [ + "sentry.User" + ], + "sentry.UserIP": [ + "sentry.User" + ], + "sentry.UserOption": [ + "sentry.User" + ], + "sentry.UserPermission": [ + "sentry.User" + ], + "sentry.UserReport": [], + "sentry.UserRole": [ + "sentry.User" + ], + "sentry.UserRoleUser": [ + "sentry.User", + "sentry.UserRole" + ], + "sessions.Session": [], + "sites.Site": [], + "social_auth.UserSocialAuth": [ + "sentry.User" + ] +} \ No newline at end of file diff --git a/fixtures/backup/model_dependencies/sorted.json b/fixtures/backup/model_dependencies/sorted.json new file mode 100644 index 00000000000000..cfae1388296aca --- /dev/null +++ b/fixtures/backup/model_dependencies/sorted.json @@ -0,0 +1,206 @@ +[ + "sessions.Session", + "sites.Site", + "sentry.Option", + "sentry.ControlOption", + "sentry.Actor", + "sentry.RegionOutbox", + "sentry.ControlOutbox", + "sentry.ApiKey", + "sentry.AuthProviderDefaultTeams", + "sentry.AuthProvider", + "sentry.ControlFileBlob", + "sentry.ControlFile", + "sentry.FileBlob", + "sentry.File", + "sentry.Broadcast", + "sentry.CommitAuthor", + "sentry.ProjectDebugFile", + "sentry.ProguardArtifactRelease", + "sentry.DeletedOrganization", + "sentry.DeletedProject", + "sentry.DeletedTeam", + "sentry.Email", + "sentry.EventAttachment", + "sentry.EventUser", + "sentry.GroupCommitResolution", + "sentry.GroupRedirect", + "sentry.GroupRelease", + "sentry.Organization", + "sentry.IdentityProvider", + "sentry.DocIntegration", + "sentry.ExternalActor", + "sentry.ExternalIssue", + "sentry.Integration", + "sentry.IntegrationExternalProject", + "sentry.IntegrationFeature", + "sentry.LatestRepoReleaseEnvironment", + "sentry.User", + "sentry.NotificationSetting", + "sentry.OrganizationMapping", + "sentry.OrganizationMemberMapping", + "sentry.OrgAuthToken", + "sentry.ProjectPlatform", + "sentry.PromptsActivity", + "sentry.PullRequest", + "sentry.PullRequestComment", + "sentry.RecentSearch", + "sentry.RelayUsage", + "sentry.Relay", + "sentry.ReleaseFile", + "sentry.Repository", + "sentry.SavedSearch", + "sentry.ScheduledDeletion", + "sentry.RegionScheduledDeletion", + "sentry.SentryFunction", + "sentry.ServiceHook", + "sentry.RegionTombstone", + "sentry.ControlTombstone", + "sentry.UserEmail", + "sentry.UserIP", + "sentry.UserPermission", + "sentry.UserReport", + "sentry.UserRole", + "sentry.UserRoleUser", + "sentry.TimeSeriesSnapshot", + "sentry.Monitor", + "sentry.MonitorLocation", + "sentry.MetricsKeyIndexer", + "sentry.StringIndexer", + "sentry.PerfStringIndexer", + "sentry.ExportedData", + "sentry.ExportedDataBlob", + "nodestore.Node", + "replays.ReplayRecordingSegment", + "social_auth.UserSocialAuth", + "sentry.ServiceHookProject", + "sentry.LostPasswordHash", + "sentry.OrganizationIntegration", + "sentry.Identity", + "sentry.Team", + "sentry.FeatureAdoption", + "sentry.DashboardTombstone", + "sentry.Commit", + "sentry.BroadcastSeen", + "sentry.UserAvatar", + "sentry.TeamAvatar", + "sentry.OrganizationAvatar", + "sentry.DocIntegrationAvatar", + "sentry.FileBlobIndex", + "sentry.FileBlobOwner", + "sentry.ControlFileBlobIndex", + "sentry.ControlFileBlobOwner", + "sentry.AuthIdentity", + "sentry.Authenticator", + "sentry.AuditLogEntry", + "sentry.AssistantActivity", + "sentry.ArtifactBundleFlatFileIndex", + "sentry.ArtifactBundle", + "sentry.ApiApplication", + "sentry.UserOption", + "sentry.OrganizationOption", + "sentry.ApiAuthorization", + "sentry.ApiGrant", + "sentry.ApiToken", + "sentry.FlatFileIndexState", + "sentry.ArtifactBundleIndex", + "sentry.ReleaseArtifactBundle", + "sentry.DebugIdArtifactBundle", + "sentry.ProjectArtifactBundle", + "sentry.CommitFileChange", + "sentry.OrganizationMember", + "sentry.SentryApp", + "sentry.PagerDutyService", + "sentry.SentryAppComponent", + "sentry.SentryAppInstallation", + "sentry.SentryAppInstallationForProvider", + "sentry.SentryAppInstallationToken", + "sentry.OrganizationAccessRequest", + "sentry.Project", + "sentry.ProjectBookmark", + "sentry.ProjectKey", + "sentry.ProjectOwnership", + "sentry.ProjectRedirect", + "sentry.PullRequestCommit", + "sentry.RawEvent", + "sentry.ReprocessingReport", + "sentry.Rule", + "sentry.RuleActivity", + "sentry.ProjectTransactionThresholdOverride", + "sentry.ProjectTransactionThreshold", + "sentry.NotificationAction", + "sentry.DiscoverSavedQuery", + "sentry.DiscoverSavedQueryProject", + "sentry.NotificationActionProject", + "sentry.ProjectTeam", + "sentry.ProcessingIssue", + "sentry.OrganizationOnboardingTask", + "sentry.LatestAppConnectBuildsCheck", + "sentry.RepositoryProjectPathConfig", + "sentry.ProjectIntegration", + "sentry.GroupTombstone", + "sentry.Release", + "sentry.ReleaseProject", + "sentry.OrganizationMemberTeam", + "sentry.Group", + "sentry.GroupHistory", + "sentry.Environment", + "sentry.EnvironmentProject", + "sentry.Distribution", + "sentry.Deploy", + "sentry.Dashboard", + "sentry.DashboardProject", + "sentry.Counter", + "sentry.SentryAppAvatar", + "sentry.ProjectAvatar", + "sentry.AppConnectBuild", + "sentry.ProjectOption", + "sentry.Activity", + "sentry.DashboardWidget", + "sentry.GroupOwner", + "sentry.GroupAssignee", + "sentry.GroupBookmark", + "sentry.GroupEmailThread", + "sentry.GroupEnvironment", + "sentry.GroupHash", + "sentry.GroupInbox", + "sentry.GroupLink", + "sentry.GroupMeta", + "sentry.GroupResolution", + "sentry.GroupRuleStatus", + "sentry.GroupSeen", + "sentry.GroupShare", + "sentry.GroupSnooze", + "sentry.GroupSubscription", + "sentry.PlatformExternalIssue", + "sentry.EventProcessingIssue", + "sentry.SnubaQuery", + "sentry.SnubaQueryEventType", + "sentry.QuerySubscription", + "sentry.ProjectCodeOwners", + "sentry.ReleaseActivity", + "sentry.ReleaseCommit", + "sentry.ReleaseEnvironment", + "sentry.ReleaseHeadCommit", + "sentry.ReleaseProjectEnvironment", + "sentry.RuleFireHistory", + "sentry.AlertRule", + "sentry.AlertRuleActivity", + "sentry.TeamKeyTransaction", + "sentry.MonitorEnvironment", + "sentry.MonitorCheckIn", + "sentry.AlertRuleExcludedProjects", + "sentry.Incident", + "sentry.IncidentSeen", + "sentry.IncidentProject", + "sentry.RuleSnooze", + "sentry.DashboardWidgetQuery", + "sentry.PendingIncidentSnapshot", + "sentry.IncidentSnapshot", + "sentry.IncidentActivity", + "sentry.IncidentSubscription", + "sentry.AlertRuleTrigger", + "sentry.AlertRuleTriggerExclusion", + "sentry.AlertRuleTriggerAction", + "sentry.IncidentTrigger" +] \ No newline at end of file diff --git a/src/sentry/backup/dependencies.py b/src/sentry/backup/dependencies.py index 50bfb30d27e889..d9c2064ae22c37 100644 --- a/src/sentry/backup/dependencies.py +++ b/src/sentry/backup/dependencies.py @@ -1,24 +1,94 @@ from __future__ import annotations -from django.db.models.fields.related import ManyToManyField +from enum import Enum, auto, unique +from typing import NamedTuple + +from django.db import models +from django.db.models.fields.related import ForeignKey, ManyToManyField, OneToOneField from sentry.backup.helpers import EXCLUDED_APPS +from sentry.silo import SiloMode +from sentry.utils import json + + +@unique +class ForeignFieldKind(Enum): + """Kinds of foreign fields that we care about.""" + + # Uses our `FlexibleForeignKey` wrapper. + FlexibleForeignKey = auto() + + # Uses our `HybridCloudForeignKey` wrapper. + HybridCloudForeignKey = auto() + + # Uses our `OneToOneCascadeDeletes` wrapper. + OneToOneCascadeDeletes = auto() + + # A naked usage of Django's `ManyToManyField`. + ManyToManyField = auto() + + # A naked usage of Django's `ForeignKey`. + DefaultForeignKey = auto() + + # A naked usage of Django's `OneToOneField`. + DefaultOneToOneField = auto() + + +class ForeignField(NamedTuple): + """A field that creates a dependency on another Sentry model.""" + + model: models.base.ModelBase + kind: ForeignFieldKind + + +class ModelRelations(NamedTuple): + """What other models does this model depend on, and how?""" + + model: models.base.ModelBase + relations: dict[str, ForeignField] + silos: list[SiloMode] + + def flatten(self) -> set[models.base.ModelBase]: + """Returns a flat list of all related models, omitting the kind of relation they have.""" + + return {ff.model for ff in self.relations.values()} -def sort_dependencies(): - """ - Similar to Django's except that we discard the important of natural keys - when sorting dependencies (i.e. it works without them). - """ +def normalize_model_name(model): + return f"{model._meta.app_label}.{model._meta.object_name}" + + +class DependenciesJSONEncoder(json.JSONEncoder): + """JSON serializer that outputs a detailed serialization of all models included in a + `ModelRelations`.""" + + def default(self, obj): + if isinstance(obj, models.base.ModelBase): + return normalize_model_name(obj) + if isinstance(obj, ForeignFieldKind): + return obj.name + if isinstance(obj, SiloMode): + return obj.name + if isinstance(obj, set): + return sorted(list(obj), key=lambda obj: normalize_model_name(obj)) + return super().default(obj) + + +def dependencies() -> dict[str, ModelRelations]: + """Produce a dictionary mapping model type definitions to a `ModelDeps` describing their dependencies.""" + from django.apps import apps + from sentry.db.models.base import ModelSiloLimit + from sentry.db.models.fields.foreignkey import FlexibleForeignKey + from sentry.db.models.fields.hybrid_cloud_foreign_key import HybridCloudForeignKey + from sentry.db.models.fields.onetoone import OneToOneCascadeDeletes from sentry.models.actor import Actor from sentry.models.team import Team from sentry.models.user import User # Process the list of models, and get the list of dependencies - model_dependencies = [] - models = set() + model_dependencies_list: dict[str, ModelRelations] = {} for app_config in apps.get_app_configs(): if app_config.label in EXCLUDED_APPS: continue @@ -26,17 +96,9 @@ def sort_dependencies(): model_iterator = app_config.get_models() for model in model_iterator: - models.add(model) - # Add any explicitly defined dependencies - if hasattr(model, "natural_key"): - deps = getattr(model.natural_key, "dependencies", []) - if deps: - deps = [apps.get_model(*d.split(".")) for d in deps] - else: - deps = [] + relations: dict[str, ForeignField] = dict() - # Now add a dependency for any FK relation with a model that - # defines a natural key + # Now add a dependency for any FK relation with a model that defines a natural key. for field in model._meta.fields: rel_model = getattr(field.remote_field, "model", None) if rel_model is not None and rel_model != model: @@ -46,22 +108,74 @@ def sort_dependencies(): if model == Actor and (rel_model == Team or rel_model == User): continue - deps.append(rel_model) + if isinstance(field, FlexibleForeignKey): + relations[field.name] = ForeignField( + model=rel_model, + kind=ForeignFieldKind.FlexibleForeignKey, + ) + elif isinstance(field, HybridCloudForeignKey): + relations[field.name] = ForeignField( + model=rel_model, + kind=ForeignFieldKind.HybridCloudForeignKey, + ) + elif isinstance(field, ForeignKey): + relations[field.name] = ForeignField( + model=rel_model, + kind=ForeignFieldKind.DefaultForeignKey, + ) - # Also add a dependency for any simple M2M relation with a model - # that defines a natural key. M2M relations with explicit through - # models don't count as dependencies. + # Also add a dependency for any simple M2M relation. many_to_many_fields = [ field for field in model._meta.get_fields() if isinstance(field, ManyToManyField) ] for field in many_to_many_fields: rel_model = getattr(field.remote_field, "model", None) if rel_model is not None and rel_model != model: - deps.append(rel_model) + relations[field.name] = ForeignField( + model=rel_model, + kind=ForeignFieldKind.ManyToManyField, + ) + + # Finally, get all simple O2O relations as well. + one_to_one_fields = [ + field for field in model._meta.get_fields() if isinstance(field, OneToOneField) + ] + for field in one_to_one_fields: + rel_model = getattr(field.remote_field, "model", None) + if rel_model is not None and rel_model != model: + if isinstance(field, OneToOneCascadeDeletes): + relations[field.name] = ForeignField( + model=rel_model, + kind=ForeignFieldKind.OneToOneCascadeDeletes, + ) + elif isinstance(field, OneToOneField): + relations[field.name] = ForeignField( + model=rel_model, + kind=ForeignFieldKind.DefaultOneToOneField, + ) + else: + raise RuntimeError("Unknown one to kind") + + model_dependencies_list[normalize_model_name(model)] = ModelRelations( + model=model, + relations=relations, + silos=list( + getattr(model._meta, "silo_limit", ModelSiloLimit(SiloMode.MONOLITH)).modes + ), + ) + return model_dependencies_list + + +def sorted_dependencies(): + """Produce a list of model definitions such that, for every item in the list, all of the other models it mentions in its fields and/or natural key (ie, its "dependencies") have already appeared in the list. + + Similar to Django's algorithm except that we discard the importance of natural keys + when sorting dependencies (ie, it works without them).""" - model_dependencies.append((model, deps)) + model_dependencies_list = list(dependencies().values()) + model_dependencies_list.reverse() + model_set = {md.model for md in model_dependencies_list} - model_dependencies.reverse() # Now sort the models to ensure that dependencies are met. This # is done by repeatedly iterating over the input list of models. # If all the dependencies of a given model are in the final list, @@ -71,32 +185,34 @@ def sort_dependencies(): # If we do a full iteration without a promotion, that means there are # circular dependencies in the list. model_list = [] - while model_dependencies: + while model_dependencies_list: skipped = [] changed = False - while model_dependencies: - model, deps = model_dependencies.pop() + while model_dependencies_list: + model_deps = model_dependencies_list.pop() + deps = model_deps.flatten() + model = model_deps.model # If all of the models in the dependency list are either already # on the final model list, or not on the original serialization list, # then we've found another model with all it's dependencies satisfied. found = True - for candidate in ((d not in models or d in model_list) for d in deps): + for candidate in ((d not in model_set or d in model_list) for d in deps): if not candidate: found = False if found: model_list.append(model) changed = True else: - skipped.append((model, deps)) + skipped.append(model_deps) if not changed: raise RuntimeError( "Can't resolve dependencies for %s in serialized app list." % ", ".join( - f"{model._meta.app_label}.{model._meta.object_name}" - for model, deps in sorted(skipped, key=lambda obj: obj[0].__name__) + normalize_model_name(m.model) + for m in sorted(skipped, key=lambda obj: normalize_model_name(obj)) ) ) - model_dependencies = skipped + model_dependencies_list = skipped return model_list diff --git a/src/sentry/backup/exports.py b/src/sentry/backup/exports.py index 05d0d8f7456a66..acb4f103ef3432 100644 --- a/src/sentry/backup/exports.py +++ b/src/sentry/backup/exports.py @@ -7,7 +7,7 @@ from django.core.serializers import serialize from django.core.serializers.json import DjangoJSONEncoder -from sentry.backup.dependencies import sort_dependencies +from sentry.backup.dependencies import sorted_dependencies UTC_0 = timezone(timedelta(hours=0)) @@ -41,7 +41,7 @@ def exports(dest, old_config: OldExportConfig, indent: int, printer=click.echo): def yield_objects(): # Collate the objects to be serialized. - for model in sort_dependencies(): + for model in sorted_dependencies(): if ( not getattr(model, "__include_in_export__", old_config.include_non_sentry_models) or model.__name__.lower() in old_config.excluded_models diff --git a/src/sentry/backup/helpers.py b/src/sentry/backup/helpers.py index 390ad86859b709..9a2321f745af72 100644 --- a/src/sentry/backup/helpers.py +++ b/src/sentry/backup/helpers.py @@ -4,7 +4,7 @@ from typing import Type # Django apps we take care to never import or export from. -EXCLUDED_APPS = frozenset(("auth", "contenttypes")) +EXCLUDED_APPS = frozenset(("auth", "contenttypes", "fixtures")) def get_final_derivations_of(model: Type) -> set[Type]: diff --git a/tests/sentry/backup/__init__.py b/tests/sentry/backup/__init__.py index fd5a18841cb08c..6a8f61ba210c61 100644 --- a/tests/sentry/backup/__init__.py +++ b/tests/sentry/backup/__init__.py @@ -68,8 +68,6 @@ def wrapped(*args, **kwargs): if isinstance(f, models.ManyToManyField): continue - # TODO(getsentry/team-ospo#156): Maybe make these checks recursive for models - # that have POPOs for some of their field values? if field_name not in data: mistakes.append(f"Must include field: `{field_name}`") continue diff --git a/tests/sentry/backup/test_dependencies.py b/tests/sentry/backup/test_dependencies.py new file mode 100644 index 00000000000000..1aaf1f69aae250 --- /dev/null +++ b/tests/sentry/backup/test_dependencies.py @@ -0,0 +1,55 @@ +from difflib import unified_diff + +from sentry.backup.dependencies import DependenciesJSONEncoder, dependencies, sorted_dependencies +from sentry.testutils.factories import get_fixture_path + +encoder = DependenciesJSONEncoder( + sort_keys=True, + ensure_ascii=True, + check_circular=True, + allow_nan=True, + indent=2, + encoding="utf-8", +) + + +def test_detailed(): + fixture_path = get_fixture_path("backup", "model_dependencies", "detailed.json") + with open(fixture_path) as fixture: + expect = fixture.read().splitlines() + + actual = encoder.encode(dependencies()).splitlines() + diff = list(unified_diff(expect, actual, n=3)) + if diff: + raise AssertionError( + "Model dependency graph does not match fixture. If you are seeing this in CI, please run `bin/generate-model-dependency-fixtures` and re-upload:\n\n" + + "\n".join(diff) + ) + + +def test_flat(): + fixture_path = get_fixture_path("backup", "model_dependencies", "flat.json") + with open(fixture_path) as fixture: + expect = fixture.read().splitlines() + + actual = encoder.encode({k: v.flatten() for k, v in dependencies().items()}).splitlines() + diff = list(unified_diff(expect, actual, n=3)) + if diff: + raise AssertionError( + "Model dependency graph does not match fixture. If you are seeing this in CI, please run `bin/generate-model-dependency-fixtures` and re-upload:\n\n" + + "\n".join(diff) + ) + + +def test_sorted(): + fixture_path = get_fixture_path("backup", "model_dependencies", "sorted.json") + with open(fixture_path) as fixture: + expect = fixture.read().splitlines() + + actual = encoder.encode(sorted_dependencies()).splitlines() + diff = list(unified_diff(expect, actual, n=3)) + if diff: + raise AssertionError( + "Model dependency list does not match fixture. If you are seeing this in CI, please run `bin/generate-model-dependency-fixtures` and re-upload:\n\n" + + "\n".join(diff) + )