From 145beb37fcb4fa2f487f18b234dd72bc4e10c279 Mon Sep 17 00:00:00 2001 From: MarkKoz Date: Sat, 21 Mar 2020 10:04:44 -0700 Subject: Allow empty list for message embeds By default, blank=False for ArrayFields but allow_empty=True for ListField. Before DRF 3.10 there was a bug that ListField didn't respect the default value of blank=False and thus created a ListField in the serialiser with the default of allow_empty=True. We were relying on the behaviour of that bug. See encode/django-rest-framework#6597. --- .../migrations/0051_allow_blank_message_embeds.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 pydis_site/apps/api/migrations/0051_allow_blank_message_embeds.py (limited to 'pydis_site/apps/api/migrations') diff --git a/pydis_site/apps/api/migrations/0051_allow_blank_message_embeds.py b/pydis_site/apps/api/migrations/0051_allow_blank_message_embeds.py new file mode 100644 index 00000000..e617e1c9 --- /dev/null +++ b/pydis_site/apps/api/migrations/0051_allow_blank_message_embeds.py @@ -0,0 +1,21 @@ +# Generated by Django 3.0.4 on 2020-03-21 17:05 + +import django.contrib.postgres.fields +import django.contrib.postgres.fields.jsonb +from django.db import migrations +import pydis_site.apps.api.models.bot.tag + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0050_remove_infractions_active_default_value'), + ] + + operations = [ + migrations.AlterField( + model_name='deletedmessage', + name='embeds', + field=django.contrib.postgres.fields.ArrayField(base_field=django.contrib.postgres.fields.jsonb.JSONField(validators=[pydis_site.apps.api.models.bot.tag.validate_tag_embed]), blank=True, help_text='Embeds attached to this message.', size=None), + ), + ] -- cgit v1.2.3 From 354d78ea01b11f8197dd6933be460f9b277e4645 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Wed, 27 May 2020 09:24:48 +0200 Subject: No longer accept or track avatar_hash. This should completely remove avatar_hash from the site - both in our tests, in the model itself, and from the database (as a result of the migration). --- .../api/migrations/0052_remove_user_avatar_hash.py | 17 +++++++ pydis_site/apps/api/models/bot/user.py | 8 ---- pydis_site/apps/api/serializers.py | 2 +- pydis_site/apps/api/tests/test_deleted_messages.py | 2 - pydis_site/apps/api/tests/test_infractions.py | 7 +-- pydis_site/apps/api/tests/test_models.py | 53 ++++++++++++++-------- pydis_site/apps/api/tests/test_nominations.py | 2 - pydis_site/apps/api/tests/test_reminders.py | 4 -- pydis_site/apps/api/tests/test_users.py | 8 ---- pydis_site/apps/api/viewsets/bot/user.py | 5 -- pydis_site/apps/home/tests/test_signal_listener.py | 5 -- pydis_site/apps/staff/tests/test_logs_view.py | 1 - 12 files changed, 52 insertions(+), 62 deletions(-) create mode 100644 pydis_site/apps/api/migrations/0052_remove_user_avatar_hash.py (limited to 'pydis_site/apps/api/migrations') diff --git a/pydis_site/apps/api/migrations/0052_remove_user_avatar_hash.py b/pydis_site/apps/api/migrations/0052_remove_user_avatar_hash.py new file mode 100644 index 00000000..26b3b954 --- /dev/null +++ b/pydis_site/apps/api/migrations/0052_remove_user_avatar_hash.py @@ -0,0 +1,17 @@ +# Generated by Django 2.2.11 on 2020-05-27 07:17 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0051_create_news_setting'), + ] + + operations = [ + migrations.RemoveField( + model_name='user', + name='avatar_hash', + ), + ] diff --git a/pydis_site/apps/api/models/bot/user.py b/pydis_site/apps/api/models/bot/user.py index 5140d2bf..65e8751e 100644 --- a/pydis_site/apps/api/models/bot/user.py +++ b/pydis_site/apps/api/models/bot/user.py @@ -31,14 +31,6 @@ class User(ModelReprMixin, models.Model): ), help_text="The discriminator of this user, taken from Discord." ) - avatar_hash = models.CharField( - max_length=100, - help_text=( - "The user's avatar hash, taken from Discord. " - "Null if the user does not have any custom avatar." - ), - null=True - ) roles = models.ManyToManyField( Role, help_text="Any roles this user has on our server." diff --git a/pydis_site/apps/api/serializers.py b/pydis_site/apps/api/serializers.py index e11c4af2..cc3f167d 100644 --- a/pydis_site/apps/api/serializers.py +++ b/pydis_site/apps/api/serializers.py @@ -235,7 +235,7 @@ class UserSerializer(BulkSerializerMixin, ModelSerializer): """Metadata defined for the Django REST Framework.""" model = User - fields = ('id', 'avatar_hash', 'name', 'discriminator', 'roles', 'in_guild') + fields = ('id', 'name', 'discriminator', 'roles', 'in_guild') depth = 1 diff --git a/pydis_site/apps/api/tests/test_deleted_messages.py b/pydis_site/apps/api/tests/test_deleted_messages.py index fb93cae6..f079a8dd 100644 --- a/pydis_site/apps/api/tests/test_deleted_messages.py +++ b/pydis_site/apps/api/tests/test_deleted_messages.py @@ -13,7 +13,6 @@ class DeletedMessagesWithoutActorTests(APISubdomainTestCase): id=55, name='Robbie Rotten', discriminator=55, - avatar_hash=None ) cls.data = { @@ -54,7 +53,6 @@ class DeletedMessagesWithActorTests(APISubdomainTestCase): id=12904, name='Joe Armstrong', discriminator=1245, - avatar_hash=None ) cls.data = { diff --git a/pydis_site/apps/api/tests/test_infractions.py b/pydis_site/apps/api/tests/test_infractions.py index bc258b77..93ef8171 100644 --- a/pydis_site/apps/api/tests/test_infractions.py +++ b/pydis_site/apps/api/tests/test_infractions.py @@ -47,7 +47,6 @@ class InfractionTests(APISubdomainTestCase): id=5, name='james', discriminator=1, - avatar_hash=None ) cls.ban_hidden = Infraction.objects.create( user_id=cls.user.id, @@ -169,13 +168,11 @@ class CreationTests(APISubdomainTestCase): id=5, name='james', discriminator=1, - avatar_hash=None ) cls.second_user = User.objects.create( id=6, name='carl', discriminator=2, - avatar_hash=None ) def test_accepts_valid_data(self): @@ -522,7 +519,6 @@ class ExpandedTests(APISubdomainTestCase): id=5, name='james', discriminator=1, - avatar_hash=None ) cls.kick = Infraction.objects.create( user_id=cls.user.id, @@ -540,7 +536,7 @@ class ExpandedTests(APISubdomainTestCase): def check_expanded_fields(self, infraction): for key in ('user', 'actor'): obj = infraction[key] - for field in ('id', 'name', 'discriminator', 'avatar_hash', 'roles', 'in_guild'): + for field in ('id', 'name', 'discriminator', 'roles', 'in_guild'): self.assertTrue(field in obj, msg=f'field "{field}" missing from {key}') def test_list_expanded(self): @@ -599,7 +595,6 @@ class SerializerTests(APISubdomainTestCase): id=5, name='james', discriminator=1, - avatar_hash=None ) def create_infraction(self, _type: str, active: bool): diff --git a/pydis_site/apps/api/tests/test_models.py b/pydis_site/apps/api/tests/test_models.py index a97d3251..b4754484 100644 --- a/pydis_site/apps/api/tests/test_models.py +++ b/pydis_site/apps/api/tests/test_models.py @@ -39,12 +39,14 @@ class StringDunderMethodTests(SimpleTestCase): self.nomination = Nomination( id=123, actor=User( - id=9876, name='Mr. Hemlock', - discriminator=6666, avatar_hash=None + id=9876, + name='Mr. Hemlock', + discriminator=6666, ), user=User( - id=9876, name="Hemlock's Cat", - discriminator=7777, avatar_hash=None + id=9876, + name="Hemlock's Cat", + discriminator=7777, ), reason="He purrrrs like the best!", ) @@ -53,15 +55,17 @@ class StringDunderMethodTests(SimpleTestCase): DeletedMessage( id=45, author=User( - id=444, name='bill', - discriminator=5, avatar_hash=None + id=444, + name='bill', + discriminator=5, ), channel_id=666, content="wooey", deletion_context=MessageDeletionContext( actor=User( - id=5555, name='shawn', - discriminator=555, avatar_hash=None + id=5555, + name='shawn', + discriminator=555, ), creation=dt.utcnow() ), @@ -84,8 +88,9 @@ class StringDunderMethodTests(SimpleTestCase): Message( id=45, author=User( - id=444, name='bill', - discriminator=5, avatar_hash=None + id=444, + name='bill', + discriminator=5, ), channel_id=666, content="wooey", @@ -93,8 +98,9 @@ class StringDunderMethodTests(SimpleTestCase): ), MessageDeletionContext( actor=User( - id=5555, name='shawn', - discriminator=555, avatar_hash=None + id=5555, + name='shawn', + discriminator=555, ), creation=dt.utcnow() ), @@ -103,22 +109,29 @@ class StringDunderMethodTests(SimpleTestCase): embed={'content': "the builder"} ), User( - id=5, name='bob', - discriminator=1, avatar_hash=None + id=5, + name='bob', + discriminator=1, ), Infraction( - user_id=5, actor_id=5, - type='kick', reason='He terk my jerb!' + user_id=5, + actor_id=5, + type='kick', + reason='He terk my jerb!' ), Infraction( - user_id=5, actor_id=5, hidden=True, - type='kick', reason='He terk my jerb!', + user_id=5, + actor_id=5, + hidden=True, + type='kick', + reason='He terk my jerb!', expires_at=dt(5018, 11, 20, 15, 52, tzinfo=timezone.utc) ), Reminder( author=User( - id=452, name='billy', - discriminator=5, avatar_hash=None + id=452, + name='billy', + discriminator=5, ), channel_id=555, jump_url=( diff --git a/pydis_site/apps/api/tests/test_nominations.py b/pydis_site/apps/api/tests/test_nominations.py index 76cb4112..92c62c87 100644 --- a/pydis_site/apps/api/tests/test_nominations.py +++ b/pydis_site/apps/api/tests/test_nominations.py @@ -13,7 +13,6 @@ class CreationTests(APISubdomainTestCase): id=1234, name='joe dart', discriminator=1111, - avatar_hash=None ) def test_accepts_valid_data(self): @@ -190,7 +189,6 @@ class NominationTests(APISubdomainTestCase): id=1234, name='joe dart', discriminator=1111, - avatar_hash=None ) cls.active_nomination = Nomination.objects.create( diff --git a/pydis_site/apps/api/tests/test_reminders.py b/pydis_site/apps/api/tests/test_reminders.py index 3441e0cc..c7fa07c9 100644 --- a/pydis_site/apps/api/tests/test_reminders.py +++ b/pydis_site/apps/api/tests/test_reminders.py @@ -53,7 +53,6 @@ class ReminderCreationTests(APISubdomainTestCase): id=1234, name='Mermaid Man', discriminator=1234, - avatar_hash=None, ) def test_accepts_valid_data(self): @@ -86,7 +85,6 @@ class ReminderDeletionTests(APISubdomainTestCase): id=6789, name='Barnacle Boy', discriminator=6789, - avatar_hash=None, ) cls.reminder = Reminder.objects.create( @@ -118,7 +116,6 @@ class ReminderListTests(APISubdomainTestCase): id=6789, name='Patrick Star', discriminator=6789, - avatar_hash=None, ) cls.reminder_one = Reminder.objects.create( @@ -172,7 +169,6 @@ class ReminderUpdateTests(APISubdomainTestCase): id=666, name='Man Ray', discriminator=666, - avatar_hash=None, ) cls.reminder = Reminder.objects.create( diff --git a/pydis_site/apps/api/tests/test_users.py b/pydis_site/apps/api/tests/test_users.py index 86799f19..4f563dc6 100644 --- a/pydis_site/apps/api/tests/test_users.py +++ b/pydis_site/apps/api/tests/test_users.py @@ -49,7 +49,6 @@ class CreationTests(APISubdomainTestCase): url = reverse('bot:user-list', host='api') data = { 'id': 42, - 'avatar_hash': "validavatarhashiswear", 'name': "Test", 'discriminator': 42, 'roles': [ @@ -63,7 +62,6 @@ class CreationTests(APISubdomainTestCase): self.assertEqual(response.json(), data) user = User.objects.get(id=42) - self.assertEqual(user.avatar_hash, data['avatar_hash']) self.assertEqual(user.name, data['name']) self.assertEqual(user.discriminator, data['discriminator']) self.assertEqual(user.in_guild, data['in_guild']) @@ -73,7 +71,6 @@ class CreationTests(APISubdomainTestCase): data = [ { 'id': 5, - 'avatar_hash': "hahayes", 'name': "test man", 'discriminator': 42, 'roles': [ @@ -83,7 +80,6 @@ class CreationTests(APISubdomainTestCase): }, { 'id': 8, - 'avatar_hash': "maybenot", 'name': "another test man", 'discriminator': 555, 'roles': [], @@ -99,7 +95,6 @@ class CreationTests(APISubdomainTestCase): url = reverse('bot:user-list', host='api') data = { 'id': 5, - 'avatar_hash': "hahayes", 'name': "test man", 'discriminator': 42, 'roles': [ @@ -114,7 +109,6 @@ class CreationTests(APISubdomainTestCase): url = reverse('bot:user-list', host='api') data = { 'id': True, - 'avatar_hash': 1902831, 'discriminator': "totally!" } @@ -148,7 +142,6 @@ class UserModelTests(APISubdomainTestCase): ) cls.user_with_roles = User.objects.create( id=1, - avatar_hash="coolavatarhash", name="Test User with two roles", discriminator=1111, in_guild=True, @@ -157,7 +150,6 @@ class UserModelTests(APISubdomainTestCase): cls.user_without_roles = User.objects.create( id=2, - avatar_hash="coolavatarhash", name="Test User without roles", discriminator=2222, in_guild=True, diff --git a/pydis_site/apps/api/viewsets/bot/user.py b/pydis_site/apps/api/viewsets/bot/user.py index a407787e..8f5bccfa 100644 --- a/pydis_site/apps/api/viewsets/bot/user.py +++ b/pydis_site/apps/api/viewsets/bot/user.py @@ -17,7 +17,6 @@ class UserViewSet(BulkCreateModelMixin, ModelViewSet): >>> [ ... { ... 'id': 409107086526644234, - ... 'avatar': "3ba3c1acce584c20b1e96fc04bbe80eb", ... 'name': "Python", ... 'discriminator': 4329, ... 'roles': [ @@ -39,7 +38,6 @@ class UserViewSet(BulkCreateModelMixin, ModelViewSet): #### Response format >>> { ... 'id': 409107086526644234, - ... 'avatar': "3ba3c1acce584c20b1e96fc04bbe80eb", ... 'name': "Python", ... 'discriminator': 4329, ... 'roles': [ @@ -62,7 +60,6 @@ class UserViewSet(BulkCreateModelMixin, ModelViewSet): #### Request body >>> { ... 'id': int, - ... 'avatar': str, ... 'name': str, ... 'discriminator': int, ... 'roles': List[int], @@ -83,7 +80,6 @@ class UserViewSet(BulkCreateModelMixin, ModelViewSet): #### Request body >>> { ... 'id': int, - ... 'avatar': str, ... 'name': str, ... 'discriminator': int, ... 'roles': List[int], @@ -102,7 +98,6 @@ class UserViewSet(BulkCreateModelMixin, ModelViewSet): #### Request body >>> { ... 'id': int, - ... 'avatar': str, ... 'name': str, ... 'discriminator': int, ... 'roles': List[int], diff --git a/pydis_site/apps/home/tests/test_signal_listener.py b/pydis_site/apps/home/tests/test_signal_listener.py index 66a67252..fb9a17db 100644 --- a/pydis_site/apps/home/tests/test_signal_listener.py +++ b/pydis_site/apps/home/tests/test_signal_listener.py @@ -81,14 +81,12 @@ class SignalListenerTests(TestCase): id=0, name="user", discriminator=0, - avatar_hash=None ) cls.discord_unmapped = DiscordUser.objects.create( id=2, name="unmapped", discriminator=0, - avatar_hash=None ) cls.discord_unmapped.roles.add(cls.unmapped_role) @@ -98,7 +96,6 @@ class SignalListenerTests(TestCase): id=3, name="not-in-guild", discriminator=0, - avatar_hash=None, in_guild=False ) @@ -106,7 +103,6 @@ class SignalListenerTests(TestCase): id=1, name="admin", discriminator=0, - avatar_hash=None ) cls.discord_admin.roles.set([cls.admin_role]) @@ -116,7 +112,6 @@ class SignalListenerTests(TestCase): id=4, name="admin", discriminator=0, - avatar_hash=None ) cls.discord_moderator.roles.set([cls.moderator_role]) diff --git a/pydis_site/apps/staff/tests/test_logs_view.py b/pydis_site/apps/staff/tests/test_logs_view.py index 1415c558..936d1ad5 100644 --- a/pydis_site/apps/staff/tests/test_logs_view.py +++ b/pydis_site/apps/staff/tests/test_logs_view.py @@ -21,7 +21,6 @@ class TestLogsView(TestCase): id=12345678901, name='Alan Turing', discriminator=1912, - avatar_hash=None ) cls.author.roles.add(cls.developers_role) -- cgit v1.2.3 From fac0d15afade7b00e883e3f9798a846956f222cc Mon Sep 17 00:00:00 2001 From: Joseph Banks Date: Tue, 2 Jun 2020 21:13:01 +0100 Subject: Add migrations to switch user field to array --- .../api/migrations/0053_user_roles_to_array.py | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 pydis_site/apps/api/migrations/0053_user_roles_to_array.py (limited to 'pydis_site/apps/api/migrations') diff --git a/pydis_site/apps/api/migrations/0053_user_roles_to_array.py b/pydis_site/apps/api/migrations/0053_user_roles_to_array.py new file mode 100644 index 00000000..7ff3a548 --- /dev/null +++ b/pydis_site/apps/api/migrations/0053_user_roles_to_array.py @@ -0,0 +1,24 @@ +# Generated by Django 2.2.11 on 2020-06-02 13:42 + +import django.contrib.postgres.fields +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0052_remove_user_avatar_hash'), + ] + + operations = [ + migrations.RemoveField( + model_name='user', + name='roles', + ), + migrations.AddField( + model_name='user', + name='roles', + field=django.contrib.postgres.fields.ArrayField(base_field=models.BigIntegerField(validators=[django.core.validators.MinValueValidator(limit_value=0, message='Role IDs cannot be negative.')]), default=list, help_text='IDs of roles the user has on the server', size=None), + ), + ] -- cgit v1.2.3 From 044fb2661de97c710c37e48f908d99a4b173a264 Mon Sep 17 00:00:00 2001 From: Joseph Banks Date: Tue, 2 Jun 2020 21:13:19 +0100 Subject: Add validator to ensure roles passed to user model exist --- .../migrations/0054_user_invalidate_unknown_role.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 pydis_site/apps/api/migrations/0054_user_invalidate_unknown_role.py (limited to 'pydis_site/apps/api/migrations') diff --git a/pydis_site/apps/api/migrations/0054_user_invalidate_unknown_role.py b/pydis_site/apps/api/migrations/0054_user_invalidate_unknown_role.py new file mode 100644 index 00000000..96230015 --- /dev/null +++ b/pydis_site/apps/api/migrations/0054_user_invalidate_unknown_role.py @@ -0,0 +1,21 @@ +# Generated by Django 2.2.11 on 2020-06-02 20:08 + +import django.contrib.postgres.fields +import django.core.validators +from django.db import migrations, models +import pydis_site.apps.api.models.bot.user + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0053_user_roles_to_array'), + ] + + operations = [ + migrations.AlterField( + model_name='user', + name='roles', + field=django.contrib.postgres.fields.ArrayField(base_field=models.BigIntegerField(validators=[django.core.validators.MinValueValidator(limit_value=0, message='Role IDs cannot be negative.'), pydis_site.apps.api.models.bot.user._validate_existing_role]), default=list, help_text='IDs of roles the user has on the server', size=None), + ), + ] -- cgit v1.2.3 From 1f4beeb10ccec010aa2d503ed73b4b64e9c1895f Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Tue, 14 Jul 2020 14:21:16 +0200 Subject: Rename utils.py to mixins.py. More precise. https://github.com/python-discord/site/issues/305 --- pydis_site/apps/api/migrations/0049_offensivemessage.py | 4 ++-- pydis_site/apps/api/models/__init__.py | 2 +- pydis_site/apps/api/models/bot/bot_setting.py | 2 +- pydis_site/apps/api/models/bot/documentation_link.py | 2 +- pydis_site/apps/api/models/bot/infraction.py | 2 +- pydis_site/apps/api/models/bot/message.py | 2 +- .../apps/api/models/bot/message_deletion_context.py | 2 +- pydis_site/apps/api/models/bot/nomination.py | 2 +- .../apps/api/models/bot/off_topic_channel_name.py | 2 +- pydis_site/apps/api/models/bot/offensive_message.py | 2 +- pydis_site/apps/api/models/bot/reminder.py | 2 +- pydis_site/apps/api/models/bot/role.py | 2 +- pydis_site/apps/api/models/bot/tag.py | 2 +- pydis_site/apps/api/models/bot/user.py | 2 +- pydis_site/apps/api/models/log_entry.py | 2 +- pydis_site/apps/api/models/mixins.py | 17 +++++++++++++++++ pydis_site/apps/api/models/utils.py | 17 ----------------- 17 files changed, 33 insertions(+), 33 deletions(-) create mode 100644 pydis_site/apps/api/models/mixins.py delete mode 100644 pydis_site/apps/api/models/utils.py (limited to 'pydis_site/apps/api/migrations') diff --git a/pydis_site/apps/api/migrations/0049_offensivemessage.py b/pydis_site/apps/api/migrations/0049_offensivemessage.py index fe4a1961..f342cec3 100644 --- a/pydis_site/apps/api/migrations/0049_offensivemessage.py +++ b/pydis_site/apps/api/migrations/0049_offensivemessage.py @@ -3,7 +3,7 @@ import django.core.validators from django.db import migrations, models import pydis_site.apps.api.models.bot.offensive_message -import pydis_site.apps.api.models.utils +import pydis_site.apps.api.models.mixins class Migration(migrations.Migration): @@ -20,6 +20,6 @@ class Migration(migrations.Migration): ('channel_id', models.BigIntegerField(help_text='The channel ID that the message was sent in, taken from Discord.', validators=[django.core.validators.MinValueValidator(limit_value=0, message='Channel IDs cannot be negative.')])), ('delete_date', models.DateTimeField(help_text='The date on which the message will be auto-deleted.', validators=[pydis_site.apps.api.models.bot.offensive_message.future_date_validator])), ], - bases=(pydis_site.apps.api.models.utils.ModelReprMixin, models.Model), + bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model), ), ] diff --git a/pydis_site/apps/api/models/__init__.py b/pydis_site/apps/api/models/__init__.py index 450d18cd..644b8757 100644 --- a/pydis_site/apps/api/models/__init__.py +++ b/pydis_site/apps/api/models/__init__.py @@ -15,4 +15,4 @@ from .bot import ( User ) from .log_entry import LogEntry -from .utils import ModelReprMixin +from .mixins import ModelReprMixin diff --git a/pydis_site/apps/api/models/bot/bot_setting.py b/pydis_site/apps/api/models/bot/bot_setting.py index 8d48eac7..2a3944f8 100644 --- a/pydis_site/apps/api/models/bot/bot_setting.py +++ b/pydis_site/apps/api/models/bot/bot_setting.py @@ -2,7 +2,7 @@ from django.contrib.postgres import fields as pgfields from django.core.exceptions import ValidationError from django.db import models -from pydis_site.apps.api.models.utils import ModelReprMixin +from pydis_site.apps.api.models.mixins import ModelReprMixin def validate_bot_setting_name(name: str) -> None: diff --git a/pydis_site/apps/api/models/bot/documentation_link.py b/pydis_site/apps/api/models/bot/documentation_link.py index f844ae04..5a46460b 100644 --- a/pydis_site/apps/api/models/bot/documentation_link.py +++ b/pydis_site/apps/api/models/bot/documentation_link.py @@ -1,6 +1,6 @@ from django.db import models -from pydis_site.apps.api.models.utils import ModelReprMixin +from pydis_site.apps.api.models.mixins import ModelReprMixin class DocumentationLink(ModelReprMixin, models.Model): diff --git a/pydis_site/apps/api/models/bot/infraction.py b/pydis_site/apps/api/models/bot/infraction.py index f58e89a3..7660cbba 100644 --- a/pydis_site/apps/api/models/bot/infraction.py +++ b/pydis_site/apps/api/models/bot/infraction.py @@ -2,7 +2,7 @@ from django.db import models from django.utils import timezone from pydis_site.apps.api.models.bot.user import User -from pydis_site.apps.api.models.utils import ModelReprMixin +from pydis_site.apps.api.models.mixins import ModelReprMixin class Infraction(ModelReprMixin, models.Model): diff --git a/pydis_site/apps/api/models/bot/message.py b/pydis_site/apps/api/models/bot/message.py index 8b18fc9f..7694ac75 100644 --- a/pydis_site/apps/api/models/bot/message.py +++ b/pydis_site/apps/api/models/bot/message.py @@ -7,7 +7,7 @@ from django.utils import timezone from pydis_site.apps.api.models.bot.tag import validate_tag_embed from pydis_site.apps.api.models.bot.user import User -from pydis_site.apps.api.models.utils import ModelReprMixin +from pydis_site.apps.api.models.mixins import ModelReprMixin class Message(ModelReprMixin, models.Model): diff --git a/pydis_site/apps/api/models/bot/message_deletion_context.py b/pydis_site/apps/api/models/bot/message_deletion_context.py index 44a0c8ae..04ae8d34 100644 --- a/pydis_site/apps/api/models/bot/message_deletion_context.py +++ b/pydis_site/apps/api/models/bot/message_deletion_context.py @@ -1,7 +1,7 @@ from django.db import models from pydis_site.apps.api.models.bot.user import User -from pydis_site.apps.api.models.utils import ModelReprMixin +from pydis_site.apps.api.models.mixins import ModelReprMixin class MessageDeletionContext(ModelReprMixin, models.Model): diff --git a/pydis_site/apps/api/models/bot/nomination.py b/pydis_site/apps/api/models/bot/nomination.py index cd9951aa..21e34e87 100644 --- a/pydis_site/apps/api/models/bot/nomination.py +++ b/pydis_site/apps/api/models/bot/nomination.py @@ -1,7 +1,7 @@ from django.db import models from pydis_site.apps.api.models.bot.user import User -from pydis_site.apps.api.models.utils import ModelReprMixin +from pydis_site.apps.api.models.mixins import ModelReprMixin class Nomination(ModelReprMixin, models.Model): diff --git a/pydis_site/apps/api/models/bot/off_topic_channel_name.py b/pydis_site/apps/api/models/bot/off_topic_channel_name.py index 29280c27..20e77b9f 100644 --- a/pydis_site/apps/api/models/bot/off_topic_channel_name.py +++ b/pydis_site/apps/api/models/bot/off_topic_channel_name.py @@ -1,7 +1,7 @@ from django.core.validators import RegexValidator from django.db import models -from pydis_site.apps.api.models.utils import ModelReprMixin +from pydis_site.apps.api.models.mixins import ModelReprMixin class OffTopicChannelName(ModelReprMixin, models.Model): diff --git a/pydis_site/apps/api/models/bot/offensive_message.py b/pydis_site/apps/api/models/bot/offensive_message.py index b466d9c2..6c0e5ffb 100644 --- a/pydis_site/apps/api/models/bot/offensive_message.py +++ b/pydis_site/apps/api/models/bot/offensive_message.py @@ -4,7 +4,7 @@ from django.core.exceptions import ValidationError from django.core.validators import MinValueValidator from django.db import models -from pydis_site.apps.api.models.utils import ModelReprMixin +from pydis_site.apps.api.models.mixins import ModelReprMixin def future_date_validator(date: datetime.date) -> None: diff --git a/pydis_site/apps/api/models/bot/reminder.py b/pydis_site/apps/api/models/bot/reminder.py index d53fedb5..28722435 100644 --- a/pydis_site/apps/api/models/bot/reminder.py +++ b/pydis_site/apps/api/models/bot/reminder.py @@ -2,7 +2,7 @@ from django.core.validators import MinValueValidator from django.db import models from pydis_site.apps.api.models.bot.user import User -from pydis_site.apps.api.models.utils import ModelReprMixin +from pydis_site.apps.api.models.mixins import ModelReprMixin class Reminder(ModelReprMixin, models.Model): diff --git a/pydis_site/apps/api/models/bot/role.py b/pydis_site/apps/api/models/bot/role.py index 58bbf8b4..721e4815 100644 --- a/pydis_site/apps/api/models/bot/role.py +++ b/pydis_site/apps/api/models/bot/role.py @@ -3,7 +3,7 @@ from __future__ import annotations from django.core.validators import MaxValueValidator, MinValueValidator from django.db import models -from pydis_site.apps.api.models.utils import ModelReprMixin +from pydis_site.apps.api.models.mixins import ModelReprMixin class Role(ModelReprMixin, models.Model): diff --git a/pydis_site/apps/api/models/bot/tag.py b/pydis_site/apps/api/models/bot/tag.py index 5d4cc393..5e53582f 100644 --- a/pydis_site/apps/api/models/bot/tag.py +++ b/pydis_site/apps/api/models/bot/tag.py @@ -6,7 +6,7 @@ from django.core.exceptions import ValidationError from django.core.validators import MaxLengthValidator, MinLengthValidator from django.db import models -from pydis_site.apps.api.models.utils import ModelReprMixin +from pydis_site.apps.api.models.mixins import ModelReprMixin def is_bool_validator(value: Any) -> None: diff --git a/pydis_site/apps/api/models/bot/user.py b/pydis_site/apps/api/models/bot/user.py index bff4d642..d7f203aa 100644 --- a/pydis_site/apps/api/models/bot/user.py +++ b/pydis_site/apps/api/models/bot/user.py @@ -4,7 +4,7 @@ from django.core.validators import MaxValueValidator, MinValueValidator from django.db import models from pydis_site.apps.api.models.bot.role import Role -from pydis_site.apps.api.models.utils import ModelReprMixin +from pydis_site.apps.api.models.mixins import ModelReprMixin def _validate_existing_role(value: int) -> None: diff --git a/pydis_site/apps/api/models/log_entry.py b/pydis_site/apps/api/models/log_entry.py index 488af48e..752cd2ca 100644 --- a/pydis_site/apps/api/models/log_entry.py +++ b/pydis_site/apps/api/models/log_entry.py @@ -1,7 +1,7 @@ from django.db import models from django.utils import timezone -from pydis_site.apps.api.models.utils import ModelReprMixin +from pydis_site.apps.api.models.mixins import ModelReprMixin class LogEntry(ModelReprMixin, models.Model): diff --git a/pydis_site/apps/api/models/mixins.py b/pydis_site/apps/api/models/mixins.py new file mode 100644 index 00000000..0540c4de --- /dev/null +++ b/pydis_site/apps/api/models/mixins.py @@ -0,0 +1,17 @@ +from operator import itemgetter + + +class ModelReprMixin: + """Mixin providing a `__repr__()` to display model class name and initialisation parameters.""" + + def __repr__(self): + """Returns the current model class name and initialisation parameters.""" + attributes = ' '.join( + f'{attribute}={value!r}' + for attribute, value in sorted( + self.__dict__.items(), + key=itemgetter(0) + ) + if not attribute.startswith('_') + ) + return f'<{self.__class__.__name__}({attributes})>' diff --git a/pydis_site/apps/api/models/utils.py b/pydis_site/apps/api/models/utils.py deleted file mode 100644 index 0540c4de..00000000 --- a/pydis_site/apps/api/models/utils.py +++ /dev/null @@ -1,17 +0,0 @@ -from operator import itemgetter - - -class ModelReprMixin: - """Mixin providing a `__repr__()` to display model class name and initialisation parameters.""" - - def __repr__(self): - """Returns the current model class name and initialisation parameters.""" - attributes = ' '.join( - f'{attribute}={value!r}' - for attribute, value in sorted( - self.__dict__.items(), - key=itemgetter(0) - ) - if not attribute.startswith('_') - ) - return f'<{self.__class__.__name__}({attributes})>' -- cgit v1.2.3 From fbd00e184e60f21630e25a3bcc36c46f05f44bae Mon Sep 17 00:00:00 2001 From: MarkKoz Date: Tue, 14 Jul 2020 13:29:10 -0700 Subject: Merge migrations --- pydis_site/apps/api/migrations/0055_merge_20200714_2027.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 pydis_site/apps/api/migrations/0055_merge_20200714_2027.py (limited to 'pydis_site/apps/api/migrations') diff --git a/pydis_site/apps/api/migrations/0055_merge_20200714_2027.py b/pydis_site/apps/api/migrations/0055_merge_20200714_2027.py new file mode 100644 index 00000000..f2a0e638 --- /dev/null +++ b/pydis_site/apps/api/migrations/0055_merge_20200714_2027.py @@ -0,0 +1,14 @@ +# Generated by Django 3.0.8 on 2020-07-14 20:27 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0051_allow_blank_message_embeds'), + ('api', '0054_user_invalidate_unknown_role'), + ] + + operations = [ + ] -- cgit v1.2.3 From 9208e0ac7943ce4ce14957f49c1597fb723cbd95 Mon Sep 17 00:00:00 2001 From: MarkKoz Date: Tue, 14 Jul 2020 13:37:19 -0700 Subject: Allow empty list for user roles This is the same issue as 145beb37fcb4fa2f487f18b234dd72bc4e10c279. See that commit for more information. --- .../api/migrations/0056_allow_blank_user_roles.py | 21 +++++++++++++++++++++ pydis_site/apps/api/models/bot/user.py | 1 + 2 files changed, 22 insertions(+) create mode 100644 pydis_site/apps/api/migrations/0056_allow_blank_user_roles.py (limited to 'pydis_site/apps/api/migrations') diff --git a/pydis_site/apps/api/migrations/0056_allow_blank_user_roles.py b/pydis_site/apps/api/migrations/0056_allow_blank_user_roles.py new file mode 100644 index 00000000..489941c7 --- /dev/null +++ b/pydis_site/apps/api/migrations/0056_allow_blank_user_roles.py @@ -0,0 +1,21 @@ +# Generated by Django 3.0.8 on 2020-07-14 20:35 + +import django.contrib.postgres.fields +import django.core.validators +from django.db import migrations, models +import pydis_site.apps.api.models.bot.user + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0055_merge_20200714_2027'), + ] + + operations = [ + migrations.AlterField( + model_name='user', + name='roles', + field=django.contrib.postgres.fields.ArrayField(base_field=models.BigIntegerField(validators=[django.core.validators.MinValueValidator(limit_value=0, message='Role IDs cannot be negative.'), pydis_site.apps.api.models.bot.user._validate_existing_role]), blank=True, default=list, help_text='IDs of roles the user has on the server', size=None), + ), + ] diff --git a/pydis_site/apps/api/models/bot/user.py b/pydis_site/apps/api/models/bot/user.py index bff4d642..0d8c574a 100644 --- a/pydis_site/apps/api/models/bot/user.py +++ b/pydis_site/apps/api/models/bot/user.py @@ -52,6 +52,7 @@ class User(ModelReprMixin, models.Model): ) ), default=list, + blank=True, help_text="IDs of roles the user has on the server" ) in_guild = models.BooleanField( -- cgit v1.2.3 From 0a73c2a019cd973c00cefbcf239a0bc7ca947cfa Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Wed, 15 Jul 2020 14:27:51 +0200 Subject: Minor fixes for imports and __init__ files. https://github.com/python-discord/site/issues/305 --- pydis_site/apps/api/migrations/0007_tag.py | 2 +- pydis_site/apps/api/migrations/0009_snakefact.py | 2 +- pydis_site/apps/api/migrations/0010_snakeidiom.py | 2 +- pydis_site/apps/api/migrations/0012_specialsnake.py | 2 +- pydis_site/apps/api/migrations/0018_messagedeletioncontext.py | 2 +- pydis_site/apps/api/migrations/0019_deletedmessage.py | 2 +- pydis_site/apps/api/migrations/0020_infraction.py | 2 +- pydis_site/apps/api/migrations/0030_reminder.py | 2 +- pydis_site/apps/api/migrations/0031_nomination.py | 2 +- pydis_site/apps/api/migrations/0032_botsetting.py | 2 +- pydis_site/apps/api/migrations/0035_create_table_log_entry.py | 2 +- pydis_site/apps/api/models/__init__.py | 1 - pydis_site/apps/api/viewsets/__init__.py | 1 + pydis_site/apps/api/viewsets/bot/__init__.py | 1 + 14 files changed, 13 insertions(+), 12 deletions(-) (limited to 'pydis_site/apps/api/migrations') diff --git a/pydis_site/apps/api/migrations/0007_tag.py b/pydis_site/apps/api/migrations/0007_tag.py index c22715f9..b6d146fe 100644 --- a/pydis_site/apps/api/migrations/0007_tag.py +++ b/pydis_site/apps/api/migrations/0007_tag.py @@ -18,6 +18,6 @@ class Migration(migrations.Migration): ('title', models.CharField(help_text='The title of this tag, shown in searches and providing a quick overview over what this embed contains.', max_length=100, primary_key=True, serialize=False)), ('embed', django.contrib.postgres.fields.jsonb.JSONField(help_text='The actual embed shown by this tag.')), ], - bases=(pydis_site.apps.api.models.ModelReprMixin, models.Model), + bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model), ), ] diff --git a/pydis_site/apps/api/migrations/0009_snakefact.py b/pydis_site/apps/api/migrations/0009_snakefact.py index 4fc63bc9..fd583846 100644 --- a/pydis_site/apps/api/migrations/0009_snakefact.py +++ b/pydis_site/apps/api/migrations/0009_snakefact.py @@ -16,6 +16,6 @@ class Migration(migrations.Migration): fields=[ ('fact', models.CharField(help_text='A fact about snakes.', max_length=200, primary_key=True, serialize=False)), ], - bases=(pydis_site.apps.api.models.ModelReprMixin, models.Model), + bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model), ), ] diff --git a/pydis_site/apps/api/migrations/0010_snakeidiom.py b/pydis_site/apps/api/migrations/0010_snakeidiom.py index be089cf4..7d06ce5f 100644 --- a/pydis_site/apps/api/migrations/0010_snakeidiom.py +++ b/pydis_site/apps/api/migrations/0010_snakeidiom.py @@ -16,6 +16,6 @@ class Migration(migrations.Migration): fields=[ ('idiom', models.CharField(help_text='A snake idiom', max_length=140, primary_key=True, serialize=False)), ], - bases=(pydis_site.apps.api.models.ModelReprMixin, models.Model), + bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model), ), ] diff --git a/pydis_site/apps/api/migrations/0012_specialsnake.py b/pydis_site/apps/api/migrations/0012_specialsnake.py index 77072526..ed0c1563 100644 --- a/pydis_site/apps/api/migrations/0012_specialsnake.py +++ b/pydis_site/apps/api/migrations/0012_specialsnake.py @@ -17,6 +17,6 @@ class Migration(migrations.Migration): ('name', models.CharField(max_length=140, primary_key=True, serialize=False)), ('info', models.TextField()), ], - bases=(pydis_site.apps.api.models.ModelReprMixin, models.Model), + bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model), ), ] diff --git a/pydis_site/apps/api/migrations/0018_messagedeletioncontext.py b/pydis_site/apps/api/migrations/0018_messagedeletioncontext.py index dced1288..7e372d04 100644 --- a/pydis_site/apps/api/migrations/0018_messagedeletioncontext.py +++ b/pydis_site/apps/api/migrations/0018_messagedeletioncontext.py @@ -19,6 +19,6 @@ class Migration(migrations.Migration): ('creation', models.DateTimeField(help_text='When this deletion took place.')), ('actor', models.ForeignKey(help_text='The original actor causing this deletion. Could be the author of a manual clean command invocation, the bot when executing automatic actions, or nothing to indicate that the bulk deletion was not issued by us.', null=True, on_delete=django.db.models.deletion.CASCADE, to='api.User')), ], - bases=(pydis_site.apps.api.models.ModelReprMixin, models.Model), + bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model), ), ] diff --git a/pydis_site/apps/api/migrations/0019_deletedmessage.py b/pydis_site/apps/api/migrations/0019_deletedmessage.py index 4b028f0c..33746253 100644 --- a/pydis_site/apps/api/migrations/0019_deletedmessage.py +++ b/pydis_site/apps/api/migrations/0019_deletedmessage.py @@ -25,6 +25,6 @@ class Migration(migrations.Migration): options={ 'abstract': False, }, - bases=(pydis_site.apps.api.models.ModelReprMixin, models.Model), + bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model), ), ] diff --git a/pydis_site/apps/api/migrations/0020_infraction.py b/pydis_site/apps/api/migrations/0020_infraction.py index 6bef6b77..96c71687 100644 --- a/pydis_site/apps/api/migrations/0020_infraction.py +++ b/pydis_site/apps/api/migrations/0020_infraction.py @@ -25,6 +25,6 @@ class Migration(migrations.Migration): ('actor', models.ForeignKey(help_text='The user which applied the infraction.', on_delete=django.db.models.deletion.CASCADE, related_name='infractions_given', to='api.User')), ('user', models.ForeignKey(help_text='The user to which the infraction was applied.', on_delete=django.db.models.deletion.CASCADE, related_name='infractions_received', to='api.User')), ], - bases=(pydis_site.apps.api.models.ModelReprMixin, models.Model), + bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model), ), ] diff --git a/pydis_site/apps/api/migrations/0030_reminder.py b/pydis_site/apps/api/migrations/0030_reminder.py index 8c42f6dc..e1f1afc3 100644 --- a/pydis_site/apps/api/migrations/0030_reminder.py +++ b/pydis_site/apps/api/migrations/0030_reminder.py @@ -22,6 +22,6 @@ class Migration(migrations.Migration): ('expiration', models.DateTimeField(help_text='When this reminder should be sent.')), ('author', models.ForeignKey(help_text='The creator of this reminder.', on_delete=django.db.models.deletion.CASCADE, to='api.User')), ], - bases=(pydis_site.apps.api.models.ModelReprMixin, models.Model), + bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model), ), ] diff --git a/pydis_site/apps/api/migrations/0031_nomination.py b/pydis_site/apps/api/migrations/0031_nomination.py index 75e69701..f39436c1 100644 --- a/pydis_site/apps/api/migrations/0031_nomination.py +++ b/pydis_site/apps/api/migrations/0031_nomination.py @@ -21,6 +21,6 @@ class Migration(migrations.Migration): ('inserted_at', models.DateTimeField(auto_now_add=True, help_text='The creation date of this nomination.')), ('author', models.ForeignKey(help_text='The staff member that nominated this user.', on_delete=django.db.models.deletion.CASCADE, related_name='nomination_set', to='api.User')), ], - bases=(pydis_site.apps.api.models.ModelReprMixin, models.Model), + bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model), ), ] diff --git a/pydis_site/apps/api/migrations/0032_botsetting.py b/pydis_site/apps/api/migrations/0032_botsetting.py index 25186a2b..3304edef 100644 --- a/pydis_site/apps/api/migrations/0032_botsetting.py +++ b/pydis_site/apps/api/migrations/0032_botsetting.py @@ -18,6 +18,6 @@ class Migration(migrations.Migration): ('name', models.CharField(max_length=50, primary_key=True, serialize=False)), ('data', django.contrib.postgres.fields.jsonb.JSONField(help_text='The actual settings of this setting.')), ], - bases=(pydis_site.apps.api.models.ModelReprMixin, models.Model), + bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model), ), ] diff --git a/pydis_site/apps/api/migrations/0035_create_table_log_entry.py b/pydis_site/apps/api/migrations/0035_create_table_log_entry.py index a8256a0e..c9a1ad19 100644 --- a/pydis_site/apps/api/migrations/0035_create_table_log_entry.py +++ b/pydis_site/apps/api/migrations/0035_create_table_log_entry.py @@ -24,6 +24,6 @@ class Migration(migrations.Migration): ('line', models.PositiveSmallIntegerField(help_text='The line at which the log line was emitted.')), ('message', models.TextField(help_text='The textual content of the log line.')), ], - bases=(pydis_site.apps.api.models.ModelReprMixin, models.Model), + bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model), ), ] diff --git a/pydis_site/apps/api/models/__init__.py b/pydis_site/apps/api/models/__init__.py index 04d0fc50..2839fbba 100644 --- a/pydis_site/apps/api/models/__init__.py +++ b/pydis_site/apps/api/models/__init__.py @@ -16,4 +16,3 @@ from .bot import ( User ) from .log_entry import LogEntry -from .mixins import ModelReprMixin, ModelTimestampMixin diff --git a/pydis_site/apps/api/viewsets/__init__.py b/pydis_site/apps/api/viewsets/__init__.py index 3cf9f641..98d3d586 100644 --- a/pydis_site/apps/api/viewsets/__init__.py +++ b/pydis_site/apps/api/viewsets/__init__.py @@ -1,5 +1,6 @@ # flake8: noqa from .bot import ( + AllowListViewSet, BotSettingViewSet, DeletedMessageViewSet, DocumentationLinkViewSet, diff --git a/pydis_site/apps/api/viewsets/bot/__init__.py b/pydis_site/apps/api/viewsets/bot/__init__.py index b3e0fa4d..86bfc910 100644 --- a/pydis_site/apps/api/viewsets/bot/__init__.py +++ b/pydis_site/apps/api/viewsets/bot/__init__.py @@ -1,4 +1,5 @@ # flake8: noqa +from .allowlist import AllowListViewSet from .bot_setting import BotSettingViewSet from .deleted_message import DeletedMessageViewSet from .documentation_link import DocumentationLinkViewSet -- cgit v1.2.3 From 787b317b4617df7931c96697126b26ea7e2a217d Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Wed, 15 Jul 2020 14:29:54 +0200 Subject: Add a migration for the new AllowList model. https://github.com/python-discord/site/issues/305 --- pydis_site/apps/api/migrations/0057_allowlist.py | 29 ++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 pydis_site/apps/api/migrations/0057_allowlist.py (limited to 'pydis_site/apps/api/migrations') diff --git a/pydis_site/apps/api/migrations/0057_allowlist.py b/pydis_site/apps/api/migrations/0057_allowlist.py new file mode 100644 index 00000000..7d815e91 --- /dev/null +++ b/pydis_site/apps/api/migrations/0057_allowlist.py @@ -0,0 +1,29 @@ +# Generated by Django 3.0.8 on 2020-07-15 11:23 + +from django.db import migrations, models +import pydis_site.apps.api.models.mixins + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0056_allow_blank_user_roles'), + ] + + operations = [ + migrations.CreateModel( + name='AllowList', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('type', models.CharField(choices=[('GUILD_INVITE_ID', 'Guild Invite Id'), ('FILE_FORMAT', 'File Format'), ('DOMAIN_NAME', 'Domain Name'), ('WORD_WATCHLIST', 'Word Watchlist')], help_text='The type of allowlist this is on.', max_length=50)), + ('allowed', models.BooleanField(help_text='Whether this item is on the allowlist or the denylist.')), + ('content', models.TextField(help_text='The data to add to the allowlist.')), + ], + options={ + 'abstract': False, + }, + bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model), + ), + ] -- cgit v1.2.3 From e8a32c717babee132626d6574f7ca706338739dc Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Wed, 15 Jul 2020 15:32:23 +0200 Subject: Add a UniqueConstraint to prevent duplicates. https://github.com/python-discord/site/issues/305 --- pydis_site/apps/api/migrations/0057_allowlist.py | 29 ----------------- .../migrations/0057_create_new_allowlist_model.py | 37 ++++++++++++++++++++++ pydis_site/apps/api/models/bot/allowlist.py | 13 ++++++++ 3 files changed, 50 insertions(+), 29 deletions(-) delete mode 100644 pydis_site/apps/api/migrations/0057_allowlist.py create mode 100644 pydis_site/apps/api/migrations/0057_create_new_allowlist_model.py (limited to 'pydis_site/apps/api/migrations') diff --git a/pydis_site/apps/api/migrations/0057_allowlist.py b/pydis_site/apps/api/migrations/0057_allowlist.py deleted file mode 100644 index 7d815e91..00000000 --- a/pydis_site/apps/api/migrations/0057_allowlist.py +++ /dev/null @@ -1,29 +0,0 @@ -# Generated by Django 3.0.8 on 2020-07-15 11:23 - -from django.db import migrations, models -import pydis_site.apps.api.models.mixins - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0056_allow_blank_user_roles'), - ] - - operations = [ - migrations.CreateModel( - name='AllowList', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('updated_at', models.DateTimeField(auto_now=True)), - ('type', models.CharField(choices=[('GUILD_INVITE_ID', 'Guild Invite Id'), ('FILE_FORMAT', 'File Format'), ('DOMAIN_NAME', 'Domain Name'), ('WORD_WATCHLIST', 'Word Watchlist')], help_text='The type of allowlist this is on.', max_length=50)), - ('allowed', models.BooleanField(help_text='Whether this item is on the allowlist or the denylist.')), - ('content', models.TextField(help_text='The data to add to the allowlist.')), - ], - options={ - 'abstract': False, - }, - bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model), - ), - ] diff --git a/pydis_site/apps/api/migrations/0057_create_new_allowlist_model.py b/pydis_site/apps/api/migrations/0057_create_new_allowlist_model.py new file mode 100644 index 00000000..45650d86 --- /dev/null +++ b/pydis_site/apps/api/migrations/0057_create_new_allowlist_model.py @@ -0,0 +1,37 @@ +# Generated by Django 3.0.8 on 2020-07-15 11:23 + +from django.db import migrations, models +import pydis_site.apps.api.models.mixins + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0056_allow_blank_user_roles'), + ] + + operations = [ + migrations.CreateModel( + name='AllowList', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('type', models.CharField(choices=[('GUILD_INVITE_ID', 'Guild Invite Id'), ('FILE_FORMAT', 'File Format'), ('DOMAIN_NAME', 'Domain Name'), ('WORD_WATCHLIST', 'Word Watchlist')], help_text='The type of allowlist this is on.', max_length=50)), + ('allowed', models.BooleanField(help_text='Whether this item is on the allowlist or the denylist.')), + ('content', models.TextField(help_text='The data to add to the allowlist.')), + ], + options={ + 'abstract': False, + }, + bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model), + ), + migrations.AlterModelTable( + name='allowlist', + table='allow_list', + ), + migrations.AddConstraint( + model_name='allowlist', + constraint=models.UniqueConstraint(fields=('content', 'type'), name='unique_allowlist'), + ) + ] diff --git a/pydis_site/apps/api/models/bot/allowlist.py b/pydis_site/apps/api/models/bot/allowlist.py index b0aea066..fc57ef32 100644 --- a/pydis_site/apps/api/models/bot/allowlist.py +++ b/pydis_site/apps/api/models/bot/allowlist.py @@ -24,3 +24,16 @@ class AllowList(ModelTimestampMixin, ModelReprMixin, models.Model): content = models.TextField( help_text="The data to add to the allowlist." ) + + class Meta: + """Metaconfig for this model.""" + + db_table = 'allow_list' + + # This constraint ensures only one allowlist with the same content + # can exist per type.This means that we cannot have both an allow + # and a deny for the same item, and we cannot have duplicates of the + # same item. + constraints = [ + models.UniqueConstraint(fields=['content', 'type'], name='unique_allowlist'), + ] -- cgit v1.2.3 From 02412121e8272d35a03c30a80a4a67e6aabdf0eb Mon Sep 17 00:00:00 2001 From: kosayoda Date: Thu, 16 Jul 2020 10:40:03 +0800 Subject: Add migration for the mentions field in the Reminder model --- .../apps/api/migrations/0055_reminder_mentions.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 pydis_site/apps/api/migrations/0055_reminder_mentions.py (limited to 'pydis_site/apps/api/migrations') diff --git a/pydis_site/apps/api/migrations/0055_reminder_mentions.py b/pydis_site/apps/api/migrations/0055_reminder_mentions.py new file mode 100644 index 00000000..d73b450d --- /dev/null +++ b/pydis_site/apps/api/migrations/0055_reminder_mentions.py @@ -0,0 +1,20 @@ +# Generated by Django 2.2.14 on 2020-07-15 07:37 + +import django.contrib.postgres.fields +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0054_user_invalidate_unknown_role'), + ] + + operations = [ + migrations.AddField( + model_name='reminder', + name='mentions', + field=django.contrib.postgres.fields.ArrayField(base_field=models.BigIntegerField(validators=[django.core.validators.MinValueValidator(limit_value=0, message='Mention IDs cannot be negative.')]), blank=True, default=list, help_text='IDs of roles or users to ping with the reminder.', size=None), + ), + ] -- cgit v1.2.3 From fbcf39f2bb05b9746ac911d25c2118ac9cf9dff3 Mon Sep 17 00:00:00 2001 From: kosayoda Date: Thu, 16 Jul 2020 15:52:02 +0800 Subject: Merge migrations --- pydis_site/apps/api/migrations/0057_merge_20200716_0751.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 pydis_site/apps/api/migrations/0057_merge_20200716_0751.py (limited to 'pydis_site/apps/api/migrations') diff --git a/pydis_site/apps/api/migrations/0057_merge_20200716_0751.py b/pydis_site/apps/api/migrations/0057_merge_20200716_0751.py new file mode 100644 index 00000000..47a6d2d4 --- /dev/null +++ b/pydis_site/apps/api/migrations/0057_merge_20200716_0751.py @@ -0,0 +1,14 @@ +# Generated by Django 2.2.14 on 2020-07-16 07:51 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0055_reminder_mentions'), + ('api', '0056_allow_blank_user_roles'), + ] + + operations = [ + ] -- cgit v1.2.3 From 4470afc5b48fa1c71ed717372acad5414e6ec0ad Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Thu, 16 Jul 2020 12:59:43 +0200 Subject: Fix a bug in an old migration. https://github.com/python-discord/site/issues/305 --- .../api/migrations/0025_allow_custom_inserted_at_infraction_field.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'pydis_site/apps/api/migrations') diff --git a/pydis_site/apps/api/migrations/0025_allow_custom_inserted_at_infraction_field.py b/pydis_site/apps/api/migrations/0025_allow_custom_inserted_at_infraction_field.py index 0c02cb91..9589346d 100644 --- a/pydis_site/apps/api/migrations/0025_allow_custom_inserted_at_infraction_field.py +++ b/pydis_site/apps/api/migrations/0025_allow_custom_inserted_at_infraction_field.py @@ -1,7 +1,7 @@ # Generated by Django 2.1.4 on 2019-01-06 16:01 -import datetime from django.db import migrations, models +from django.utils import timezone class Migration(migrations.Migration): @@ -14,6 +14,6 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='infraction', name='inserted_at', - field=models.DateTimeField(default=datetime.datetime.utcnow, help_text='The date and time of the creation of this infraction.'), + field=models.DateTimeField(default=timezone.now(), help_text='The date and time of the creation of this infraction.'), ), ] -- cgit v1.2.3 From 76cd687715e49cee97bac24f5d3c8ca40ffca099 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Thu, 16 Jul 2020 20:48:56 +0200 Subject: Rename AllowList to AllowDenyList. https://github.com/python-discord/site/issues/305 --- .../0057_create_new_allowdenylist_model.py | 32 ++++++++++ .../migrations/0057_create_new_allowlist_model.py | 37 ----------- pydis_site/apps/api/models/__init__.py | 2 +- pydis_site/apps/api/models/bot/__init__.py | 2 +- pydis_site/apps/api/models/bot/allow_deny_list.py | 37 +++++++++++ pydis_site/apps/api/models/bot/allowlist.py | 39 ------------ pydis_site/apps/api/serializers.py | 8 +-- pydis_site/apps/api/tests/test_allowlists.py | 14 ++--- pydis_site/apps/api/urls.py | 6 +- pydis_site/apps/api/viewsets/__init__.py | 2 +- pydis_site/apps/api/viewsets/bot/__init__.py | 2 +- .../apps/api/viewsets/bot/allow_deny_list.py | 72 ++++++++++++++++++++++ pydis_site/apps/api/viewsets/bot/allowlist.py | 72 ---------------------- 13 files changed, 159 insertions(+), 166 deletions(-) create mode 100644 pydis_site/apps/api/migrations/0057_create_new_allowdenylist_model.py delete mode 100644 pydis_site/apps/api/migrations/0057_create_new_allowlist_model.py create mode 100644 pydis_site/apps/api/models/bot/allow_deny_list.py delete mode 100644 pydis_site/apps/api/models/bot/allowlist.py create mode 100644 pydis_site/apps/api/viewsets/bot/allow_deny_list.py delete mode 100644 pydis_site/apps/api/viewsets/bot/allowlist.py (limited to 'pydis_site/apps/api/migrations') diff --git a/pydis_site/apps/api/migrations/0057_create_new_allowdenylist_model.py b/pydis_site/apps/api/migrations/0057_create_new_allowdenylist_model.py new file mode 100644 index 00000000..c450344b --- /dev/null +++ b/pydis_site/apps/api/migrations/0057_create_new_allowdenylist_model.py @@ -0,0 +1,32 @@ +# Generated by Django 3.0.8 on 2020-07-15 11:23 + +from django.db import migrations, models +import pydis_site.apps.api.models.mixins + + +class Migration(migrations.Migration): + dependencies = [ + ('api', '0056_allow_blank_user_roles'), + ] + + operations = [ + migrations.CreateModel( + name='AllowDenyList', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('type', models.CharField( + choices=[('GUILD_INVITE_ID', 'Guild Invite Id'), ('FILE_FORMAT', 'File Format'), + ('DOMAIN_NAME', 'Domain Name'), ('WORD_WATCHLIST', 'Word Watchlist')], + help_text='The type of allowlist this is on.', max_length=50)), + ('allowed', models.BooleanField(help_text='Whether this item is on the allowlist or the denylist.')), + ('content', models.TextField(help_text='The data to add to the allow or denylist.')), + ], + bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model), + ), + migrations.AddConstraint( + model_name='allowdenylist', + constraint=models.UniqueConstraint(fields=('content', 'type'), name='unique_allow_deny_list'), + ) + ] diff --git a/pydis_site/apps/api/migrations/0057_create_new_allowlist_model.py b/pydis_site/apps/api/migrations/0057_create_new_allowlist_model.py deleted file mode 100644 index 45650d86..00000000 --- a/pydis_site/apps/api/migrations/0057_create_new_allowlist_model.py +++ /dev/null @@ -1,37 +0,0 @@ -# Generated by Django 3.0.8 on 2020-07-15 11:23 - -from django.db import migrations, models -import pydis_site.apps.api.models.mixins - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0056_allow_blank_user_roles'), - ] - - operations = [ - migrations.CreateModel( - name='AllowList', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('updated_at', models.DateTimeField(auto_now=True)), - ('type', models.CharField(choices=[('GUILD_INVITE_ID', 'Guild Invite Id'), ('FILE_FORMAT', 'File Format'), ('DOMAIN_NAME', 'Domain Name'), ('WORD_WATCHLIST', 'Word Watchlist')], help_text='The type of allowlist this is on.', max_length=50)), - ('allowed', models.BooleanField(help_text='Whether this item is on the allowlist or the denylist.')), - ('content', models.TextField(help_text='The data to add to the allowlist.')), - ], - options={ - 'abstract': False, - }, - bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model), - ), - migrations.AlterModelTable( - name='allowlist', - table='allow_list', - ), - migrations.AddConstraint( - model_name='allowlist', - constraint=models.UniqueConstraint(fields=('content', 'type'), name='unique_allowlist'), - ) - ] diff --git a/pydis_site/apps/api/models/__init__.py b/pydis_site/apps/api/models/__init__.py index 2839fbba..34973a8d 100644 --- a/pydis_site/apps/api/models/__init__.py +++ b/pydis_site/apps/api/models/__init__.py @@ -1,6 +1,6 @@ # flake8: noqa from .bot import ( - AllowList, + AllowDenyList, BotSetting, DocumentationLink, DeletedMessage, diff --git a/pydis_site/apps/api/models/bot/__init__.py b/pydis_site/apps/api/models/bot/__init__.py index b373ee84..1234b35a 100644 --- a/pydis_site/apps/api/models/bot/__init__.py +++ b/pydis_site/apps/api/models/bot/__init__.py @@ -1,5 +1,5 @@ # flake8: noqa -from .allowlist import AllowList +from .allow_deny_list import AllowDenyList from .bot_setting import BotSetting from .deleted_message import DeletedMessage from .documentation_link import DocumentationLink diff --git a/pydis_site/apps/api/models/bot/allow_deny_list.py b/pydis_site/apps/api/models/bot/allow_deny_list.py new file mode 100644 index 00000000..1eef47ba --- /dev/null +++ b/pydis_site/apps/api/models/bot/allow_deny_list.py @@ -0,0 +1,37 @@ +from django.db import models + +from pydis_site.apps.api.models.mixins import ModelReprMixin, ModelTimestampMixin + + +class AllowDenyList(ModelTimestampMixin, ModelReprMixin, models.Model): + """An item that is either allowed or denied.""" + + AllowDenyListType = models.TextChoices( + 'AllowDenyListType', + 'GUILD_INVITE_ID ' + 'FILE_FORMAT ' + 'DOMAIN_NAME ' + 'WORD_WATCHLIST ' + ) + type = models.CharField( + max_length=50, + help_text="The type of allowlist this is on.", + choices=AllowDenyListType.choices, + ) + allowed = models.BooleanField( + help_text="Whether this item is on the allowlist or the denylist." + ) + content = models.TextField( + help_text="The data to add to the allow or denylist." + ) + + class Meta: + """Metaconfig for this model.""" + + # This constraint ensures only one allow or denylist with the + # same content can exist. This means that we cannot have both an allow + # and a deny for the same item, and we cannot have duplicates of the + # same item. + constraints = [ + models.UniqueConstraint(fields=['content', 'type'], name='unique_allow_deny_list'), + ] diff --git a/pydis_site/apps/api/models/bot/allowlist.py b/pydis_site/apps/api/models/bot/allowlist.py deleted file mode 100644 index fc57ef32..00000000 --- a/pydis_site/apps/api/models/bot/allowlist.py +++ /dev/null @@ -1,39 +0,0 @@ -from django.db import models - -from pydis_site.apps.api.models.mixins import ModelReprMixin, ModelTimestampMixin - - -class AllowList(ModelTimestampMixin, ModelReprMixin, models.Model): - """An item that is either allowed or denied.""" - - AllowListType = models.TextChoices( - 'AllowListType', - 'GUILD_INVITE_ID ' - 'FILE_FORMAT ' - 'DOMAIN_NAME ' - 'WORD_WATCHLIST ' - ) - type = models.CharField( - max_length=50, - help_text="The type of allowlist this is on.", - choices=AllowListType.choices, - ) - allowed = models.BooleanField( - help_text="Whether this item is on the allowlist or the denylist." - ) - content = models.TextField( - help_text="The data to add to the allowlist." - ) - - class Meta: - """Metaconfig for this model.""" - - db_table = 'allow_list' - - # This constraint ensures only one allowlist with the same content - # can exist per type.This means that we cannot have both an allow - # and a deny for the same item, and we cannot have duplicates of the - # same item. - constraints = [ - models.UniqueConstraint(fields=['content', 'type'], name='unique_allowlist'), - ] diff --git a/pydis_site/apps/api/serializers.py b/pydis_site/apps/api/serializers.py index 13030074..d532dd69 100644 --- a/pydis_site/apps/api/serializers.py +++ b/pydis_site/apps/api/serializers.py @@ -4,7 +4,7 @@ from rest_framework.validators import UniqueTogetherValidator from rest_framework_bulk import BulkSerializerMixin from .models import ( - AllowList, + AllowDenyList, BotSetting, DeletedMessage, DocumentationLink, @@ -104,13 +104,13 @@ class DocumentationLinkSerializer(ModelSerializer): fields = ('package', 'base_url', 'inventory_url') -class AllowListSerializer(ModelSerializer): - """A class providing (de-)serialization of `AllowList` instances.""" +class AllowDenyListSerializer(ModelSerializer): + """A class providing (de-)serialization of `AllowDenyList` instances.""" class Meta: """Metadata defined for the Django REST Framework.""" - model = AllowList + model = AllowDenyList fields = ('id', 'created_at', 'updated_at', 'type', 'allowed', 'content') diff --git a/pydis_site/apps/api/tests/test_allowlists.py b/pydis_site/apps/api/tests/test_allowlists.py index c6004439..5aa50670 100644 --- a/pydis_site/apps/api/tests/test_allowlists.py +++ b/pydis_site/apps/api/tests/test_allowlists.py @@ -1,9 +1,9 @@ from django_hosts.resolvers import reverse -from .base import APISubdomainTestCase -from ..models import AllowList +from pydis_site.apps.api.models import AllowDenyList +from pydis_site.apps.api.tests.base import APISubdomainTestCase -URL = reverse('bot:allowlist-list', host='api') +URL = reverse('bot:allowdenylist-list', host='api') JPEG_ALLOWLIST = { "type": 'FILE_FORMAT', "allowed": True, @@ -38,8 +38,8 @@ class EmptyDatabaseTests(APISubdomainTestCase): class FetchTests(APISubdomainTestCase): @classmethod def setUpTestData(cls): - cls.jpeg_format = AllowList.objects.create(**JPEG_ALLOWLIST) - cls.png_format = AllowList.objects.create(**PNG_ALLOWLIST) + cls.jpeg_format = AllowDenyList.objects.create(**JPEG_ALLOWLIST) + cls.png_format = AllowDenyList.objects.create(**PNG_ALLOWLIST) def test_returns_name_in_list(self): response = self.client.get(URL) @@ -83,8 +83,8 @@ class CreationTests(APISubdomainTestCase): class DeletionTests(APISubdomainTestCase): @classmethod def setUpTestData(cls): - cls.jpeg_format = AllowList.objects.create(**JPEG_ALLOWLIST) - cls.png_format = AllowList.objects.create(**PNG_ALLOWLIST) + cls.jpeg_format = AllowDenyList.objects.create(**JPEG_ALLOWLIST) + cls.png_format = AllowDenyList.objects.create(**PNG_ALLOWLIST) def test_deleting_unknown_id_returns_404(self): response = self.client.delete(f"{URL}/200") diff --git a/pydis_site/apps/api/urls.py b/pydis_site/apps/api/urls.py index bf41f09f..b6ed2914 100644 --- a/pydis_site/apps/api/urls.py +++ b/pydis_site/apps/api/urls.py @@ -3,7 +3,7 @@ from rest_framework.routers import DefaultRouter from .views import HealthcheckView, RulesView from .viewsets import ( - AllowListViewSet, + AllowDenyListViewSet, BotSettingViewSet, DeletedMessageViewSet, DocumentationLinkViewSet, @@ -22,8 +22,8 @@ from .viewsets import ( # http://www.django-rest-framework.org/api-guide/routers/#defaultrouter bot_router = DefaultRouter(trailing_slash=False) bot_router.register( - 'allowlists', - AllowListViewSet + 'allow_deny_lists', + AllowDenyListViewSet ) bot_router.register( 'bot-settings', diff --git a/pydis_site/apps/api/viewsets/__init__.py b/pydis_site/apps/api/viewsets/__init__.py index 98d3d586..eb7d5098 100644 --- a/pydis_site/apps/api/viewsets/__init__.py +++ b/pydis_site/apps/api/viewsets/__init__.py @@ -1,6 +1,6 @@ # flake8: noqa from .bot import ( - AllowListViewSet, + AllowDenyListViewSet, BotSettingViewSet, DeletedMessageViewSet, DocumentationLinkViewSet, diff --git a/pydis_site/apps/api/viewsets/bot/__init__.py b/pydis_site/apps/api/viewsets/bot/__init__.py index 86bfc910..11638dd8 100644 --- a/pydis_site/apps/api/viewsets/bot/__init__.py +++ b/pydis_site/apps/api/viewsets/bot/__init__.py @@ -1,5 +1,5 @@ # flake8: noqa -from .allowlist import AllowListViewSet +from .allow_deny_list import AllowDenyListViewSet from .bot_setting import BotSettingViewSet from .deleted_message import DeletedMessageViewSet from .documentation_link import DocumentationLinkViewSet diff --git a/pydis_site/apps/api/viewsets/bot/allow_deny_list.py b/pydis_site/apps/api/viewsets/bot/allow_deny_list.py new file mode 100644 index 00000000..a2499d89 --- /dev/null +++ b/pydis_site/apps/api/viewsets/bot/allow_deny_list.py @@ -0,0 +1,72 @@ +from rest_framework.viewsets import ModelViewSet + +from pydis_site.apps.api.models.bot.allow_deny_list import AllowDenyList +from pydis_site.apps.api.serializers import AllowDenyListSerializer + + +class AllowDenyListViewSet(ModelViewSet): + """ + View providing CRUD operations on items allowed or denied by our bot. + + ## Routes + ### GET /bot/allow_deny_lists + Returns all allow and denylist items in the database. + + #### Response format + >>> [ + ... { + ... 'id': "2309268224", + ... 'created_at': "01-01-2020 ...", + ... 'updated_at': "01-01-2020 ...", + ... 'type': "file_format", + ... 'allowed': 'true', + ... 'content': ".jpeg", + ... }, + ... ... + ... ] + + #### Status codes + - 200: returned on success + - 401: returned if unauthenticated + + ### GET /bot/allow_deny_lists/ + Returns a specific AllowDenyList item from the database. + + #### Response format + >>> { + ... 'id': "2309268224", + ... 'created_at': "01-01-2020 ...", + ... 'updated_at': "01-01-2020 ...", + ... 'type': "file_format", + ... 'allowed': 'true', + ... 'content': ".jpeg", + ... } + + #### Status codes + - 200: returned on success + - 404: returned if the id was not found. + + ### POST /bot/allow_deny_lists + Adds a single AllowDenyList item to the database. + + #### Request body + >>> { + ... 'type': str, + ... 'allowed': bool, + ... 'content': str, + ... } + + #### Status codes + - 201: returned on success + - 400: if one of the given fields is invalid + + ### DELETE /bot/allow_deny_lists/ + Deletes the AllowDenyList item with the given `id`. + + #### Status codes + - 204: returned on success + - 404: if a tag with the given `id` does not exist + """ + + serializer_class = AllowDenyListSerializer + queryset = AllowDenyList.objects.all() diff --git a/pydis_site/apps/api/viewsets/bot/allowlist.py b/pydis_site/apps/api/viewsets/bot/allowlist.py deleted file mode 100644 index 7cc82ff7..00000000 --- a/pydis_site/apps/api/viewsets/bot/allowlist.py +++ /dev/null @@ -1,72 +0,0 @@ -from rest_framework.viewsets import ModelViewSet - -from pydis_site.apps.api.models.bot.allowlist import AllowList -from pydis_site.apps.api.serializers import AllowListSerializer - - -class AllowListViewSet(ModelViewSet): - """ - View providing CRUD operations on items whitelisted or blacklisted by our bot. - - ## Routes - ### GET /bot/allowlists - Returns all allowlist items in the database. - - #### Response format - >>> [ - ... { - ... 'id': "2309268224", - ... 'created_at': "01-01-2020 ...", - ... 'updated_at': "01-01-2020 ...", - ... 'type': "file_format", - ... 'allowed': 'true', - ... 'content': ".jpeg", - ... }, - ... ... - ... ] - - #### Status codes - - 200: returned on success - - 401: returned if unauthenticated - - ### GET /bot/allowlists/ - Returns a specific AllowList item from the database. - - #### Response format - >>> { - ... 'id': "2309268224", - ... 'created_at': "01-01-2020 ...", - ... 'updated_at': "01-01-2020 ...", - ... 'type': "file_format", - ... 'allowed': 'true', - ... 'content': ".jpeg", - ... } - - #### Status codes - - 200: returned on success - - 404: returned if the id was not found. - - ### POST /bot/allowedlists - Adds a single allowedlist item to the database. - - #### Request body - >>> { - ... 'type': str, - ... 'allowed': bool, - ... 'content': str, - ... } - - #### Status codes - - 201: returned on success - - 400: if one of the given fields is invalid - - ### DELETE /bot/allowedlists/ - Deletes the tag with the given `id`. - - #### Status codes - - 204: returned on success - - 404: if a tag with the given `id` does not exist - """ - - serializer_class = AllowListSerializer - queryset = AllowList.objects.all() -- cgit v1.2.3 From 8874a83fa6372e0ce46e9552e8c21c182e4a1c3e Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Sun, 19 Jul 2020 11:55:20 +0200 Subject: Add another AllowDenyList field, 'comment'. This will be used to describe or justify the entries in the blacklist or whitelist, for example for the guild name in the case of guild invite IDs, so that we have some context when we're displaying the list. https://github.com/python-discord/site/issues/305 --- pydis_site/apps/api/migrations/0057_create_new_allowdenylist_model.py | 1 + pydis_site/apps/api/models/bot/allow_deny_list.py | 4 ++++ pydis_site/apps/api/serializers.py | 2 +- pydis_site/apps/api/viewsets/bot/allow_deny_list.py | 3 +++ 4 files changed, 9 insertions(+), 1 deletion(-) (limited to 'pydis_site/apps/api/migrations') diff --git a/pydis_site/apps/api/migrations/0057_create_new_allowdenylist_model.py b/pydis_site/apps/api/migrations/0057_create_new_allowdenylist_model.py index c450344b..d36acd70 100644 --- a/pydis_site/apps/api/migrations/0057_create_new_allowdenylist_model.py +++ b/pydis_site/apps/api/migrations/0057_create_new_allowdenylist_model.py @@ -22,6 +22,7 @@ class Migration(migrations.Migration): help_text='The type of allowlist this is on.', max_length=50)), ('allowed', models.BooleanField(help_text='Whether this item is on the allowlist or the denylist.')), ('content', models.TextField(help_text='The data to add to the allow or denylist.')), + ('comment', models.TextField(help_text="Optional comment on this entry.", null=True)), ], bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model), ), diff --git a/pydis_site/apps/api/models/bot/allow_deny_list.py b/pydis_site/apps/api/models/bot/allow_deny_list.py index 1eef47ba..b95dd72e 100644 --- a/pydis_site/apps/api/models/bot/allow_deny_list.py +++ b/pydis_site/apps/api/models/bot/allow_deny_list.py @@ -24,6 +24,10 @@ class AllowDenyList(ModelTimestampMixin, ModelReprMixin, models.Model): content = models.TextField( help_text="The data to add to the allow or denylist." ) + comment = models.TextField( + help_text="Optional comment on this entry.", + null=True + ) class Meta: """Metaconfig for this model.""" diff --git a/pydis_site/apps/api/serializers.py b/pydis_site/apps/api/serializers.py index d532dd69..50f27f1d 100644 --- a/pydis_site/apps/api/serializers.py +++ b/pydis_site/apps/api/serializers.py @@ -111,7 +111,7 @@ class AllowDenyListSerializer(ModelSerializer): """Metadata defined for the Django REST Framework.""" model = AllowDenyList - fields = ('id', 'created_at', 'updated_at', 'type', 'allowed', 'content') + fields = ('id', 'created_at', 'updated_at', 'type', 'allowed', 'content', 'comment') class InfractionSerializer(ModelSerializer): diff --git a/pydis_site/apps/api/viewsets/bot/allow_deny_list.py b/pydis_site/apps/api/viewsets/bot/allow_deny_list.py index 2b9fd6a7..72cbc84c 100644 --- a/pydis_site/apps/api/viewsets/bot/allow_deny_list.py +++ b/pydis_site/apps/api/viewsets/bot/allow_deny_list.py @@ -24,6 +24,7 @@ class AllowDenyListViewSet(ModelViewSet): ... 'type': "file_format", ... 'allowed': 'true', ... 'content': ".jpeg", + ... 'comment': "Popular image format.", ... }, ... ... ... ] @@ -43,6 +44,7 @@ class AllowDenyListViewSet(ModelViewSet): ... 'type': "file_format", ... 'allowed': 'true', ... 'content': ".jpeg", + ... 'comment': "Popular image format.", ... } #### Status codes @@ -57,6 +59,7 @@ class AllowDenyListViewSet(ModelViewSet): ... 'type': str, ... 'allowed': bool, ... 'content': str, + ... 'comment': Optional[str], ... } #### Status codes -- cgit v1.2.3 From 74ef6fee279c451dac165d3792242ffdf35441ff Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Sun, 19 Jul 2020 12:45:14 +0200 Subject: Simplify AllowDenyListType names. GUILD_INVITE_ID -> GUILD_INVITE WORD_WATCHLIST -> FILTER_TOKEN --- pydis_site/apps/api/migrations/0057_create_new_allowdenylist_model.py | 4 ++-- pydis_site/apps/api/models/bot/allow_deny_list.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'pydis_site/apps/api/migrations') diff --git a/pydis_site/apps/api/migrations/0057_create_new_allowdenylist_model.py b/pydis_site/apps/api/migrations/0057_create_new_allowdenylist_model.py index d36acd70..ab8c5f3f 100644 --- a/pydis_site/apps/api/migrations/0057_create_new_allowdenylist_model.py +++ b/pydis_site/apps/api/migrations/0057_create_new_allowdenylist_model.py @@ -17,8 +17,8 @@ class Migration(migrations.Migration): ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('type', models.CharField( - choices=[('GUILD_INVITE_ID', 'Guild Invite Id'), ('FILE_FORMAT', 'File Format'), - ('DOMAIN_NAME', 'Domain Name'), ('WORD_WATCHLIST', 'Word Watchlist')], + choices=[('GUILD_INVITE', 'Guild Invite'), ('FILE_FORMAT', 'File Format'), + ('DOMAIN_NAME', 'Domain Name'), ('FILTER_TOKEN', 'Filter Token')], help_text='The type of allowlist this is on.', max_length=50)), ('allowed', models.BooleanField(help_text='Whether this item is on the allowlist or the denylist.')), ('content', models.TextField(help_text='The data to add to the allow or denylist.')), diff --git a/pydis_site/apps/api/models/bot/allow_deny_list.py b/pydis_site/apps/api/models/bot/allow_deny_list.py index b95dd72e..e1e09a78 100644 --- a/pydis_site/apps/api/models/bot/allow_deny_list.py +++ b/pydis_site/apps/api/models/bot/allow_deny_list.py @@ -8,10 +8,10 @@ class AllowDenyList(ModelTimestampMixin, ModelReprMixin, models.Model): AllowDenyListType = models.TextChoices( 'AllowDenyListType', - 'GUILD_INVITE_ID ' + 'GUILD_INVITE ' 'FILE_FORMAT ' 'DOMAIN_NAME ' - 'WORD_WATCHLIST ' + 'FILTER_TOKEN ' ) type = models.CharField( max_length=50, -- cgit v1.2.3 From 285c81bc13e996246ad9463951853fc12903d766 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Mon, 27 Jul 2020 09:53:38 +0200 Subject: Rename AllowDenyList to FilterList --- .../0057_create_new_allowdenylist_model.py | 33 ------- .../migrations/0057_create_new_filterlist_model.py | 33 +++++++ pydis_site/apps/api/models/__init__.py | 2 +- pydis_site/apps/api/models/bot/__init__.py | 2 +- pydis_site/apps/api/models/bot/allow_deny_list.py | 41 -------- pydis_site/apps/api/models/bot/filter_list.py | 41 ++++++++ pydis_site/apps/api/serializers.py | 10 +- pydis_site/apps/api/tests/test_allowlists.py | 107 --------------------- pydis_site/apps/api/tests/test_filterlists.py | 107 +++++++++++++++++++++ pydis_site/apps/api/urls.py | 7 +- pydis_site/apps/api/viewsets/__init__.py | 2 +- pydis_site/apps/api/viewsets/bot/__init__.py | 2 +- .../apps/api/viewsets/bot/allow_deny_list.py | 97 ------------------- pydis_site/apps/api/viewsets/bot/filter_list.py | 97 +++++++++++++++++++ 14 files changed, 290 insertions(+), 291 deletions(-) delete mode 100644 pydis_site/apps/api/migrations/0057_create_new_allowdenylist_model.py create mode 100644 pydis_site/apps/api/migrations/0057_create_new_filterlist_model.py delete mode 100644 pydis_site/apps/api/models/bot/allow_deny_list.py create mode 100644 pydis_site/apps/api/models/bot/filter_list.py delete mode 100644 pydis_site/apps/api/tests/test_allowlists.py create mode 100644 pydis_site/apps/api/tests/test_filterlists.py delete mode 100644 pydis_site/apps/api/viewsets/bot/allow_deny_list.py create mode 100644 pydis_site/apps/api/viewsets/bot/filter_list.py (limited to 'pydis_site/apps/api/migrations') diff --git a/pydis_site/apps/api/migrations/0057_create_new_allowdenylist_model.py b/pydis_site/apps/api/migrations/0057_create_new_allowdenylist_model.py deleted file mode 100644 index ab8c5f3f..00000000 --- a/pydis_site/apps/api/migrations/0057_create_new_allowdenylist_model.py +++ /dev/null @@ -1,33 +0,0 @@ -# Generated by Django 3.0.8 on 2020-07-15 11:23 - -from django.db import migrations, models -import pydis_site.apps.api.models.mixins - - -class Migration(migrations.Migration): - dependencies = [ - ('api', '0056_allow_blank_user_roles'), - ] - - operations = [ - migrations.CreateModel( - name='AllowDenyList', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('updated_at', models.DateTimeField(auto_now=True)), - ('type', models.CharField( - choices=[('GUILD_INVITE', 'Guild Invite'), ('FILE_FORMAT', 'File Format'), - ('DOMAIN_NAME', 'Domain Name'), ('FILTER_TOKEN', 'Filter Token')], - help_text='The type of allowlist this is on.', max_length=50)), - ('allowed', models.BooleanField(help_text='Whether this item is on the allowlist or the denylist.')), - ('content', models.TextField(help_text='The data to add to the allow or denylist.')), - ('comment', models.TextField(help_text="Optional comment on this entry.", null=True)), - ], - bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model), - ), - migrations.AddConstraint( - model_name='allowdenylist', - constraint=models.UniqueConstraint(fields=('content', 'type'), name='unique_allow_deny_list'), - ) - ] diff --git a/pydis_site/apps/api/migrations/0057_create_new_filterlist_model.py b/pydis_site/apps/api/migrations/0057_create_new_filterlist_model.py new file mode 100644 index 00000000..44025ee8 --- /dev/null +++ b/pydis_site/apps/api/migrations/0057_create_new_filterlist_model.py @@ -0,0 +1,33 @@ +# Generated by Django 3.0.8 on 2020-07-15 11:23 + +from django.db import migrations, models +import pydis_site.apps.api.models.mixins + + +class Migration(migrations.Migration): + dependencies = [ + ('api', '0056_allow_blank_user_roles'), + ] + + operations = [ + migrations.CreateModel( + name='FilterList', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('type', models.CharField( + choices=[('GUILD_INVITE', 'Guild Invite'), ('FILE_FORMAT', 'File Format'), + ('DOMAIN_NAME', 'Domain Name'), ('FILTER_TOKEN', 'Filter Token')], + help_text='The type of allowlist this is on.', max_length=50)), + ('allowed', models.BooleanField(help_text='Whether this item is on the allowlist or the denylist.')), + ('content', models.TextField(help_text='The data to add to the allow or denylist.')), + ('comment', models.TextField(help_text="Optional comment on this entry.", null=True)), + ], + bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model), + ), + migrations.AddConstraint( + model_name='filterlist', + constraint=models.UniqueConstraint(fields=('content', 'type'), name='unique_filter_list'), + ) + ] diff --git a/pydis_site/apps/api/models/__init__.py b/pydis_site/apps/api/models/__init__.py index 34973a8d..1d0ab7ea 100644 --- a/pydis_site/apps/api/models/__init__.py +++ b/pydis_site/apps/api/models/__init__.py @@ -1,6 +1,6 @@ # flake8: noqa from .bot import ( - AllowDenyList, + FilterList, BotSetting, DocumentationLink, DeletedMessage, diff --git a/pydis_site/apps/api/models/bot/__init__.py b/pydis_site/apps/api/models/bot/__init__.py index 1234b35a..efd98184 100644 --- a/pydis_site/apps/api/models/bot/__init__.py +++ b/pydis_site/apps/api/models/bot/__init__.py @@ -1,5 +1,5 @@ # flake8: noqa -from .allow_deny_list import AllowDenyList +from .filter_list import FilterList from .bot_setting import BotSetting from .deleted_message import DeletedMessage from .documentation_link import DocumentationLink diff --git a/pydis_site/apps/api/models/bot/allow_deny_list.py b/pydis_site/apps/api/models/bot/allow_deny_list.py deleted file mode 100644 index e1e09a78..00000000 --- a/pydis_site/apps/api/models/bot/allow_deny_list.py +++ /dev/null @@ -1,41 +0,0 @@ -from django.db import models - -from pydis_site.apps.api.models.mixins import ModelReprMixin, ModelTimestampMixin - - -class AllowDenyList(ModelTimestampMixin, ModelReprMixin, models.Model): - """An item that is either allowed or denied.""" - - AllowDenyListType = models.TextChoices( - 'AllowDenyListType', - 'GUILD_INVITE ' - 'FILE_FORMAT ' - 'DOMAIN_NAME ' - 'FILTER_TOKEN ' - ) - type = models.CharField( - max_length=50, - help_text="The type of allowlist this is on.", - choices=AllowDenyListType.choices, - ) - allowed = models.BooleanField( - help_text="Whether this item is on the allowlist or the denylist." - ) - content = models.TextField( - help_text="The data to add to the allow or denylist." - ) - comment = models.TextField( - help_text="Optional comment on this entry.", - null=True - ) - - class Meta: - """Metaconfig for this model.""" - - # This constraint ensures only one allow or denylist with the - # same content can exist. This means that we cannot have both an allow - # and a deny for the same item, and we cannot have duplicates of the - # same item. - constraints = [ - models.UniqueConstraint(fields=['content', 'type'], name='unique_allow_deny_list'), - ] diff --git a/pydis_site/apps/api/models/bot/filter_list.py b/pydis_site/apps/api/models/bot/filter_list.py new file mode 100644 index 00000000..d279e137 --- /dev/null +++ b/pydis_site/apps/api/models/bot/filter_list.py @@ -0,0 +1,41 @@ +from django.db import models + +from pydis_site.apps.api.models.mixins import ModelReprMixin, ModelTimestampMixin + + +class FilterList(ModelTimestampMixin, ModelReprMixin, models.Model): + """An item that is either allowed or denied.""" + + FilterListType = models.TextChoices( + 'FilterListType', + 'GUILD_INVITE ' + 'FILE_FORMAT ' + 'DOMAIN_NAME ' + 'FILTER_TOKEN ' + ) + type = models.CharField( + max_length=50, + help_text="The type of allowlist this is on.", + choices=FilterListType.choices, + ) + allowed = models.BooleanField( + help_text="Whether this item is on the allowlist or the denylist." + ) + content = models.TextField( + help_text="The data to add to the allow or denylist." + ) + comment = models.TextField( + help_text="Optional comment on this entry.", + null=True + ) + + class Meta: + """Metaconfig for this model.""" + + # This constraint ensures only one filterlist with the + # same content can exist. This means that we cannot have both an allow + # and a deny for the same item, and we cannot have duplicates of the + # same item. + constraints = [ + models.UniqueConstraint(fields=['content', 'type'], name='unique_filter_list'), + ] diff --git a/pydis_site/apps/api/serializers.py b/pydis_site/apps/api/serializers.py index 50f27f1d..8fd40da7 100644 --- a/pydis_site/apps/api/serializers.py +++ b/pydis_site/apps/api/serializers.py @@ -4,10 +4,10 @@ from rest_framework.validators import UniqueTogetherValidator from rest_framework_bulk import BulkSerializerMixin from .models import ( - AllowDenyList, BotSetting, DeletedMessage, DocumentationLink, + FilterList, Infraction, LogEntry, MessageDeletionContext, @@ -17,7 +17,7 @@ from .models import ( Reminder, Role, Tag, - User, + User ) @@ -104,13 +104,13 @@ class DocumentationLinkSerializer(ModelSerializer): fields = ('package', 'base_url', 'inventory_url') -class AllowDenyListSerializer(ModelSerializer): - """A class providing (de-)serialization of `AllowDenyList` instances.""" +class FilterListSerializer(ModelSerializer): + """A class providing (de-)serialization of `FilterList` instances.""" class Meta: """Metadata defined for the Django REST Framework.""" - model = AllowDenyList + model = FilterList fields = ('id', 'created_at', 'updated_at', 'type', 'allowed', 'content', 'comment') diff --git a/pydis_site/apps/api/tests/test_allowlists.py b/pydis_site/apps/api/tests/test_allowlists.py deleted file mode 100644 index fd8772d0..00000000 --- a/pydis_site/apps/api/tests/test_allowlists.py +++ /dev/null @@ -1,107 +0,0 @@ -from django_hosts.resolvers import reverse - -from pydis_site.apps.api.models import AllowDenyList -from pydis_site.apps.api.tests.base import APISubdomainTestCase - -URL = reverse('bot:allowdenylist-list', host='api') -JPEG_ALLOWLIST = { - "type": 'FILE_FORMAT', - "allowed": True, - "content": ".jpeg", -} -PNG_ALLOWLIST = { - "type": 'FILE_FORMAT', - "allowed": True, - "content": ".png", -} - - -class UnauthenticatedTests(APISubdomainTestCase): - def setUp(self): - super().setUp() - self.client.force_authenticate(user=None) - - def test_cannot_read_allowedlist_list(self): - response = self.client.get(URL) - - self.assertEqual(response.status_code, 401) - - -class EmptyDatabaseTests(APISubdomainTestCase): - def test_returns_empty_object(self): - response = self.client.get(URL) - - self.assertEqual(response.status_code, 200) - self.assertEqual(response.json(), []) - - -class FetchTests(APISubdomainTestCase): - @classmethod - def setUpTestData(cls): - cls.jpeg_format = AllowDenyList.objects.create(**JPEG_ALLOWLIST) - cls.png_format = AllowDenyList.objects.create(**PNG_ALLOWLIST) - - def test_returns_name_in_list(self): - response = self.client.get(URL) - - self.assertEqual(response.status_code, 200) - self.assertEqual(response.json()[0]["content"], self.jpeg_format.content) - self.assertEqual(response.json()[1]["content"], self.png_format.content) - - def test_returns_single_item_by_id(self): - response = self.client.get(f'{URL}/{self.jpeg_format.id}') - - self.assertEqual(response.status_code, 200) - self.assertEqual(response.json().get("content"), self.jpeg_format.content) - - def test_returns_allow_deny_list_types(self): - response = self.client.get(f'{URL}/get_types') - - self.assertEqual(response.status_code, 200) - for api_type, model_type in zip(response.json(), AllowDenyList.AllowDenyListType.choices): - self.assertEquals(api_type[0], model_type[0]) - self.assertEquals(api_type[1], model_type[1]) - - -class CreationTests(APISubdomainTestCase): - def test_returns_400_for_missing_params(self): - no_type_json = { - "allowed": True, - "content": ".jpeg" - } - no_allowed_json = { - "type": "FILE_FORMAT", - "content": ".jpeg" - } - no_content_json = { - "allowed": True, - "type": "FILE_FORMAT" - } - cases = [{}, no_type_json, no_allowed_json, no_content_json] - - for case in cases: - with self.subTest(case=case): - response = self.client.post(URL, data=case) - self.assertEqual(response.status_code, 400) - - def test_returns_201_for_successful_creation(self): - response = self.client.post(URL, data=JPEG_ALLOWLIST) - self.assertEqual(response.status_code, 201) - - -class DeletionTests(APISubdomainTestCase): - @classmethod - def setUpTestData(cls): - cls.jpeg_format = AllowDenyList.objects.create(**JPEG_ALLOWLIST) - cls.png_format = AllowDenyList.objects.create(**PNG_ALLOWLIST) - - def test_deleting_unknown_id_returns_404(self): - response = self.client.delete(f"{URL}/200") - self.assertEqual(response.status_code, 404) - - def test_deleting_known_id_returns_204(self): - response = self.client.delete(f"{URL}/{self.jpeg_format.id}") - self.assertEqual(response.status_code, 204) - - response = self.client.get(f"{URL}/{self.jpeg_format.id}") - self.assertNotIn(self.png_format.content, response.json()) diff --git a/pydis_site/apps/api/tests/test_filterlists.py b/pydis_site/apps/api/tests/test_filterlists.py new file mode 100644 index 00000000..b577c31c --- /dev/null +++ b/pydis_site/apps/api/tests/test_filterlists.py @@ -0,0 +1,107 @@ +from django_hosts.resolvers import reverse + +from pydis_site.apps.api.models import FilterList +from pydis_site.apps.api.tests.base import APISubdomainTestCase + +URL = reverse('bot:filterlist-list', host='api') +JPEG_ALLOWLIST = { + "type": 'FILE_FORMAT', + "allowed": True, + "content": ".jpeg", +} +PNG_ALLOWLIST = { + "type": 'FILE_FORMAT', + "allowed": True, + "content": ".png", +} + + +class UnauthenticatedTests(APISubdomainTestCase): + def setUp(self): + super().setUp() + self.client.force_authenticate(user=None) + + def test_cannot_read_allowedlist_list(self): + response = self.client.get(URL) + + self.assertEqual(response.status_code, 401) + + +class EmptyDatabaseTests(APISubdomainTestCase): + def test_returns_empty_object(self): + response = self.client.get(URL) + + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json(), []) + + +class FetchTests(APISubdomainTestCase): + @classmethod + def setUpTestData(cls): + cls.jpeg_format = FilterList.objects.create(**JPEG_ALLOWLIST) + cls.png_format = FilterList.objects.create(**PNG_ALLOWLIST) + + def test_returns_name_in_list(self): + response = self.client.get(URL) + + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json()[0]["content"], self.jpeg_format.content) + self.assertEqual(response.json()[1]["content"], self.png_format.content) + + def test_returns_single_item_by_id(self): + response = self.client.get(f'{URL}/{self.jpeg_format.id}') + + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json().get("content"), self.jpeg_format.content) + + def test_returns_filter_list_types(self): + response = self.client.get(f'{URL}/get-types') + + self.assertEqual(response.status_code, 200) + for api_type, model_type in zip(response.json(), FilterList.FilterListType.choices): + self.assertEquals(api_type[0], model_type[0]) + self.assertEquals(api_type[1], model_type[1]) + + +class CreationTests(APISubdomainTestCase): + def test_returns_400_for_missing_params(self): + no_type_json = { + "allowed": True, + "content": ".jpeg" + } + no_allowed_json = { + "type": "FILE_FORMAT", + "content": ".jpeg" + } + no_content_json = { + "allowed": True, + "type": "FILE_FORMAT" + } + cases = [{}, no_type_json, no_allowed_json, no_content_json] + + for case in cases: + with self.subTest(case=case): + response = self.client.post(URL, data=case) + self.assertEqual(response.status_code, 400) + + def test_returns_201_for_successful_creation(self): + response = self.client.post(URL, data=JPEG_ALLOWLIST) + self.assertEqual(response.status_code, 201) + + +class DeletionTests(APISubdomainTestCase): + @classmethod + def setUpTestData(cls): + cls.jpeg_format = FilterList.objects.create(**JPEG_ALLOWLIST) + cls.png_format = FilterList.objects.create(**PNG_ALLOWLIST) + + def test_deleting_unknown_id_returns_404(self): + response = self.client.delete(f"{URL}/200") + self.assertEqual(response.status_code, 404) + + def test_deleting_known_id_returns_204(self): + response = self.client.delete(f"{URL}/{self.jpeg_format.id}") + self.assertEqual(response.status_code, 204) + + response = self.client.get(f"{URL}/{self.jpeg_format.id}") + self.assertNotIn(self.png_format.content, response.json()) diff --git a/pydis_site/apps/api/urls.py b/pydis_site/apps/api/urls.py index b6ed2914..a4fd5b2e 100644 --- a/pydis_site/apps/api/urls.py +++ b/pydis_site/apps/api/urls.py @@ -3,10 +3,10 @@ from rest_framework.routers import DefaultRouter from .views import HealthcheckView, RulesView from .viewsets import ( - AllowDenyListViewSet, BotSettingViewSet, DeletedMessageViewSet, DocumentationLinkViewSet, + FilterListViewSet, InfractionViewSet, LogEntryViewSet, NominationViewSet, @@ -18,12 +18,11 @@ from .viewsets import ( UserViewSet ) - # http://www.django-rest-framework.org/api-guide/routers/#defaultrouter bot_router = DefaultRouter(trailing_slash=False) bot_router.register( - 'allow_deny_lists', - AllowDenyListViewSet + 'filter-lists', + FilterListViewSet ) bot_router.register( 'bot-settings', diff --git a/pydis_site/apps/api/viewsets/__init__.py b/pydis_site/apps/api/viewsets/__init__.py index eb7d5098..8699517e 100644 --- a/pydis_site/apps/api/viewsets/__init__.py +++ b/pydis_site/apps/api/viewsets/__init__.py @@ -1,6 +1,6 @@ # flake8: noqa from .bot import ( - AllowDenyListViewSet, + FilterListViewSet, BotSettingViewSet, DeletedMessageViewSet, DocumentationLinkViewSet, diff --git a/pydis_site/apps/api/viewsets/bot/__init__.py b/pydis_site/apps/api/viewsets/bot/__init__.py index 11638dd8..e64e3988 100644 --- a/pydis_site/apps/api/viewsets/bot/__init__.py +++ b/pydis_site/apps/api/viewsets/bot/__init__.py @@ -1,5 +1,5 @@ # flake8: noqa -from .allow_deny_list import AllowDenyListViewSet +from .filter_list import FilterListViewSet from .bot_setting import BotSettingViewSet from .deleted_message import DeletedMessageViewSet from .documentation_link import DocumentationLinkViewSet diff --git a/pydis_site/apps/api/viewsets/bot/allow_deny_list.py b/pydis_site/apps/api/viewsets/bot/allow_deny_list.py deleted file mode 100644 index 6ea8da56..00000000 --- a/pydis_site/apps/api/viewsets/bot/allow_deny_list.py +++ /dev/null @@ -1,97 +0,0 @@ -from rest_framework.decorators import action -from rest_framework.request import Request -from rest_framework.response import Response -from rest_framework.viewsets import ModelViewSet - -from pydis_site.apps.api.models.bot.allow_deny_list import AllowDenyList -from pydis_site.apps.api.serializers import AllowDenyListSerializer - - -class AllowDenyListViewSet(ModelViewSet): - """ - View providing CRUD operations on items allowed or denied by our bot. - - ## Routes - ### GET /bot/allow_deny_lists - Returns all allow and denylist items in the database. - - #### Response format - >>> [ - ... { - ... 'id': "2309268224", - ... 'created_at': "01-01-2020 ...", - ... 'updated_at': "01-01-2020 ...", - ... 'type': "file_format", - ... 'allowed': 'true', - ... 'content': ".jpeg", - ... 'comment': "Popular image format.", - ... }, - ... ... - ... ] - - #### Status codes - - 200: returned on success - - 401: returned if unauthenticated - - ### GET /bot/allow_deny_lists/ - Returns a specific AllowDenyList item from the database. - - #### Response format - >>> { - ... 'id': "2309268224", - ... 'created_at': "01-01-2020 ...", - ... 'updated_at': "01-01-2020 ...", - ... 'type': "file_format", - ... 'allowed': 'true', - ... 'content': ".jpeg", - ... 'comment': "Popular image format.", - ... } - - #### Status codes - - 200: returned on success - - 404: returned if the id was not found. - - ### GET /bot/allow_deny_lists/get_types - Returns a list of valid list types that can be used in POST requests. - - #### Response format - >>> [ - ... ["GUILD_INVITE","Guild Invite"], - ... ["FILE_FORMAT","File Format"], - ... ["DOMAIN_NAME","Domain Name"], - ... ["FILTER_TOKEN","Filter Token"] - ... ] - - #### Status codes - - 200: returned on success - - ### POST /bot/allow_deny_lists - Adds a single AllowDenyList item to the database. - - #### Request body - >>> { - ... 'type': str, - ... 'allowed': bool, - ... 'content': str, - ... 'comment': Optional[str], - ... } - - #### Status codes - - 201: returned on success - - 400: if one of the given fields is invalid - - ### DELETE /bot/allow_deny_lists/ - Deletes the AllowDenyList item with the given `id`. - - #### Status codes - - 204: returned on success - - 404: if a tag with the given `id` does not exist - """ - - serializer_class = AllowDenyListSerializer - queryset = AllowDenyList.objects.all() - - @action(detail=False, methods=["get"]) - def get_types(self, _: Request) -> Response: - """Get a list of all the types of AllowDenyLists we support.""" - return Response(AllowDenyList.AllowDenyListType.choices) diff --git a/pydis_site/apps/api/viewsets/bot/filter_list.py b/pydis_site/apps/api/viewsets/bot/filter_list.py new file mode 100644 index 00000000..2cb21ab9 --- /dev/null +++ b/pydis_site/apps/api/viewsets/bot/filter_list.py @@ -0,0 +1,97 @@ +from rest_framework.decorators import action +from rest_framework.request import Request +from rest_framework.response import Response +from rest_framework.viewsets import ModelViewSet + +from pydis_site.apps.api.models.bot.filter_list import FilterList +from pydis_site.apps.api.serializers import FilterListSerializer + + +class FilterListViewSet(ModelViewSet): + """ + View providing CRUD operations on items allowed or denied by our bot. + + ## Routes + ### GET /bot/filter-lists + Returns all filterlist items in the database. + + #### Response format + >>> [ + ... { + ... 'id': "2309268224", + ... 'created_at': "01-01-2020 ...", + ... 'updated_at': "01-01-2020 ...", + ... 'type': "file_format", + ... 'allowed': 'true', + ... 'content': ".jpeg", + ... 'comment': "Popular image format.", + ... }, + ... ... + ... ] + + #### Status codes + - 200: returned on success + - 401: returned if unauthenticated + + ### GET /bot/filter-lists/ + Returns a specific FilterList item from the database. + + #### Response format + >>> { + ... 'id': "2309268224", + ... 'created_at': "01-01-2020 ...", + ... 'updated_at': "01-01-2020 ...", + ... 'type': "file_format", + ... 'allowed': 'true', + ... 'content': ".jpeg", + ... 'comment': "Popular image format.", + ... } + + #### Status codes + - 200: returned on success + - 404: returned if the id was not found. + + ### GET /bot/filter-lists/get-types + Returns a list of valid list types that can be used in POST requests. + + #### Response format + >>> [ + ... ["GUILD_INVITE","Guild Invite"], + ... ["FILE_FORMAT","File Format"], + ... ["DOMAIN_NAME","Domain Name"], + ... ["FILTER_TOKEN","Filter Token"] + ... ] + + #### Status codes + - 200: returned on success + + ### POST /bot/filter-lists + Adds a single FilterList item to the database. + + #### Request body + >>> { + ... 'type': str, + ... 'allowed': bool, + ... 'content': str, + ... 'comment': Optional[str], + ... } + + #### Status codes + - 201: returned on success + - 400: if one of the given fields is invalid + + ### DELETE /bot/filter-lists/ + Deletes the FilterList item with the given `id`. + + #### Status codes + - 204: returned on success + - 404: if a tag with the given `id` does not exist + """ + + serializer_class = FilterListSerializer + queryset = FilterList.objects.all() + + @action(detail=False, url_path='get-types', methods=["get"]) + def get_types(self, _: Request) -> Response: + """Get a list of all the types of FilterLists we support.""" + return Response(FilterList.FilterListType.choices) -- cgit v1.2.3 From 0d7c95a80692f15c36b36152034e3bbe54e3c37c Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Wed, 29 Jul 2020 13:55:46 +0200 Subject: Handle unique validator in DRF, not Django. I was handling this in a Django vanilla kind of way, which was causing the constraint to return a 500 instead of a 400. This changes the approach to use the DRF way, and makes it return 400. It doesn't actually change the way anything behaves, other than returning the right status code. --- .../api/migrations/0057_create_new_filterlist_model.py | 4 ---- pydis_site/apps/api/models/bot/filter_list.py | 11 ----------- pydis_site/apps/api/serializers.py | 15 +++++++++++++++ 3 files changed, 15 insertions(+), 15 deletions(-) (limited to 'pydis_site/apps/api/migrations') diff --git a/pydis_site/apps/api/migrations/0057_create_new_filterlist_model.py b/pydis_site/apps/api/migrations/0057_create_new_filterlist_model.py index 44025ee8..b5398568 100644 --- a/pydis_site/apps/api/migrations/0057_create_new_filterlist_model.py +++ b/pydis_site/apps/api/migrations/0057_create_new_filterlist_model.py @@ -25,9 +25,5 @@ class Migration(migrations.Migration): ('comment', models.TextField(help_text="Optional comment on this entry.", null=True)), ], bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model), - ), - migrations.AddConstraint( - model_name='filterlist', - constraint=models.UniqueConstraint(fields=('content', 'type'), name='unique_filter_list'), ) ] diff --git a/pydis_site/apps/api/models/bot/filter_list.py b/pydis_site/apps/api/models/bot/filter_list.py index d279e137..5961aed7 100644 --- a/pydis_site/apps/api/models/bot/filter_list.py +++ b/pydis_site/apps/api/models/bot/filter_list.py @@ -28,14 +28,3 @@ class FilterList(ModelTimestampMixin, ModelReprMixin, models.Model): help_text="Optional comment on this entry.", null=True ) - - class Meta: - """Metaconfig for this model.""" - - # This constraint ensures only one filterlist with the - # same content can exist. This means that we cannot have both an allow - # and a deny for the same item, and we cannot have duplicates of the - # same item. - constraints = [ - models.UniqueConstraint(fields=['content', 'type'], name='unique_filter_list'), - ] diff --git a/pydis_site/apps/api/serializers.py b/pydis_site/apps/api/serializers.py index 8fd40da7..54bb8a5d 100644 --- a/pydis_site/apps/api/serializers.py +++ b/pydis_site/apps/api/serializers.py @@ -113,6 +113,21 @@ class FilterListSerializer(ModelSerializer): model = FilterList fields = ('id', 'created_at', 'updated_at', 'type', 'allowed', 'content', 'comment') + # This validator ensures only one filterlist with the + # same content can exist. This means that we cannot have both an allow + # and a deny for the same item, and we cannot have duplicates of the + # same item. + validators = [ + UniqueTogetherValidator( + queryset=FilterList.objects.all(), + fields=['content', 'type'], + message=( + "A filterlist for this item already exists. " + "Please note that you cannot add the same item to both allow and deny." + ) + ), + ] + class InfractionSerializer(ModelSerializer): """A class providing (de-)serialization of `Infraction` instances.""" -- cgit v1.2.3 From f15a345556b1902f3314d4edcc8ae3cf23da7a62 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Wed, 29 Jul 2020 18:19:15 +0200 Subject: Fix multiple leafs for migration graph. --- .../migrations/0057_create_new_filterlist_model.py | 29 ---------------------- .../migrations/0058_create_new_filterlist_model.py | 29 ++++++++++++++++++++++ 2 files changed, 29 insertions(+), 29 deletions(-) delete mode 100644 pydis_site/apps/api/migrations/0057_create_new_filterlist_model.py create mode 100644 pydis_site/apps/api/migrations/0058_create_new_filterlist_model.py (limited to 'pydis_site/apps/api/migrations') diff --git a/pydis_site/apps/api/migrations/0057_create_new_filterlist_model.py b/pydis_site/apps/api/migrations/0057_create_new_filterlist_model.py deleted file mode 100644 index b5398568..00000000 --- a/pydis_site/apps/api/migrations/0057_create_new_filterlist_model.py +++ /dev/null @@ -1,29 +0,0 @@ -# Generated by Django 3.0.8 on 2020-07-15 11:23 - -from django.db import migrations, models -import pydis_site.apps.api.models.mixins - - -class Migration(migrations.Migration): - dependencies = [ - ('api', '0056_allow_blank_user_roles'), - ] - - operations = [ - migrations.CreateModel( - name='FilterList', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('updated_at', models.DateTimeField(auto_now=True)), - ('type', models.CharField( - choices=[('GUILD_INVITE', 'Guild Invite'), ('FILE_FORMAT', 'File Format'), - ('DOMAIN_NAME', 'Domain Name'), ('FILTER_TOKEN', 'Filter Token')], - help_text='The type of allowlist this is on.', max_length=50)), - ('allowed', models.BooleanField(help_text='Whether this item is on the allowlist or the denylist.')), - ('content', models.TextField(help_text='The data to add to the allow or denylist.')), - ('comment', models.TextField(help_text="Optional comment on this entry.", null=True)), - ], - bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model), - ) - ] diff --git a/pydis_site/apps/api/migrations/0058_create_new_filterlist_model.py b/pydis_site/apps/api/migrations/0058_create_new_filterlist_model.py new file mode 100644 index 00000000..fffb8159 --- /dev/null +++ b/pydis_site/apps/api/migrations/0058_create_new_filterlist_model.py @@ -0,0 +1,29 @@ +# Generated by Django 3.0.8 on 2020-07-15 11:23 + +from django.db import migrations, models +import pydis_site.apps.api.models.mixins + + +class Migration(migrations.Migration): + dependencies = [ + ('api', '0057_merge_20200716_0751'), + ] + + operations = [ + migrations.CreateModel( + name='FilterList', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('type', models.CharField( + choices=[('GUILD_INVITE', 'Guild Invite'), ('FILE_FORMAT', 'File Format'), + ('DOMAIN_NAME', 'Domain Name'), ('FILTER_TOKEN', 'Filter Token')], + help_text='The type of allowlist this is on.', max_length=50)), + ('allowed', models.BooleanField(help_text='Whether this item is on the allowlist or the denylist.')), + ('content', models.TextField(help_text='The data to add to the allow or denylist.')), + ('comment', models.TextField(help_text="Optional comment on this entry.", null=True)), + ], + bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model), + ) + ] -- cgit v1.2.3 From 12f447bc9636382a623e6b30ae43ccc036c488b5 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Wed, 29 Jul 2020 19:07:03 +0200 Subject: Add a constraint in the DB model as well. This really should've been handled automatically by DRF, and in the future, it will be. But for now, we need to have constraints both on the serializer (to get status code 400), and on the model (to prevent direct database constraint violations). See https://github.com/encode/django-rest-framework/issues/7173 --- .../apps/api/migrations/0058_create_new_filterlist_model.py | 4 ++++ pydis_site/apps/api/models/bot/filter_list.py | 11 +++++++++++ 2 files changed, 15 insertions(+) (limited to 'pydis_site/apps/api/migrations') diff --git a/pydis_site/apps/api/migrations/0058_create_new_filterlist_model.py b/pydis_site/apps/api/migrations/0058_create_new_filterlist_model.py index fffb8159..aecfdad7 100644 --- a/pydis_site/apps/api/migrations/0058_create_new_filterlist_model.py +++ b/pydis_site/apps/api/migrations/0058_create_new_filterlist_model.py @@ -25,5 +25,9 @@ class Migration(migrations.Migration): ('comment', models.TextField(help_text="Optional comment on this entry.", null=True)), ], bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model), + ), + migrations.AddConstraint( + model_name='filterlist', + constraint=models.UniqueConstraint(fields=('content', 'type'), name='unique_filter_list') ) ] diff --git a/pydis_site/apps/api/models/bot/filter_list.py b/pydis_site/apps/api/models/bot/filter_list.py index 5961aed7..d279e137 100644 --- a/pydis_site/apps/api/models/bot/filter_list.py +++ b/pydis_site/apps/api/models/bot/filter_list.py @@ -28,3 +28,14 @@ class FilterList(ModelTimestampMixin, ModelReprMixin, models.Model): help_text="Optional comment on this entry.", null=True ) + + class Meta: + """Metaconfig for this model.""" + + # This constraint ensures only one filterlist with the + # same content can exist. This means that we cannot have both an allow + # and a deny for the same item, and we cannot have duplicates of the + # same item. + constraints = [ + models.UniqueConstraint(fields=['content', 'type'], name='unique_filter_list'), + ] -- cgit v1.2.3 From 82f2297fbdaa98e085655a9e66540980e3081210 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Thu, 30 Jul 2020 10:19:41 +0200 Subject: Provide callable, not return value. Co-authored-by: Sebastiaan Zeeff <33516116+SebastiaanZ@users.noreply.github.com> --- .../api/migrations/0025_allow_custom_inserted_at_infraction_field.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'pydis_site/apps/api/migrations') diff --git a/pydis_site/apps/api/migrations/0025_allow_custom_inserted_at_infraction_field.py b/pydis_site/apps/api/migrations/0025_allow_custom_inserted_at_infraction_field.py index 9589346d..c7fac012 100644 --- a/pydis_site/apps/api/migrations/0025_allow_custom_inserted_at_infraction_field.py +++ b/pydis_site/apps/api/migrations/0025_allow_custom_inserted_at_infraction_field.py @@ -14,6 +14,6 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='infraction', name='inserted_at', - field=models.DateTimeField(default=timezone.now(), help_text='The date and time of the creation of this infraction.'), + field=models.DateTimeField(default=timezone.now, help_text='The date and time of the creation of this infraction.'), ), ] -- cgit v1.2.3 From 322004e10f023f197e5fd07b0a87e0a10f45ea35 Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Thu, 30 Jul 2020 11:29:01 +0200 Subject: Add a migration for all the existing data. This will populate the table with domain names, guild invites, filter tokens and file formats. --- .../api/migrations/0059_populate_filterlists.py | 153 +++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 pydis_site/apps/api/migrations/0059_populate_filterlists.py (limited to 'pydis_site/apps/api/migrations') diff --git a/pydis_site/apps/api/migrations/0059_populate_filterlists.py b/pydis_site/apps/api/migrations/0059_populate_filterlists.py new file mode 100644 index 00000000..8c550191 --- /dev/null +++ b/pydis_site/apps/api/migrations/0059_populate_filterlists.py @@ -0,0 +1,153 @@ +from django.db import migrations + +guild_invite_whitelist = [ + ("discord.gg/python", "Python Discord", True), + ("discord.gg/4JJdJKb", "RLBot", True), + ("discord.gg/djPtTRJ", "Kivy", True), + ("discord.gg/QXyegWe", "Pyglet", True), + ("discord.gg/9XsucTT", "Panda3D", True), + ("discord.gg/AP3rq2k", "PyWeek", True), + ("discord.gg/vSPsP9t", "Microsoft Python", True), + ("discord.gg/bRCvFy9", "Discord.js Official", True), + ("discord.gg/9zT7NHP", "Programming Discussions", True), + ("discord.gg/ysd6M4r", "JetBrains Community", True), + ("discord.gg/4xJeCgy", "Raspberry Pie", True), + ("discord.gg/AStb3kZ", "Ren'Py", True), + ("discord.gg/t655QNV", "Python Discord: Emojis 1", True), + ("discord.gg/vRZPkqC", "Python Discord: Emojis 2", True), + ("discord.gg/jTtgWuy", "Django", True), + ("discord.gg/W9BypZF", "STEM", True), + ("discord.gg/dpy", "discord.py", True), + ("discord.gg/programming", "Programmers Hangout", True), + ("discord.gg/qhGUjGD", "SpeakJS", True), + ("discord.gg/eTbWSZj", "Functional Programming", True), + ("discord.gg/r8yreB6", "PyGame", True), + ("discord.gg/5UBnR3P", "Python Atlanta", True), + ("discord.gg/ccyrDKv", "C#", True), +] + +domain_name_blacklist = [ + ("pornhub.com", None, False), + ("liveleak.com", None, False), + ("grabify.link", None, False), + ("bmwforum.co", None, False), + ("leancoding.co", None, False), + ("spottyfly.com", None, False), + ("stopify.co", None, False), + ("yoütu.be", None, False), + ("discörd.com", None, False), + ("minecräft.com", None, False), + ("freegiftcards.co", None, False), + ("disçordapp.com", None, False), + ("fortnight.space", None, False), + ("fortnitechat.site", None, False), + ("joinmy.site", None, False), + ("curiouscat.club", None, False), + ("catsnthings.fun", None, False), + ("yourtube.site", None, False), + ("youtubeshort.watch", None, False), + ("catsnthing.com", None, False), + ("youtubeshort.pro", None, False), + ("canadianlumberjacks.online", None, False), + ("poweredbydialup.club", None, False), + ("poweredbydialup.online", None, False), + ("poweredbysecurity.org", None, False), + ("poweredbysecurity.online", None, False), + ("ssteam.site", None, False), + ("steamwalletgift.com", None, False), + ("discord.gift", None, False), + ("lmgtfy.com", None, False), +] + +filter_token_blacklist = [ + ("\bgoo+ks*\b", None, False), + ("\bky+s+\b", None, False), + ("\bki+ke+s*\b", None, False), + ("\bbeaner+s?\b", None, False), + ("\bcoo+ns*\b", None, False), + ("\bnig+lets*\b", None, False), + ("\bslant-eyes*\b", None, False), + ("\btowe?l-?head+s*\b", None, False), + ("\bchi*n+k+s*\b", None, False), + ("\bspick*s*\b", None, False), + ("\bkill* +(?:yo)?urself+\b", None, False), + ("\bjew+s*\b", None, False), + ("\bsuicide\b", None, False), + ("\brape\b", None, False), + ("\b(re+)tar+(d+|t+)(ed)?\b", None, False), + ("\bta+r+d+\b", None, False), + ("\bcunts*\b", None, False), + ("\btrann*y\b", None, False), + ("\bshemale\b", None, False), + ("fa+g+s*", None, False), + ("卐", None, False), + ("卍", None, False), + ("࿖", None, False), + ("࿕", None, False), + ("࿘", None, False), + ("࿗", None, False), + ("cuck(?!oo+)", None, False), + ("nigg+(?:e*r+|a+h*?|u+h+)s?", None, False), + ("fag+o+t+s*", None, False), +] + +file_format_whitelist = [ + (".3gp", None, True), + (".3g2", None, True), + (".avi", None, True), + (".bmp", None, True), + (".gif", None, True), + (".h264", None, True), + (".jpg", None, True), + (".jpeg", None, True), + (".m4v", None, True), + (".mkv", None, True), + (".mov", None, True), + (".mp4", None, True), + (".mpeg", None, True), + (".mpg", None, True), + (".png", None, True), + (".tiff", None, True), + (".wmv", None, True), + (".svg", None, True), + (".psd", "Photoshop", True), + (".ai", "Illustrator", True), + (".aep", "After Effects", True), + (".xcf", "GIMP", True), + (".mp3", None, True), + (".wav", None, True), + (".ogg", None, True), + (".webm", None, True), + (".webp", None, True), +] + +populate_data = { + "FILTER_TOKEN": filter_token_blacklist, + "DOMAIN_NAME": domain_name_blacklist, + "FILE_FORMAT": file_format_whitelist, + "GUILD_INVITE": guild_invite_whitelist, +} + + +class Migration(migrations.Migration): + dependencies = [("api", "0058_create_new_filterlist_model")] + + def populate_filterlists(app, _): + FilterList = app.get_model("api", "FilterList") + + for filterlist_type, metadata in populate_data.items(): + for content, comment, allowed in metadata: + FilterList.objects.create( + type=filterlist_type, + allowed=allowed, + content=content, + comment=comment, + ) + + def clear_filterlists(app, _): + FilterList = app.get_model("api", "FilterList") + FilterList.objects.all().delete() + + operations = [ + migrations.RunPython(populate_filterlists, clear_filterlists) + ] -- cgit v1.2.3 From e4cec0aeb2a791e622be8edd94fb4e82d150deab Mon Sep 17 00:00:00 2001 From: Leon Sandøy Date: Mon, 3 Aug 2020 12:53:39 +0200 Subject: Fix bad migration 0059 This had the wrong data for GUILD_INVITES - it had invites instead of Guild IDs. This new migration solves this problem. --- .../migrations/0060_populate_filterlists_fix.py | 85 ++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 pydis_site/apps/api/migrations/0060_populate_filterlists_fix.py (limited to 'pydis_site/apps/api/migrations') diff --git a/pydis_site/apps/api/migrations/0060_populate_filterlists_fix.py b/pydis_site/apps/api/migrations/0060_populate_filterlists_fix.py new file mode 100644 index 00000000..53846f02 --- /dev/null +++ b/pydis_site/apps/api/migrations/0060_populate_filterlists_fix.py @@ -0,0 +1,85 @@ +from django.db import migrations + +bad_guild_invite_whitelist = [ + ("discord.gg/python", "Python Discord", True), + ("discord.gg/4JJdJKb", "RLBot", True), + ("discord.gg/djPtTRJ", "Kivy", True), + ("discord.gg/QXyegWe", "Pyglet", True), + ("discord.gg/9XsucTT", "Panda3D", True), + ("discord.gg/AP3rq2k", "PyWeek", True), + ("discord.gg/vSPsP9t", "Microsoft Python", True), + ("discord.gg/bRCvFy9", "Discord.js Official", True), + ("discord.gg/9zT7NHP", "Programming Discussions", True), + ("discord.gg/ysd6M4r", "JetBrains Community", True), + ("discord.gg/4xJeCgy", "Raspberry Pie", True), + ("discord.gg/AStb3kZ", "Ren'Py", True), + ("discord.gg/t655QNV", "Python Discord: Emojis 1", True), + ("discord.gg/vRZPkqC", "Python Discord: Emojis 2", True), + ("discord.gg/jTtgWuy", "Django", True), + ("discord.gg/W9BypZF", "STEM", True), + ("discord.gg/dpy", "discord.py", True), + ("discord.gg/programming", "Programmers Hangout", True), + ("discord.gg/qhGUjGD", "SpeakJS", True), + ("discord.gg/eTbWSZj", "Functional Programming", True), + ("discord.gg/r8yreB6", "PyGame", True), + ("discord.gg/5UBnR3P", "Python Atlanta", True), + ("discord.gg/ccyrDKv", "C#", True), +] + +guild_invite_whitelist = [ + ("267624335836053506", "Python Discord", True), + ("348658686962696195", "RLBot", True), + ("423249981340778496", "Kivy", True), + ("438622377094414346", "Pyglet", True), + ("524691714909274162", "Panda3D", True), + ("666560367173828639", "PyWeek", True), + ("702724176489873509", "Microsoft Python", True), + ("222078108977594368", "Discord.js Official", True), + ("238666723824238602", "Programming Discussions", True), + ("433980600391696384", "JetBrains Community", True), + ("204621105720328193", "Raspberry Pie", True), + ("286633898581164032", "Ren'Py", True), + ("440186186024222721", "Python Discord: Emojis 1", True), + ("578587418123304970", "Python Discord: Emojis 2", True), + ("159039020565790721", "Django", True), + ("273944235143593984", "STEM", True), + ("336642139381301249", "discord.py", True), + ("244230771232079873", "Programmers Hangout", True), + ("239433591950540801", "SpeakJS", True), + ("280033776820813825", "Functional Programming", True), + ("349505959032389632", "PyGame", True), + ("488751051629920277", "Python Atlanta", True), + ("143867839282020352", "C#", True), +] + + +class Migration(migrations.Migration): + dependencies = [("api", "0059_populate_filterlists")] + + def fix_filterlist(app, _): + FilterList = app.get_model("api", "FilterList") + FilterList.objects.filter(type="GUILD_INVITE").delete() # Clear out the stuff added in 0059. + + for content, comment, allowed in guild_invite_whitelist: + FilterList.objects.create( + type="GUILD_INVITE", + allowed=allowed, + content=content, + comment=comment, + ) + + def restore_bad_filterlist(app, _): + FilterList = app.get_model("api", "FilterList") + FilterList.objects.filter(type="GUILD_INVITE").delete() + + for content, comment, allowed in bad_guild_invite_whitelist: + FilterList.objects.create( + type="GUILD_INVITE", + allowed=allowed, + content=content, + comment=comment, + ) + + operations = [ + migrations.RunPython(fix_filterlist, restore_bad_filterlist) + ] -- cgit v1.2.3