This commit is contained in:
zandercymatics 2024-04-30 12:26:46 -06:00
parent 91545f45be
commit 74978ba5ba
No known key found for this signature in database
GPG key ID: FF4636ABEC9682B7
8 changed files with 193 additions and 115 deletions

View file

@ -33,7 +33,6 @@ pyzipper="*"
tblib = "*"
django-admin-multiple-choice-list-filter = "*"
django-waffle = "*"
cffi = "*"
[dev-packages]
django-debug-toolbar = "*"

107
src/Pipfile.lock generated
View file

@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "303753e916d4562cfa08da082bfc358d5dd3f7e6c45939225df7bd28ba3b44a9"
"sha256": "2c6a2a75bb42b1c2c4eb57e472ff7da8e9f2908e0b71cfb01daab53c73d26964"
},
"pipfile-spec": 6,
"requires": {},
@ -32,20 +32,20 @@
},
"boto3": {
"hashes": [
"sha256:b59355bf4a1408563969526f314611dbeacc151cf90ecb22af295dcc4fe18def",
"sha256:e39516e4ca21612932599819662759c04485d53ca457996a913163da11f052a4"
"sha256:22f65b3c9b7a419f8f39c2dddc421e14fab8cbb3bd8a9d467e874237d39f59b1",
"sha256:bbb87d641c73462e53b1777083b55c8f13921618ad08757478a8122985c56c13"
],
"index": "pypi",
"markers": "python_version >= '3.8'",
"version": "==1.34.93"
"version": "==1.34.94"
},
"botocore": {
"hashes": [
"sha256:6fbd5a53a2adc9b3d4ebd90ae0ede83a91a41d96231f8a5984051f75495f246d",
"sha256:79d39b0b87e962991c6dd55e78ce15155099f6fb741be88b1b8a456a702cc150"
"sha256:99b11be9a28f9051af4c96fa121e9c3f22a86d499abd773c9e868b2a38961bae",
"sha256:f00a79002e0cb9d6895ecd0919c506402850177d7b6c4d2634fa2da362d95bcb"
],
"markers": "python_version >= '3.8'",
"version": "==1.34.93"
"version": "==1.34.94"
},
"cachetools": {
"hashes": [
@ -127,8 +127,7 @@
"sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956",
"sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"
],
"index": "pypi",
"markers": "python_version >= '3.8'",
"markers": "platform_python_implementation != 'PyPy'",
"version": "==1.16.0"
},
"charset-normalizer": {
@ -402,11 +401,12 @@
},
"faker": {
"hashes": [
"sha256:13676b71346608350accc56e302d55ab7fca0db3f739145c3a3157d9623658a5",
"sha256:7692aa95155109b9348ab94afddd9049df41db64baa4ba6736653e947b52378e"
"sha256:87ef41e24b39a5be66ecd874af86f77eebd26782a2681200e86c5326340a95d3",
"sha256:e23a2b74888885c3d23a9237bacb823041291c03d609a39acb9ebe6c123f3986"
],
"index": "pypi",
"markers": "python_version >= '3.8'",
"version": "==24.14.0"
"version": "==25.0.0"
},
"fred-epplib": {
"git": "https://github.com/cisagov/epplib.git",
@ -597,7 +597,6 @@
"sha256:3d0c3dd24bb4605439bf91068598d00c6370684f8de4a67c2992683f6c309d6b",
"sha256:3dbe858ee582cbb2c6294dc85f55b5f19c918c2597855e950f34b660f1a5ede6",
"sha256:3dc773b2861b37b41a6136e0b72a1a44689a9c4c101e0cddb6b854016acc0aa8",
"sha256:3e183c6e3298a2ed5af9d7a356ea823bccaab4ec2349dc9ed83999fd289d14d5",
"sha256:3f7765e69bbce0906a7c74d5fe46d2c7a7596147318dbc08e4a2431f3060e306",
"sha256:417d14450f06d51f363e41cace6488519038f940676ce9664b34ebf5653433a5",
"sha256:44f6c7caff88d988db017b9b0e4ab04934f11e3e72d478031efc7edcac6c622f",
@ -1157,14 +1156,6 @@
"markers": "python_version >= '3.8'",
"version": "==4.11.0"
},
"tzdata": {
"hashes": [
"sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd",
"sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"
],
"markers": "sys_platform == 'win32'",
"version": "==2024.1"
},
"urllib3": {
"hashes": [
"sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d",
@ -1290,20 +1281,20 @@
},
"blinker": {
"hashes": [
"sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9",
"sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182"
"sha256:5f1cdeff423b77c31b89de0565cd03e5275a03028f44b2b15f912632a58cced6",
"sha256:da44ec748222dcd0105ef975eed946da197d5bdf8bafb6aa92f5bc89da63fa25"
],
"markers": "python_version >= '3.8'",
"version": "==1.7.0"
"version": "==1.8.1"
},
"boto3": {
"hashes": [
"sha256:b59355bf4a1408563969526f314611dbeacc151cf90ecb22af295dcc4fe18def",
"sha256:e39516e4ca21612932599819662759c04485d53ca457996a913163da11f052a4"
"sha256:22f65b3c9b7a419f8f39c2dddc421e14fab8cbb3bd8a9d467e874237d39f59b1",
"sha256:bbb87d641c73462e53b1777083b55c8f13921618ad08757478a8122985c56c13"
],
"index": "pypi",
"markers": "python_version >= '3.8'",
"version": "==1.34.93"
"version": "==1.34.94"
},
"boto3-mocking": {
"hashes": [
@ -1316,28 +1307,28 @@
},
"boto3-stubs": {
"hashes": [
"sha256:13c86a8137e969cea21a0f5700c66eb74f864cba8b94a816ee66b4224234f645",
"sha256:1aae0f06c56c8d9a67c45afaf61f2e7bc3fefac207ba5946a23b562d2631d8a3"
"sha256:6722b0b024293eb37713b8ec35d02e2c42c48b43615a8544c402972db053412d",
"sha256:6dc13d312ea2e7e045e71ba8d2796ee41e1ba1b98eaef7a84eb099e5b46ee450"
],
"index": "pypi",
"markers": "python_version >= '3.8'",
"version": "==1.34.93"
"version": "==1.34.94"
},
"botocore": {
"hashes": [
"sha256:6fbd5a53a2adc9b3d4ebd90ae0ede83a91a41d96231f8a5984051f75495f246d",
"sha256:79d39b0b87e962991c6dd55e78ce15155099f6fb741be88b1b8a456a702cc150"
"sha256:99b11be9a28f9051af4c96fa121e9c3f22a86d499abd773c9e868b2a38961bae",
"sha256:f00a79002e0cb9d6895ecd0919c506402850177d7b6c4d2634fa2da362d95bcb"
],
"markers": "python_version >= '3.8'",
"version": "==1.34.93"
"version": "==1.34.94"
},
"botocore-stubs": {
"hashes": [
"sha256:22e71e4c507df2e91d5673d2b65281c115d4c8d2601a3c2497c7a309fef6a0ec",
"sha256:909ebaf7a70ce83de4d85eed4b810e68c4d4cb64606d7431a263b909c92b7fa4"
"sha256:64d80a3467e3b19939e9c2750af33328b3087f8f524998dbdf7ed168227f507d",
"sha256:b0345f55babd8b901c53804fc5c326a4a0bd2e23e3b71f9ea5d9f7663466e6ba"
],
"markers": "python_version >= '3.8' and python_version < '4.0'",
"version": "==1.34.93"
"version": "==1.34.94"
},
"click": {
"hashes": [
@ -1347,14 +1338,6 @@
"markers": "python_version >= '3.7'",
"version": "==8.1.7"
},
"colorama": {
"hashes": [
"sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44",
"sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"
],
"markers": "platform_system == 'Windows'",
"version": "==0.4.6"
},
"django": {
"hashes": [
"sha256:a2d4c4d4ea0b6f0895acde632071aff6400bfc331228fc978b05452a0ff3e9f1",
@ -1382,20 +1365,20 @@
},
"django-stubs": {
"hashes": [
"sha256:4cf4de258fa71adc6f2799e983091b9d46cfc67c6eebc68fe111218c9a62b3b8",
"sha256:8ccd2ff4ee5adf22b9e3b7b1a516d2e1c2191e9d94e672c35cc2bc3dd61e0f6b"
"sha256:084484cbe16a6d388e80ec687e46f529d67a232f3befaf55c936b3b476be289d",
"sha256:b8a792bee526d6cab31e197cb414ee7fa218abd931a50948c66a80b3a2548621"
],
"index": "pypi",
"markers": "python_version >= '3.8'",
"version": "==4.2.7"
"version": "==5.0.0"
},
"django-stubs-ext": {
"hashes": [
"sha256:45a5d102417a412e3606e3c358adb4744988a92b7b58ccf3fd64bddd5d04d14c",
"sha256:519342ac0849cda1559746c9a563f03ff99f636b0ebe7c14b75e816a00dfddc3"
"sha256:5bacfbb498a206d5938454222b843d81da79ea8b6fcd1a59003f529e775bc115",
"sha256:8e1334fdf0c8bff87e25d593b33d4247487338aaed943037826244ff788b56a8"
],
"markers": "python_version >= '3.8'",
"version": "==4.2.7"
"version": "==5.0.0"
},
"django-webtest": {
"hashes": [
@ -1665,6 +1648,14 @@
"markers": "python_version >= '3.8'",
"version": "==5.2.0"
},
"tomli": {
"hashes": [
"sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc",
"sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"
],
"markers": "python_version < '3.11'",
"version": "==2.0.1"
},
"types-awscrt": {
"hashes": [
"sha256:3ae374b553e7228ba41a528cf42bd0b2ad7303d806c73eff4aaaac1515e3ea4e",
@ -1682,14 +1673,6 @@
"markers": "python_version >= '3.7'",
"version": "==5.3.0.7"
},
"types-pytz": {
"hashes": [
"sha256:6810c8a1f68f21fdf0f4f374a432487c77645a0ac0b31de4bf4690cf21ad3981",
"sha256:8335d443310e2db7b74e007414e74c4f53b67452c0cb0d228ca359ccfba59659"
],
"markers": "python_version >= '3.8'",
"version": "==2024.1.0.20240417"
},
"types-pyyaml": {
"hashes": [
"sha256:a9e0f0f88dc835739b0c1ca51ee90d04ca2a897a71af79de9aec5f38cb0a5342",
@ -1724,14 +1707,6 @@
"markers": "python_version >= '3.8'",
"version": "==4.11.0"
},
"tzdata": {
"hashes": [
"sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd",
"sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"
],
"markers": "sys_platform == 'win32'",
"version": "==2024.1"
},
"urllib3": {
"hashes": [
"sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d",

View file

@ -2159,12 +2159,15 @@ class UserGroupAdmin(AuditedAdmin):
def user_group(self, obj):
return obj.name
class WaffleFlagAdmin(FlagAdmin):
class Meta:
"""Contains meta information about this class"""
model = models.WaffleFlag
fields = "__all__"
admin.site.unregister(LogEntry) # Unregister the default registration
# Unregister samples and switches from django-waffle, as we currently don't use these.

View file

@ -8,33 +8,120 @@ import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('auth', '0012_alter_user_first_name_max_length'),
('registrar', '0089_user_verification_type copy'),
("auth", "0012_alter_user_first_name_max_length"),
("registrar", "0089_user_verification_type copy"),
]
operations = [
migrations.CreateModel(
name='WaffleFlag',
name="WaffleFlag",
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(help_text='The human/computer readable name.', max_length=100, unique=True, verbose_name='Name')),
('everyone', models.BooleanField(blank=True, help_text='Flip this flag on (Yes) or off (No) for everyone, overriding all other settings. Leave as Unknown to use normally.', null=True, verbose_name='Everyone')),
('percent', models.DecimalField(blank=True, decimal_places=1, help_text='A number between 0.0 and 99.9 to indicate a percentage of users for whom this flag will be active.', max_digits=3, null=True, verbose_name='Percent')),
('testing', models.BooleanField(default=False, help_text='Allow this flag to be set for a session for user testing', verbose_name='Testing')),
('superusers', models.BooleanField(default=True, help_text='Flag always active for superusers?', verbose_name='Superusers')),
('staff', models.BooleanField(default=False, help_text='Flag always active for staff?', verbose_name='Staff')),
('authenticated', models.BooleanField(default=False, help_text='Flag always active for authenticated users?', verbose_name='Authenticated')),
('languages', models.TextField(blank=True, default='', help_text='Activate this flag for users with one of these languages (comma-separated list)', verbose_name='Languages')),
('rollout', models.BooleanField(default=False, help_text='Activate roll-out mode?', verbose_name='Rollout')),
('note', models.TextField(blank=True, help_text='Note where this Flag is used.', verbose_name='Note')),
('created', models.DateTimeField(db_index=True, default=django.utils.timezone.now, help_text='Date when this Flag was created.', verbose_name='Created')),
('modified', models.DateTimeField(default=django.utils.timezone.now, help_text='Date when this Flag was last modified.', verbose_name='Modified')),
('groups', models.ManyToManyField(blank=True, help_text='Activate this flag for these user groups.', to='auth.group', verbose_name='Groups')),
('users', models.ManyToManyField(blank=True, help_text='Activate this flag for these users.', to=settings.AUTH_USER_MODEL, verbose_name='Users')),
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
(
"name",
models.CharField(
help_text="The human/computer readable name.", max_length=100, unique=True, verbose_name="Name"
),
),
(
"everyone",
models.BooleanField(
blank=True,
help_text="Flip this flag on (Yes) or off (No) for everyone, overriding all other settings. Leave as Unknown to use normally.",
null=True,
verbose_name="Everyone",
),
),
(
"percent",
models.DecimalField(
blank=True,
decimal_places=1,
help_text="A number between 0.0 and 99.9 to indicate a percentage of users for whom this flag will be active.",
max_digits=3,
null=True,
verbose_name="Percent",
),
),
(
"testing",
models.BooleanField(
default=False,
help_text="Allow this flag to be set for a session for user testing",
verbose_name="Testing",
),
),
(
"superusers",
models.BooleanField(
default=True, help_text="Flag always active for superusers?", verbose_name="Superusers"
),
),
(
"staff",
models.BooleanField(default=False, help_text="Flag always active for staff?", verbose_name="Staff"),
),
(
"authenticated",
models.BooleanField(
default=False,
help_text="Flag always active for authenticated users?",
verbose_name="Authenticated",
),
),
(
"languages",
models.TextField(
blank=True,
default="",
help_text="Activate this flag for users with one of these languages (comma-separated list)",
verbose_name="Languages",
),
),
(
"rollout",
models.BooleanField(default=False, help_text="Activate roll-out mode?", verbose_name="Rollout"),
),
("note", models.TextField(blank=True, help_text="Note where this Flag is used.", verbose_name="Note")),
(
"created",
models.DateTimeField(
db_index=True,
default=django.utils.timezone.now,
help_text="Date when this Flag was created.",
verbose_name="Created",
),
),
(
"modified",
models.DateTimeField(
default=django.utils.timezone.now,
help_text="Date when this Flag was last modified.",
verbose_name="Modified",
),
),
(
"groups",
models.ManyToManyField(
blank=True,
help_text="Activate this flag for these user groups.",
to="auth.group",
verbose_name="Groups",
),
),
(
"users",
models.ManyToManyField(
blank=True,
help_text="Activate this flag for these users.",
to=settings.AUTH_USER_MODEL,
verbose_name="Users",
),
),
],
options={
'verbose_name': 'waffle flag',
'verbose_name_plural': 'Waffle flags',
"verbose_name": "waffle flag",
"verbose_name_plural": "Waffle flags",
},
),
]

View file

@ -9,19 +9,25 @@ from typing import Any
# For linting: RunPython expects a function reference,
# so let's give it one
def create_flags():
def create_flags(apps, schema_editor):
"""
Populates pre-existing flags we wish to associate.
Only generates a flag name and a note, but no other data is loaded at this point.
"""
WaffleFlag.create_waffle_flags_for_migrations()
def delete_flags():
# This is a bit of a hack to get around "apps" not knowing what the concept of a constant is
default_flags = WaffleFlag.get_default_waffle_flags()
WaffleFlag.create_waffle_flags_for_migrations(apps, default_flags)
def delete_flags(apps, schema_editor):
"""
Deletes all prexisting flags.
Does not delete flags not defined in this scope (user generated).
"""
WaffleFlag.delete_waffle_flags_for_migrations()
# This is a bit of a hack to get around "apps" not knowing what the concept of a constant is
default_flags = WaffleFlag.get_default_waffle_flags()
WaffleFlag.delete_waffle_flags_for_migrations(apps, default_flags)
class Migration(migrations.Migration):

View file

@ -14,48 +14,56 @@ class WaffleFlag(AbstractUserFlag):
class Meta:
"""Contains meta information about this class"""
verbose_name = "waffle flag"
verbose_name_plural = "Waffle flags"
# Defines which waffle flags should be created at startup.
# Add to this list if you want to add another flag that is generated at startup.
# When you do so, you will need to add a new instance of `0091_create_waffle_flags_v{version_number}`
# in registrar/migrations for that change to update automatically on migrate.
DEFAULT_WAFFLE_FLAGS = [
"profile_feature",
"dns_hosting_feature"
@staticmethod
def get_default_waffle_flags():
"""
Defines which waffle flags should be created at startup.
Add to this list if you want to add another flag that is generated at startup.
When you do so, you will need to add a new instance of `0091_create_waffle_flags_v{version_number}`
in registrar/migrations for that change to update automatically on migrate.
This has to exist here, as from the context of migrations, it cannot access constants
"""
default_flags = [
# flag_name, flag_note
("profile_feature", "Used for profiles"),
("dns_hosting_feature", "Used for dns hosting"),
]
return default_flags
@classmethod
def create_waffle_flags_for_migrations(cls):
@staticmethod
def create_waffle_flags_for_migrations(apps, default_waffle_flags):
"""
Creates a pre-defined list of flags for our migrations.
"""
logger.info("Creating default waffle flags...")
WaffleFlag = apps.get_model("registrar", "WaffleFlag")
# Flags can be changed through the command line or through django admin.
# To keep the scope of this function minimal and simple, if we require additional
# config on these flag, it should be done in a seperate function or as a command.
for flag_name in cls.DEFAULT_WAFFLE_FLAGS:
for flag_name, flag_note in default_waffle_flags:
try:
cls.objects.update_or_create(
WaffleFlag.objects.update_or_create(
name=flag_name,
# Booleans like superusers or is_staff can be set here, if needed.
defaults={
'note': 'Auto-generated waffle flag'
}
defaults={"note": flag_note},
)
except Exception as e:
logger.error(f"An error occurred when attempting to add or update flag {flag_name}: {e}")
@classmethod
def delete_waffle_flags_for_migrations(cls):
@staticmethod
def delete_waffle_flags_for_migrations(apps, default_waffle_flags):
"""
Delete a pre-defined list of flags for our migrations (the reverse_code operation).
"""
logger.info("Deleting default waffle flags...")
existing_flags = cls.objects.filter(name__in=cls.DEFAULT_WAFFLE_FLAGS)
WaffleFlag = apps.get_model("registrar", "WaffleFlag")
existing_flags = WaffleFlag.objects.filter(name__in=default_waffle_flags)
for flag in existing_flags:
try:
cls.objects.get(name=flag.name).delete()
WaffleFlag.objects.get(name=flag.name).delete()
except Exception as e:
logger.error(f"An error occurred when attempting to delete flag {flag.name}: {e}")

Binary file not shown.