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 | |
| 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')
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 +    ...                       }      ...                    }      ... }  |