diff options
author | 2021-12-15 19:25:55 +0100 | |
---|---|---|
committer | 2021-12-18 18:02:12 +0100 | |
commit | 4c93b1b9b75cce4e45bdbdae608f4497372c2b56 (patch) | |
tree | e79f011a3407103bb255a6fbd04a4815964cc312 /pydis_site/apps | |
parent | Patch Filter and FilterList Serializer validation logic and representation (diff) |
Prepare FilterList and Filter models, serializers for the new filter schema
- Rename channel scope fields:
- "allowed" -> "disabled"
eg.: "allowed_channels" -> "disabled_channels"
- Rename FilterLists` names:
filter_token -> tokens
domain_name -> domains
guild_invite -> invites
file_format -> formats
- Patch the docs and validators accordingly.
Diffstat (limited to 'pydis_site/apps')
4 files changed, 189 insertions, 96 deletions
diff --git a/pydis_site/apps/api/migrations/0075_prepare_filter_and_filterlist_for_new_filter_schema.py b/pydis_site/apps/api/migrations/0075_prepare_filter_and_filterlist_for_new_filter_schema.py new file mode 100644 index 00000000..30537e3d --- /dev/null +++ b/pydis_site/apps/api/migrations/0075_prepare_filter_and_filterlist_for_new_filter_schema.py @@ -0,0 +1,80 @@ +# Generated by Django 3.0.14 on 2021-12-11 23:14 +from django.apps.registry import Apps +from django.db import migrations, models +from django.db.backends.base.schema import BaseDatabaseSchemaEditor + + +def migrate_filterlist(apps: Apps, schema_editor: BaseDatabaseSchemaEditor) -> None: + FilterList = apps.get_model("api", "FilterList") + change_map = { + "filter_token": "tokens", + "domain_name": "domains", + "guild_invite": "invites", + "file_format": "formats" + } + for filter_list in FilterList.objects.all(): + if change_map.get(filter_list.name): + filter_list.name = change_map.get(filter_list.name) + filter_list.save() + + +def unmigrate_filterlist(apps: Apps, schema_editor: BaseDatabaseSchemaEditor) -> None: + FilterList = apps.get_model("api", "FilterList") + change_map = { + "tokens": "filter_token", + "domains": "domain_name", + "invites": "guild_invite", + "formats": "file_format" + } + for filter_list in FilterList.objects.all(): + if change_map.get(filter_list.name): + filter_list.name = change_map.get(filter_list.name) + filter_list.save() + + +class Migration(migrations.Migration): + dependencies = [ + ('api', '0074_merge_20211017_0822'), + ] + + operations = [ + migrations.RenameField( + model_name='filter', + old_name='allowed_categories', + new_name='disabled_categories', + ), + migrations.RenameField( + model_name='filter', + old_name='allowed_channels', + new_name='disabled_channels', + ), + migrations.RenameField( + model_name='filter', + old_name='disallowed_channels', + new_name='enabled_channels', + ), + migrations.RenameField( + model_name='filterlist', + old_name='allowed_categories', + new_name='disabled_categories', + ), + migrations.RenameField( + model_name='filterlist', + old_name='allowed_channels', + new_name='disabled_channels', + ), + migrations.RenameField( + model_name='filterlist', + old_name='disallowed_channels', + new_name='enabled_channels', + ), + migrations.RemoveField( + model_name='filterlist', + name='disallowed_categories', + ), + migrations.RemoveField( + model_name='filter', + name='disallowed_categories', + ), + migrations.RunPython(migrate_filterlist, unmigrate_filterlist) + ] diff --git a/pydis_site/apps/api/models/bot/filters.py b/pydis_site/apps/api/models/bot/filters.py index 3a1f3c6a..ae877685 100644 --- a/pydis_site/apps/api/models/bot/filters.py +++ b/pydis_site/apps/api/models/bot/filters.py @@ -99,14 +99,12 @@ class FilterList(FilterSettingsMixin): # Where a filter should apply. # # The resolution is done in the following order: - # - disallowed channels - # - disallowed categories - # - allowed categories - # - allowed channels - disallowed_channels = ArrayField(models.IntegerField()) - disallowed_categories = ArrayField(models.IntegerField()) - allowed_channels = ArrayField(models.IntegerField()) - allowed_categories = ArrayField(models.IntegerField()) + # - enabled_channels + # - disabled_categories + # - disabled_channels + enabled_channels = ArrayField(models.IntegerField()) + disabled_channels = ArrayField(models.IntegerField()) + disabled_categories = ArrayField(models.IntegerField()) class Meta: """Constrain name and list_type unique.""" @@ -157,10 +155,9 @@ class Filter(FilterSettingsMixin): ) # Check FilterList model for information about these properties. - disallowed_channels = ArrayField(models.IntegerField(), null=True) - disallowed_categories = ArrayField(models.IntegerField(), null=True) - allowed_channels = ArrayField(models.IntegerField(), null=True) - allowed_categories = ArrayField(models.IntegerField(), null=True) + enabled_channels = ArrayField(models.IntegerField(), null=True) + disabled_channels = ArrayField(models.IntegerField(), null=True) + disabled_categories = ArrayField(models.IntegerField(), null=True) def __str__(self) -> str: return f"Filter {self.content!r}" diff --git a/pydis_site/apps/api/serializers.py b/pydis_site/apps/api/serializers.py index cb8313ac..784f8160 100644 --- a/pydis_site/apps/api/serializers.py +++ b/pydis_site/apps/api/serializers.py @@ -127,18 +127,15 @@ REQUIRED_FOR_FILTER_LIST_SETTINGS = ( 'delete_messages', 'bypass_roles', 'enabled', - 'disallowed_channels', - 'disallowed_categories', - 'allowed_channels', - 'allowed_categories', + 'enabled_channels', + 'disabled_channels', + 'disabled_categories', ) # Required fields for custom JSON representation purposes BASE_FILTER_FIELDS = ('id', 'content', 'description', 'additional_field') BASE_FILTERLIST_FIELDS = ('id', 'name', 'list_type') BASE_SETTINGS_FIELDS = ( - "ping_type", - "dm_ping_type", "bypass_roles", "filter_dm", "enabled", @@ -146,11 +143,11 @@ BASE_SETTINGS_FIELDS = ( ) INFRACTION_FIELDS = ("infraction_type", "infraction_reason", "infraction_duration") CHANNEL_SCOPE_FIELDS = ( - "allowed_channels", - "allowed_categories", - "disallowed_channels", - "disallowed_categories" + "disabled_channels", + "disabled_categories", + "enabled_channels", ) +MENTIONS_FIELDS = ("ping_type", "dm_ping_type") SETTINGS_FIELDS = ALWAYS_OPTIONAL_SETTINGS + REQUIRED_FOR_FILTER_LIST_SETTINGS @@ -166,20 +163,17 @@ class FilterSerializer(ModelSerializer): raise ValidationError("Infraction type is required with infraction duration or reason") if ( - data.get('allowed_channels') is not None - and data.get('disallowed_channels') is not None + data.get('disabled_channels') is not None + and data.get('enabled_channels') is not None ): - channels_collection = data['allowed_channels'] + data['disallowed_channels'] + channels_collection = data['disabled_channels'] + data['enabled_channels'] if len(channels_collection) != len(set(channels_collection)): - raise ValidationError("Allowed and disallowed channels lists contain duplicates.") + raise ValidationError("Enabled and Disabled channels lists contain duplicates.") - if ( - data.get('allowed_categories') is not None - and data.get('disallowed_categories') is not None - ): - categories_collection = data['allowed_categories'] + data['disallowed_categories'] + if data.get('disabled_categories') is not None: + categories_collection = data['disabled_categories'] if len(categories_collection) != len(set(categories_collection)): - raise ValidationError("Allowed and disallowed categories lists contain duplicates.") + raise ValidationError("Disabled categories lists contain duplicates.") return data @@ -194,18 +188,20 @@ class FilterSerializer(ModelSerializer): field: {'required': False, 'allow_null': True} for field in SETTINGS_FIELDS } | { 'infraction_reason': {'allow_blank': True, 'allow_null': True, 'required': False}, - 'disallowed_channels': {'allow_empty': True, 'allow_null': True, 'required': False}, - 'disallowed_categories': {'allow_empty': True, 'allow_null': True, 'required': False}, - 'allowed_channels': {'allow_empty': True, 'allow_null': True, 'required': False}, - 'allowed_categories': {'allow_empty': True, 'allow_null': True, 'required': False}, + 'enabled_channels': {'allow_empty': True, 'allow_null': True, 'required': False}, + 'disabled_channels': {'allow_empty': True, 'allow_null': True, 'required': False}, + 'disabled_categories': {'allow_empty': True, 'allow_null': True, 'required': False}, } def to_representation(self, instance: Filter) -> dict: """ - Provides a custom JSON representation to the Filter Serializers. - That does not affect how the Serializer works in general. + This representation restructures how the Filter is represented. + It groups the Infraction, Channel and Mention related fields into their own separated group. + + Furthermore, it puts the fields that meant to represent Filter settings, + into a sub-field called `settings`. """ schema_settings = { "settings": @@ -214,6 +210,11 @@ class FilterSerializer(ModelSerializer): | { "channel_scope": {name: getattr(instance, name) for name in CHANNEL_SCOPE_FIELDS} + } | { + "mentions": + { + schema_field_name: getattr(instance, schema_field_name) + for schema_field_name in MENTIONS_FIELDS} } } @@ -236,20 +237,17 @@ class FilterListSerializer(ModelSerializer): raise ValidationError("Infraction type is required with infraction duration or reason") if ( - data.get('allowed_channels') is not None - and data.get('disallowed_channels') is not None + data.get('disabled_channels') is not None + and data.get('enabled_channels') is not None ): - channels_collection = data['allowed_channels'] + data['disallowed_channels'] + channels_collection = data['disabled_channels'] + data['enabled_channels'] if len(channels_collection) != len(set(channels_collection)): - raise ValidationError("Allowed and disallowed channels lists contain duplicates.") + raise ValidationError("Enabled and Disabled channels lists contain duplicates.") - if ( - data.get('allowed_categories') is not None - and data.get('disallowed_categories') is not None - ): - categories_collection = data['allowed_categories'] + data['disallowed_categories'] + if data.get('disabled_categories') is not None: + categories_collection = data['disabled_categories'] if len(categories_collection) != len(set(categories_collection)): - raise ValidationError("Allowed and disallowed categories lists contain duplicates.") + raise ValidationError("Disabled categories lists contain duplicates.") return data @@ -262,10 +260,9 @@ class FilterListSerializer(ModelSerializer): field: {'required': False, 'allow_null': True} for field in ALWAYS_OPTIONAL_SETTINGS } | { 'infraction_reason': {'allow_blank': True, 'allow_null': True, 'required': False}, - 'disallowed_channels': {'allow_empty': True}, - 'disallowed_categories': {'allow_empty': True}, - 'allowed_channels': {'allow_empty': True}, - 'allowed_categories': {'allow_empty': True}, + 'enabled_channels': {'allow_empty': True}, + 'disabled_channels': {'allow_empty': True}, + 'disabled_categories': {'allow_empty': True}, } # Ensure that we can only have one filter list with the same name and field @@ -283,7 +280,11 @@ class FilterListSerializer(ModelSerializer): """ Provides a custom JSON representation to the FilterList Serializers. - That does not affect how the Serializer works in general. + This representation restructures how the Filter is represented. + It groups the Infraction, Channel and Mention related fields into their own separated group. + + Furthermore, it puts the fields that meant to represent FilterList settings, + into a sub-field called `settings`. """ # Fetches the relating filters filters = [ @@ -301,7 +302,12 @@ class FilterListSerializer(ModelSerializer): {name: getattr(instance, name) for name in INFRACTION_FIELDS}} \ | { "channel_scope": - {name: getattr(instance, name) for name in CHANNEL_SCOPE_FIELDS}} + {name: getattr(instance, name) for name in CHANNEL_SCOPE_FIELDS}} | { + "mentions": { + schema_field_name: getattr(instance, schema_field_name) + for schema_field_name in MENTIONS_FIELDS + } + } return schema_base | {"settings": schema_settings_base | schema_settings_categories} diff --git a/pydis_site/apps/api/viewsets/bot/filters.py b/pydis_site/apps/api/viewsets/bot/filters.py index e8f3e3d9..20af079d 100644 --- a/pydis_site/apps/api/viewsets/bot/filters.py +++ b/pydis_site/apps/api/viewsets/bot/filters.py @@ -32,8 +32,6 @@ class FilterListViewSet(ModelViewSet): ... "description": "Python Discord", ... "additional_field": None, ... "settings": { - ... "ping_type": None, - ... "dm_ping_type": None ... "bypass_roles": None ... "filter_dm": None, ... "enabled": False @@ -44,11 +42,14 @@ class FilterListViewSet(ModelViewSet): ... "infraction_duration": None ... }, ... "channel_scope": { - ... "allowed_channels": None, - ... "allowed_categories": None, - ... "disallowed_channels": None, - ... "disallowed_categories": None + ... "disabled_channels": None, + ... "disabled_categories": None, + ... "enabled_channels": None ... } + ... "mentions": { + ... "ping_type": None + ... "dm_ping_type": None + ... } ... } ... ... }, @@ -72,13 +73,18 @@ class FilterListViewSet(ModelViewSet): ... "infraction_reason": "", ... "infraction_duration": None, ... } - ... "channel_scope": { - ... "disallowed_channels": [], - ... "disallowed_categories": [], - ... "allowed_channels": [], - ... "allowed_categories": [] - ... } - ... } + ... "channel_scope": { + ... "disabled_channels": None, + ... "disabled_categories": None, + ... "enabled_channels": None + ... } + ... "mentions": { + ... "ping_type": None + ... "dm_ping_type": None + ... } + ... }, + ... ... + ... ] #### Status codes - 200: returned on success @@ -88,8 +94,7 @@ class FilterListViewSet(ModelViewSet): Returns a specific FilterList item from the database. #### Response format - >>> - ... { + >>> { ... "id": 1, ... "name": "guild_invite", ... "list_type": 1, @@ -101,8 +106,6 @@ class FilterListViewSet(ModelViewSet): ... "description": "Python Discord", ... "additional_field": None, ... "settings": { - ... "ping_type": None, - ... "dm_ping_type": None ... "bypass_roles": None ... "filter_dm": None, ... "enabled": False @@ -113,15 +116,18 @@ class FilterListViewSet(ModelViewSet): ... "infraction_duration": None ... }, ... "channel_scope": { - ... "allowed_channels": None, - ... "allowed_categories": None, - ... "disallowed_channels": None, - ... "disallowed_categories": None + ... "disabled_channels": None, + ... "disabled_categories": None, + ... "enabled_channels": None ... } + ... "mentions": { + ... "ping_type": None + ... "dm_ping_type": None + ... } ... } ... ... }, - ... + ... ... ... ], ... "settings": { ... "ping_type": [ @@ -141,14 +147,16 @@ class FilterListViewSet(ModelViewSet): ... "infraction_reason": "", ... "infraction_duration": None, ... } - ... "channel_scope": { - ... "disallowed_channels": [], - ... "disallowed_categories": [], - ... "allowed_channels": [], - ... "allowed_categories": [] - ... } - ... } - ... } + ... "channel_scope": { + ... "disabled_channels": None, + ... "disabled_categories": None, + ... "enabled_channels": None + ... } + ... "mentions": { + ... "ping_type": None + ... "dm_ping_type": None + ... } + ... } #### Status codes - 200: returned on success @@ -183,8 +191,6 @@ class FilterViewSet(ModelViewSet): ... "description": "Python Discord", ... "additional_field": None, ... "settings": { - ... "ping_type": None, - ... "dm_ping_type": None ... "bypass_roles": None ... "filter_dm": None, ... "enabled": False @@ -195,11 +201,14 @@ class FilterViewSet(ModelViewSet): ... "infraction_duration": None ... }, ... "channel_scope": { - ... "allowed_channels": None, - ... "allowed_categories": None, - ... "disallowed_channels": None, - ... "disallowed_categories": None + ... "disabled_channels": None, + ... "disabled_categories": None, + ... "enabled_channels": None ... } + ... "mentions": { + ... "ping_type": None, + ... "dm_ping_type": None + ... } ... } ... }, ... ... @@ -220,8 +229,6 @@ class FilterViewSet(ModelViewSet): ... "description": "Python Discord", ... "additional_field": None, ... "settings": { - ... "ping_type": None, - ... "dm_ping_type": None ... "bypass_roles": None ... "filter_dm": None, ... "enabled": False @@ -232,11 +239,14 @@ class FilterViewSet(ModelViewSet): ... "infraction_duration": None ... }, ... "channel_scope": { - ... "allowed_channels": None, - ... "allowed_categories": None, - ... "disallowed_channels": None, - ... "disallowed_categories": None + ... "disabled_channels": None, + ... "disabled_categories": None, + ... "enabled_channels": None, ... } + ... "mentions": { + ... "ping_type": None + ... "dm_ping_type": None + ... } ... } ... } |