diff --git a/migrations_lockfile.txt b/migrations_lockfile.txt index 4cdd6b40320ef7..e9f72909864b86 100644 --- a/migrations_lockfile.txt +++ b/migrations_lockfile.txt @@ -9,5 +9,5 @@ feedback: 0003_feedback_add_env hybridcloud: 0009_make_user_id_optional_for_slug_reservation_replica nodestore: 0002_nodestore_no_dictfield replays: 0003_add_size_to_recording_segment -sentry: 0631_add_priority_columns_to_groupedmessage +sentry: 0632_apitoken_backfill_last_chars social_auth: 0002_default_auto_field diff --git a/src/sentry/migrations/0632_apitoken_backfill_last_chars.py b/src/sentry/migrations/0632_apitoken_backfill_last_chars.py new file mode 100644 index 00000000000000..0e299fb6d69390 --- /dev/null +++ b/src/sentry/migrations/0632_apitoken_backfill_last_chars.py @@ -0,0 +1,46 @@ +# Generated by Django 3.2.23 on 2024-01-17 16:06 + +from django.db import migrations + +from sentry.new_migrations.migrations import CheckedMigration +from sentry.utils.query import RangeQuerySetWrapperWithProgressBar + + +def backfill_last_token_characters(apps, _): + from sentry.models.apitoken import ApiToken + + for api_token in RangeQuerySetWrapperWithProgressBar(ApiToken.objects.all()): + if api_token.token_last_characters is None: + 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 = True + + dependencies = [ + ("sentry", "0631_add_priority_columns_to_groupedmessage"), + ] + + operations = [ + migrations.RunPython( + backfill_last_token_characters, + migrations.RunPython.noop, + hints={ + "tables": [ + "sentry_apitoken", + ] + }, + ), + ] diff --git a/tests/sentry/migrations/test_0632_apitoken_backfill_last_chars.py b/tests/sentry/migrations/test_0632_apitoken_backfill_last_chars.py new file mode 100644 index 00000000000000..1778932538209a --- /dev/null +++ b/tests/sentry/migrations/test_0632_apitoken_backfill_last_chars.py @@ -0,0 +1,37 @@ +from django.db import router + +from sentry.silo import unguarded_write +from sentry.testutils.cases import TestMigrations +from sentry.testutils.helpers import override_options + + +class LastCharsApiTokenMigrationTest(TestMigrations): + migrate_from = "0631_add_priority_columns_to_groupedmessage" + migrate_to = "0632_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() + + @override_options({"apitoken.auto-add-last-chars": False}) + 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() + + assert self.api_token.token_last_characters is None + + 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:]