From cb2d2d76588020c00ed48c9d6338bb2ac41b0177 Mon Sep 17 00:00:00 2001 From: mdtro Date: Thu, 26 Oct 2023 12:43:38 -0500 Subject: [PATCH 1/4] feat: add name and last four characters to ApiToken model --- migrations_lockfile.txt | 2 +- .../0584_apitoken_add_name_and_last_four.py | 36 +++++++++++++++++ .../0585_apitoken_backfill_last_chars.py | 40 +++++++++++++++++++ src/sentry/models/apitoken.py | 2 + .../test_0585_apitoken_backfill_last_chars.py | 23 +++++++++++ 5 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 src/sentry/migrations/0584_apitoken_add_name_and_last_four.py create mode 100644 src/sentry/migrations/0585_apitoken_backfill_last_chars.py create mode 100644 tests/sentry/migrations/test_0585_apitoken_backfill_last_chars.py diff --git a/migrations_lockfile.txt b/migrations_lockfile.txt index 35434f9975e725..0b4246e12a976f 100644 --- a/migrations_lockfile.txt +++ b/migrations_lockfile.txt @@ -9,5 +9,5 @@ feedback: 0003_feedback_add_env hybridcloud: 0007_add_orgauthtokenreplica nodestore: 0002_nodestore_no_dictfield replays: 0003_add_size_to_recording_segment -sentry: 0583_add_early_adopter_to_organization_mapping +sentry: 0585_apitoken_backfill_last_chars social_auth: 0002_default_auto_field diff --git a/src/sentry/migrations/0584_apitoken_add_name_and_last_four.py b/src/sentry/migrations/0584_apitoken_add_name_and_last_four.py new file mode 100644 index 00000000000000..dfb22c2601fa40 --- /dev/null +++ b/src/sentry/migrations/0584_apitoken_add_name_and_last_four.py @@ -0,0 +1,36 @@ +# Generated by Django 3.2.20 on 2023-10-24 23:47 + +from django.db import migrations, models + +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. For + # the most part, 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 dangerous: + # - Large data migrations. Typically we want these to be run manually by ops 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 + # have ops run this and not block the deploy. Note that while adding an index is a schema + # change, it's completely safe to run the operation after the code has deployed. + is_dangerous = False + + dependencies = [ + ("sentry", "0583_add_early_adopter_to_organization_mapping"), + ] + + operations = [ + migrations.AddField( + model_name="apitoken", + name="name", + field=models.CharField(max_length=255, null=True), + ), + migrations.AddField( + model_name="apitoken", + name="token_last_characters", + field=models.CharField(max_length=4, null=True), + ), + ] diff --git a/src/sentry/migrations/0585_apitoken_backfill_last_chars.py b/src/sentry/migrations/0585_apitoken_backfill_last_chars.py new file mode 100644 index 00000000000000..ae9f0b9444a562 --- /dev/null +++ b/src/sentry/migrations/0585_apitoken_backfill_last_chars.py @@ -0,0 +1,40 @@ +# Generated by Django 3.2.20 on 2023-10-24 23:51 + +from django.db import migrations + +from sentry.new_migrations.migrations import CheckedMigration +from sentry.utils.query import RangeQuerySetWrapperWithProgressBar + + +def backfill_last_token_characters(apps, _): + ApiToken = apps.get_model("sentry", "ApiToken") + for api_token in RangeQuerySetWrapperWithProgressBar(ApiToken.objects.all()): + last_four = api_token.token[-4:] + api_token.token_last_characters = last_four + api_token.save() + + +class Migration(CheckedMigration): + # This flag is used to mark that a migration shouldn't be automatically run in production. For + # the most part, 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 dangerous: + # - Large data migrations. Typically we want these to be run manually by ops 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 + # have ops run this and not block the deploy. Note that while adding an index is a schema + # change, it's completely safe to run the operation after the code has deployed. + is_dangerous = False + + dependencies = [ + ("sentry", "0584_apitoken_add_name_and_last_four"), + ] + + operations = [ + migrations.RunPython( + backfill_last_token_characters, + migrations.RunPython.noop, + hints={"tables": ["sentry_apitoken"]}, + ), + ] diff --git a/src/sentry/models/apitoken.py b/src/sentry/models/apitoken.py index 3c71b85170c878..bb50afac44cf1d 100644 --- a/src/sentry/models/apitoken.py +++ b/src/sentry/models/apitoken.py @@ -37,7 +37,9 @@ class ApiToken(ReplicatedControlModel, HasApiScopes): # users can generate tokens without being application-bound application = FlexibleForeignKey("sentry.ApiApplication", null=True) user = FlexibleForeignKey("sentry.User") + name = models.CharField(max_length=255, null=True) token = models.CharField(max_length=64, unique=True, default=generate_token) + token_last_characters = models.CharField(max_length=4, null=True) refresh_token = models.CharField(max_length=64, unique=True, null=True, default=generate_token) expires_at = models.DateTimeField(null=True, default=default_expiration) date_added = models.DateTimeField(default=timezone.now) diff --git a/tests/sentry/migrations/test_0585_apitoken_backfill_last_chars.py b/tests/sentry/migrations/test_0585_apitoken_backfill_last_chars.py new file mode 100644 index 00000000000000..f47eb7cfbbf5e1 --- /dev/null +++ b/tests/sentry/migrations/test_0585_apitoken_backfill_last_chars.py @@ -0,0 +1,23 @@ +from sentry.testutils.cases import TestMigrations + + +class NameLastCharsApiTokenMigrationTest(TestMigrations): + migrate_from = "0584_apitoken_add_name_and_last_four" + migrate_to = "0585_apitoken_backfill_last_chars" + connection = "control" + + def setup_before_migration(self, apps): + ApiToken = apps.get_model("sentry", "ApiToken") + self.api_token = ApiToken.objects.create( + user_id=self.user.id, + refresh_token=None, + ) + self.api_token.save() + + def test(self): + from sentry.models.apitoken import ApiToken + + api_tokens = ApiToken.objects.all() + for api_token in api_tokens: + assert api_token.name is None + assert api_token.token_last_characters == api_token.token[-4:] From 29a3761a8008967120af56111bd5323d7ffee16a Mon Sep 17 00:00:00 2001 From: mdtro Date: Thu, 26 Oct 2023 14:50:31 -0500 Subject: [PATCH 2/4] tests: wrap with unguarded_write --- .../migrations/test_0585_apitoken_backfill_last_chars.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/sentry/migrations/test_0585_apitoken_backfill_last_chars.py b/tests/sentry/migrations/test_0585_apitoken_backfill_last_chars.py index f47eb7cfbbf5e1..45adcae78b89c2 100644 --- a/tests/sentry/migrations/test_0585_apitoken_backfill_last_chars.py +++ b/tests/sentry/migrations/test_0585_apitoken_backfill_last_chars.py @@ -1,3 +1,6 @@ +from django.db import router + +from sentry.silo import unguarded_write from sentry.testutils.cases import TestMigrations @@ -6,6 +9,12 @@ class NameLastCharsApiTokenMigrationTest(TestMigrations): migrate_to = "0585_apitoken_backfill_last_chars" connection = "control" + def setUp(self): + from sentry.models.apitoken import ApiToken + + with unguarded_write(using=router.db_for_write(ApiToken)): + super().setUp() + def setup_before_migration(self, apps): ApiToken = apps.get_model("sentry", "ApiToken") self.api_token = ApiToken.objects.create( From dbc1174ca652d23bc0d3d84dc1dcea54ef475b06 Mon Sep 17 00:00:00 2001 From: mdtro Date: Thu, 26 Oct 2023 17:14:33 -0500 Subject: [PATCH 3/4] add test and fix migration name typo --- .../0582_add_status_indexes_checkins.py | 39 ------------------- 1 file changed, 39 deletions(-) delete mode 100644 src/sentry/migrations/0582_add_status_indexes_checkins.py diff --git a/src/sentry/migrations/0582_add_status_indexes_checkins.py b/src/sentry/migrations/0582_add_status_indexes_checkins.py deleted file mode 100644 index 9ad581d66d4204..00000000000000 --- a/src/sentry/migrations/0582_add_status_indexes_checkins.py +++ /dev/null @@ -1,39 +0,0 @@ -# Generated by Django 3.2.20 on 2023-10-24 17:13 - -from django.db import migrations, models - -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. For - # the most part, 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 dangerous: - # - Large data migrations. Typically we want these to be run manually by ops 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 - # have ops run this and not block the deploy. Note that while adding an index is a schema - # change, it's completely safe to run the operation after the code has deployed. - is_dangerous = True - - dependencies = [ - ("sentry", "0581_add_user_and_team_to_alert_rules"), - ] - - operations = [ - migrations.AddIndex( - model_name="monitorcheckin", - index=models.Index( - fields=["monitor", "status", "date_added"], name="sentry_moni_monitor_7ed5ce_idx" - ), - ), - migrations.AddIndex( - model_name="monitorcheckin", - index=models.Index( - fields=["monitor_environment", "status", "date_added"], - name="sentry_moni_monitor_d75fdf_idx", - ), - ), - ] From 1cfbbf88061bde5d75180a8302af0f2347990a36 Mon Sep 17 00:00:00 2001 From: mdtro Date: Thu, 26 Oct 2023 18:14:38 -0500 Subject: [PATCH 4/4] Revert "add test and fix migration name typo" This reverts commit e8061f1ec408899ad4e1f29403adf360bc2d956e. --- .../0582_add_status_indexes_checkins.py | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/sentry/migrations/0582_add_status_indexes_checkins.py diff --git a/src/sentry/migrations/0582_add_status_indexes_checkins.py b/src/sentry/migrations/0582_add_status_indexes_checkins.py new file mode 100644 index 00000000000000..9ad581d66d4204 --- /dev/null +++ b/src/sentry/migrations/0582_add_status_indexes_checkins.py @@ -0,0 +1,39 @@ +# Generated by Django 3.2.20 on 2023-10-24 17:13 + +from django.db import migrations, models + +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. For + # the most part, 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 dangerous: + # - Large data migrations. Typically we want these to be run manually by ops 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 + # have ops run this and not block the deploy. Note that while adding an index is a schema + # change, it's completely safe to run the operation after the code has deployed. + is_dangerous = True + + dependencies = [ + ("sentry", "0581_add_user_and_team_to_alert_rules"), + ] + + operations = [ + migrations.AddIndex( + model_name="monitorcheckin", + index=models.Index( + fields=["monitor", "status", "date_added"], name="sentry_moni_monitor_7ed5ce_idx" + ), + ), + migrations.AddIndex( + model_name="monitorcheckin", + index=models.Index( + fields=["monitor_environment", "status", "date_added"], + name="sentry_moni_monitor_d75fdf_idx", + ), + ), + ]