From e4a6684f7c353f9be930e718528154558b6c3c4a Mon Sep 17 00:00:00 2001 From: Ajay Singh Date: Fri, 21 Nov 2025 10:45:57 -0800 Subject: [PATCH 1/5] organizationContributors table --- .../1008_add_organization_contributors.py | 74 +++++++++++++++++++ src/sentry/models/organizationcontributors.py | 42 +++++++++++ 2 files changed, 116 insertions(+) create mode 100644 src/sentry/migrations/1008_add_organization_contributors.py create mode 100644 src/sentry/models/organizationcontributors.py diff --git a/src/sentry/migrations/1008_add_organization_contributors.py b/src/sentry/migrations/1008_add_organization_contributors.py new file mode 100644 index 00000000000000..b974bc444b14df --- /dev/null +++ b/src/sentry/migrations/1008_add_organization_contributors.py @@ -0,0 +1,74 @@ +# Generated by Django 5.2.8 on 2025-11-21 00:00 + +import django.db.models.deletion +from django.db import migrations, models + +import sentry.db.models.fields.bounded +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", "1007_cleanup_failed_safe_deletes"), + ] + + operations = [ + migrations.CreateModel( + name="OrganizationContributors", + fields=[ + ( + "id", + sentry.db.models.fields.bounded.BoundedBigAutoField( + primary_key=True, serialize=False + ), + ), + ("external_identifier", models.CharField(db_index=True, max_length=255)), + ("alias", models.CharField(blank=True, max_length=255, null=True)), + ("num_actions", sentry.db.models.fields.bounded.BoundedIntegerField(default=0)), + ("date_added", models.DateTimeField(auto_now_add=True)), + ("date_updated", models.DateTimeField(auto_now=True)), + ( + "organization", + sentry.db.models.fields.foreignkey.FlexibleForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="sentry.organization", + ), + ), + ( + "integration_id", + sentry.db.models.fields.hybrid_cloud_foreign_key.HybridCloudForeignKey( + "sentry.Integration", + on_delete="CASCADE", + ), + ), + ], + options={ + "db_table": "sentry_organizationcontributors", + "constraints": [ + models.UniqueConstraint( + fields=("organization_id", "integration_id", "external_identifier"), + name="sentry_organizationcontributors_unique_org_contributor", + ) + ], + "indexes": [ + models.Index(fields=("date_updated",)), + ], + }, + ), + ] diff --git a/src/sentry/models/organizationcontributors.py b/src/sentry/models/organizationcontributors.py new file mode 100644 index 00000000000000..71e90b47598e89 --- /dev/null +++ b/src/sentry/models/organizationcontributors.py @@ -0,0 +1,42 @@ +from __future__ import annotations + +from django.db import models + +from sentry.backup.scopes import RelocationScope +from sentry.db.models import BoundedIntegerField, FlexibleForeignKey, Model, region_silo_model +from sentry.db.models.fields.hybrid_cloud_foreign_key import HybridCloudForeignKey + + +@region_silo_model +class OrganizationContributors(Model): + """ + Tracks external contributors and their activity for an organization. + This model stores information about contributors associated with an + integration for a specific organization, including their external identity + and how many actions they have taken. + """ + + __relocation_scope__ = RelocationScope.Excluded + + organization = FlexibleForeignKey("sentry.Organization", on_delete=models.CASCADE) + + integration_id = HybridCloudForeignKey("sentry.Integration", on_delete="CASCADE") + + external_identifier = models.CharField(max_length=255) + alias = models.CharField(max_length=255, null=True, blank=True) + num_actions = BoundedIntegerField(default=0) + date_added = models.DateTimeField(auto_now_add=True) + date_updated = models.DateTimeField(auto_now=True) + + class Meta: + app_label = "sentry" + db_table = "sentry_organizationcontributors" + constraints = [ + models.UniqueConstraint( + fields=["organization_id", "integration_id", "external_identifier"], + name="sentry_organizationcontributors_unique_org_contributor", + ), + ] + indexes = [ + models.Index(fields=["date_updated"]), + ] From c06e5bc3e48b6cea200e5a30d146ceb45e94bb99 Mon Sep 17 00:00:00 2001 From: Ajay Singh Date: Fri, 21 Nov 2025 11:02:19 -0800 Subject: [PATCH 2/5] name idx, add db_index=true --- .../migrations/1008_add_organization_contributors.py | 5 ++++- src/sentry/models/organizationcontributors.py | 7 +++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/sentry/migrations/1008_add_organization_contributors.py b/src/sentry/migrations/1008_add_organization_contributors.py index b974bc444b14df..8f1291cdcb5179 100644 --- a/src/sentry/migrations/1008_add_organization_contributors.py +++ b/src/sentry/migrations/1008_add_organization_contributors.py @@ -67,7 +67,10 @@ class Migration(CheckedMigration): ) ], "indexes": [ - models.Index(fields=("date_updated",)), + models.Index( + fields=("date_updated",), + name="sentry_organizationcontributors_date_updated_idx", + ), ], }, ), diff --git a/src/sentry/models/organizationcontributors.py b/src/sentry/models/organizationcontributors.py index 71e90b47598e89..7461a28dd2e44d 100644 --- a/src/sentry/models/organizationcontributors.py +++ b/src/sentry/models/organizationcontributors.py @@ -22,7 +22,7 @@ class OrganizationContributors(Model): integration_id = HybridCloudForeignKey("sentry.Integration", on_delete="CASCADE") - external_identifier = models.CharField(max_length=255) + external_identifier = models.CharField(max_length=255, db_index=True) alias = models.CharField(max_length=255, null=True, blank=True) num_actions = BoundedIntegerField(default=0) date_added = models.DateTimeField(auto_now_add=True) @@ -38,5 +38,8 @@ class Meta: ), ] indexes = [ - models.Index(fields=["date_updated"]), + models.Index( + fields=["date_updated"], + name="sentry_organizationcontributors_date_updated_idx", + ), ] From a66d2dbcb75eaeb8920291ff6965003bb19432df Mon Sep 17 00:00:00 2001 From: Ajay Singh Date: Fri, 21 Nov 2025 13:09:54 -0800 Subject: [PATCH 3/5] lockfile --- migrations_lockfile.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migrations_lockfile.txt b/migrations_lockfile.txt index 741de05aa2ea89..6246dfb1bd639e 100644 --- a/migrations_lockfile.txt +++ b/migrations_lockfile.txt @@ -31,7 +31,7 @@ releases: 0004_cleanup_failed_safe_deletes replays: 0006_add_bulk_delete_job -sentry: 1007_cleanup_failed_safe_deletes +sentry: 1008_add_organization_contributors social_auth: 0003_social_auth_json_field From 71b0df88e07fb04801b0c1bcafb91d9d3805c30a Mon Sep 17 00:00:00 2001 From: Ajay Singh Date: Fri, 21 Nov 2025 13:47:54 -0800 Subject: [PATCH 4/5] rename --- migrations_lockfile.txt | 2 +- .../1008_add_organization_contributors.py | 77 ------------------- ...ributors.py => organizationcontributor.py} | 0 3 files changed, 1 insertion(+), 78 deletions(-) delete mode 100644 src/sentry/migrations/1008_add_organization_contributors.py rename src/sentry/models/{organizationcontributors.py => organizationcontributor.py} (100%) diff --git a/migrations_lockfile.txt b/migrations_lockfile.txt index 6246dfb1bd639e..741de05aa2ea89 100644 --- a/migrations_lockfile.txt +++ b/migrations_lockfile.txt @@ -31,7 +31,7 @@ releases: 0004_cleanup_failed_safe_deletes replays: 0006_add_bulk_delete_job -sentry: 1008_add_organization_contributors +sentry: 1007_cleanup_failed_safe_deletes social_auth: 0003_social_auth_json_field diff --git a/src/sentry/migrations/1008_add_organization_contributors.py b/src/sentry/migrations/1008_add_organization_contributors.py deleted file mode 100644 index 8f1291cdcb5179..00000000000000 --- a/src/sentry/migrations/1008_add_organization_contributors.py +++ /dev/null @@ -1,77 +0,0 @@ -# Generated by Django 5.2.8 on 2025-11-21 00:00 - -import django.db.models.deletion -from django.db import migrations, models - -import sentry.db.models.fields.bounded -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", "1007_cleanup_failed_safe_deletes"), - ] - - operations = [ - migrations.CreateModel( - name="OrganizationContributors", - fields=[ - ( - "id", - sentry.db.models.fields.bounded.BoundedBigAutoField( - primary_key=True, serialize=False - ), - ), - ("external_identifier", models.CharField(db_index=True, max_length=255)), - ("alias", models.CharField(blank=True, max_length=255, null=True)), - ("num_actions", sentry.db.models.fields.bounded.BoundedIntegerField(default=0)), - ("date_added", models.DateTimeField(auto_now_add=True)), - ("date_updated", models.DateTimeField(auto_now=True)), - ( - "organization", - sentry.db.models.fields.foreignkey.FlexibleForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="sentry.organization", - ), - ), - ( - "integration_id", - sentry.db.models.fields.hybrid_cloud_foreign_key.HybridCloudForeignKey( - "sentry.Integration", - on_delete="CASCADE", - ), - ), - ], - options={ - "db_table": "sentry_organizationcontributors", - "constraints": [ - models.UniqueConstraint( - fields=("organization_id", "integration_id", "external_identifier"), - name="sentry_organizationcontributors_unique_org_contributor", - ) - ], - "indexes": [ - models.Index( - fields=("date_updated",), - name="sentry_organizationcontributors_date_updated_idx", - ), - ], - }, - ), - ] diff --git a/src/sentry/models/organizationcontributors.py b/src/sentry/models/organizationcontributor.py similarity index 100% rename from src/sentry/models/organizationcontributors.py rename to src/sentry/models/organizationcontributor.py From 9d122b3cbc359a3b749756132546647531930e39 Mon Sep 17 00:00:00 2001 From: Ajay Singh Date: Fri, 21 Nov 2025 13:55:00 -0800 Subject: [PATCH 5/5] fix --- migrations_lockfile.txt | 2 +- ...1008_add_organizationcontributors_table.py | 72 +++++++++++++++++++ src/sentry/models/__init__.py | 1 + ...ributor.py => organizationcontributors.py} | 4 +- 4 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 src/sentry/migrations/1008_add_organizationcontributors_table.py rename src/sentry/models/{organizationcontributor.py => organizationcontributors.py} (91%) diff --git a/migrations_lockfile.txt b/migrations_lockfile.txt index 741de05aa2ea89..4d4f1057a24e47 100644 --- a/migrations_lockfile.txt +++ b/migrations_lockfile.txt @@ -31,7 +31,7 @@ releases: 0004_cleanup_failed_safe_deletes replays: 0006_add_bulk_delete_job -sentry: 1007_cleanup_failed_safe_deletes +sentry: 1008_add_organizationcontributors_table social_auth: 0003_social_auth_json_field diff --git a/src/sentry/migrations/1008_add_organizationcontributors_table.py b/src/sentry/migrations/1008_add_organizationcontributors_table.py new file mode 100644 index 00000000000000..a48c3813744ed5 --- /dev/null +++ b/src/sentry/migrations/1008_add_organizationcontributors_table.py @@ -0,0 +1,72 @@ +# Generated by Django 5.2.8 on 2025-11-21 21:54 + +import django.db.models.deletion +from django.db import migrations, models + +import sentry.db.models.fields.bounded +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", "1007_cleanup_failed_safe_deletes"), + ] + + operations = [ + migrations.CreateModel( + name="OrganizationContributors", + fields=[ + ( + "id", + sentry.db.models.fields.bounded.BoundedBigAutoField( + primary_key=True, serialize=False + ), + ), + ( + "integration_id", + sentry.db.models.fields.hybrid_cloud_foreign_key.HybridCloudForeignKey( + "sentry.Integration", db_index=True, on_delete="CASCADE" + ), + ), + ("external_identifier", models.CharField(db_index=True, max_length=255)), + ("alias", models.CharField(blank=True, max_length=255, null=True)), + ("num_actions", sentry.db.models.fields.bounded.BoundedIntegerField(default=0)), + ("date_added", models.DateTimeField(auto_now_add=True)), + ("date_updated", models.DateTimeField(auto_now=True)), + ( + "organization", + sentry.db.models.fields.foreignkey.FlexibleForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="sentry.organization" + ), + ), + ], + options={ + "db_table": "sentry_organizationcontributors", + "indexes": [ + models.Index(fields=["date_updated"], name="sentry_orgcont_date_upd_idx") + ], + "constraints": [ + models.UniqueConstraint( + fields=("organization_id", "integration_id", "external_identifier"), + name="sentry_orgcont_unique_org_cont", + ) + ], + }, + ), + ] diff --git a/src/sentry/models/__init__.py b/src/sentry/models/__init__.py index c4219227436d2e..c34615e484f620 100644 --- a/src/sentry/models/__init__.py +++ b/src/sentry/models/__init__.py @@ -69,6 +69,7 @@ from .options import * # NOQA from .organization import * # NOQA from .organizationaccessrequest import * # NOQA +from .organizationcontributors import * # NOQA from .organizationmapping import * # NOQA from .organizationmember import * # NOQA from .organizationmemberinvite import * # NOQA diff --git a/src/sentry/models/organizationcontributor.py b/src/sentry/models/organizationcontributors.py similarity index 91% rename from src/sentry/models/organizationcontributor.py rename to src/sentry/models/organizationcontributors.py index 7461a28dd2e44d..f15113ea436be0 100644 --- a/src/sentry/models/organizationcontributor.py +++ b/src/sentry/models/organizationcontributors.py @@ -34,12 +34,12 @@ class Meta: constraints = [ models.UniqueConstraint( fields=["organization_id", "integration_id", "external_identifier"], - name="sentry_organizationcontributors_unique_org_contributor", + name="sentry_orgcont_unique_org_cont", ), ] indexes = [ models.Index( fields=["date_updated"], - name="sentry_organizationcontributors_date_updated_idx", + name="sentry_orgcont_date_upd_idx", ), ]