aboutsummaryrefslogtreecommitdiffstats
path: root/pydis_site/apps/api
diff options
context:
space:
mode:
authorGravatar D0rs4n <[email protected]>2021-12-15 19:25:55 +0100
committerGravatar D0rs4n <[email protected]>2021-12-18 18:02:12 +0100
commit4c93b1b9b75cce4e45bdbdae608f4497372c2b56 (patch)
treee79f011a3407103bb255a6fbd04a4815964cc312 /pydis_site/apps/api
parentPatch 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/api')
-rw-r--r--pydis_site/apps/api/migrations/0075_prepare_filter_and_filterlist_for_new_filter_schema.py80
-rw-r--r--pydis_site/apps/api/models/bot/filters.py21
-rw-r--r--pydis_site/apps/api/serializers.py90
-rw-r--r--pydis_site/apps/api/viewsets/bot/filters.py94
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
+ ... }
... }
... }