diff options
author | 2021-10-28 19:46:07 +0300 | |
---|---|---|
committer | 2021-12-18 18:02:12 +0100 | |
commit | 25da18321e82f0a3cd18923d59d86b59acec160d (patch) | |
tree | 25cc23ed547f6d51a3070f4ff852a3cee32ee0f8 /pydis_site/apps | |
parent | Remove old models from migration (diff) |
Update filters API to actually work
Diffstat (limited to 'pydis_site/apps')
-rw-r--r-- | pydis_site/apps/api/migrations/0070_new_filter_schema.py | 16 | ||||
-rw-r--r-- | pydis_site/apps/api/models/bot/filters.py | 129 | ||||
-rw-r--r-- | pydis_site/apps/api/models/mixins.py | 5 | ||||
-rw-r--r-- | pydis_site/apps/api/serializers.py | 58 |
4 files changed, 123 insertions, 85 deletions
diff --git a/pydis_site/apps/api/migrations/0070_new_filter_schema.py b/pydis_site/apps/api/migrations/0070_new_filter_schema.py index aa114ca1..a595bda2 100644 --- a/pydis_site/apps/api/migrations/0070_new_filter_schema.py +++ b/pydis_site/apps/api/migrations/0070_new_filter_schema.py @@ -54,10 +54,10 @@ def forward(apps: Apps, schema_editor: BaseDatabaseSchemaEditor) -> None: infraction_type=None, infraction_reason="", infraction_duration=None, - disallowed_channels=[], - disallowed_categories=[], - allowed_channels=[], - allowed_categories=[] + disallowed_channels=None, + disallowed_categories=None, + allowed_channels=None, + allowed_categories=None ) new_object.save() @@ -90,10 +90,10 @@ class Migration(migrations.Migration): ('infraction_type', models.CharField(choices=[('Note', 'Note'), ('Warn', 'Warn'), ('Mute', 'Mute'), ('Kick', 'Kick'), ('Ban', 'Ban')], help_text='The infraction to apply to this user.', max_length=4, null=True)), ('infraction_reason', models.CharField(help_text='The reason to give for the infraction.', max_length=1000)), ('infraction_duration', models.DurationField(help_text='The duration of the infraction. Null if permanent.', null=True)), - ('disallowed_channels', django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(), size=None)), - ('disallowed_categories', django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(), size=None)), - ('allowed_channels', django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(), size=None)), - ('allowed_categories', django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(), size=None)), + ('disallowed_channels', django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(), null=True, size=None)), + ('disallowed_categories', django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(), null=True, size=None)), + ('allowed_channels', django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(), null=True, size=None)), + ('allowed_categories', django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(), null=True, size=None)), ], ), migrations.CreateModel( diff --git a/pydis_site/apps/api/models/bot/filters.py b/pydis_site/apps/api/models/bot/filters.py index 365259e7..b9a081e6 100644 --- a/pydis_site/apps/api/models/bot/filters.py +++ b/pydis_site/apps/api/models/bot/filters.py @@ -1,4 +1,3 @@ -from abc import abstractmethod from typing import List from django.contrib.postgres.fields import ArrayField @@ -6,8 +5,6 @@ from django.core.exceptions import ValidationError from django.db import models from django.db.models import UniqueConstraint -from pydis_site.apps.api.models.mixins import AbstractModelMeta - class FilterListType(models.IntegerChoices): """Choice between allow or deny for a list type.""" @@ -43,40 +40,9 @@ def validate_ping_field(value_list: List[str]) -> None: raise ValidationError(f"{value!r} isn't a valid ping type.") -class FilterSettingsMixin(models.Model, metaclass=AbstractModelMeta): - """Mixin for settings of a filter list.""" - - @staticmethod - @abstractmethod - def allow_null() -> bool: - """Abstract property for allowing null values.""" +class FilterSettingsMixin(models.Model): + """Mixin for common settings of a filters and filter lists.""" - ping_type = ArrayField( - models.CharField(max_length=20), - validators=(validate_ping_field,), - help_text="Who to ping when this filter triggers.", - null=allow_null.__func__() - ) - filter_dm = models.BooleanField(help_text="Whether DMs should be filtered.", null=True) - dm_ping_type = ArrayField( - models.CharField(max_length=20), - validators=(validate_ping_field,), - help_text="Who to ping when this filter triggers on a DM.", - null=allow_null.__func__() - ) - delete_messages = models.BooleanField( - help_text="Whether this filter should delete messages triggering it.", - null=allow_null.__func__() - ) - bypass_roles = ArrayField( - models.BigIntegerField(), - help_text="Roles and users who can bypass this filter.", - null=allow_null.__func__() - ) - enabled = models.BooleanField( - help_text="Whether this filter is currently enabled.", - null=allow_null.__func__() - ) dm_content = models.CharField( max_length=1000, null=True, @@ -97,18 +63,6 @@ class FilterSettingsMixin(models.Model, metaclass=AbstractModelMeta): help_text="The duration of the infraction. Null if permanent." ) - # 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()) - class Meta: """Metaclass for settings mixin.""" @@ -123,11 +77,43 @@ class FilterList(FilterSettingsMixin): choices=FilterListType.choices, help_text="Whether this list is an allowlist or denylist" ) - - @staticmethod - def allow_null() -> bool: - """Do not allow null values for default settings.""" - return False + ping_type = ArrayField( + models.CharField(max_length=20), + validators=(validate_ping_field,), + help_text="Who to ping when this filter triggers.", + null=False + ) + filter_dm = models.BooleanField(help_text="Whether DMs should be filtered.", null=False) + dm_ping_type = ArrayField( + models.CharField(max_length=20), + validators=(validate_ping_field,), + help_text="Who to ping when this filter triggers on a DM.", + null=False + ) + delete_messages = models.BooleanField( + help_text="Whether this filter should delete messages triggering it.", + null=False + ) + bypass_roles = ArrayField( + models.BigIntegerField(), + help_text="Roles and users who can bypass this filter.", + null=False + ) + enabled = models.BooleanField( + help_text="Whether this filter is currently enabled.", + null=False + ) + # 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()) class Meta: """Constrain name and list_type unique.""" @@ -150,11 +136,38 @@ class Filter(FilterSettingsMixin): FilterList, models.CASCADE, related_name="filters", help_text="The filter list containing this filter." ) + ping_type = ArrayField( + models.CharField(max_length=20), + validators=(validate_ping_field,), + help_text="Who to ping when this filter triggers.", + null=True + ) + filter_dm = models.BooleanField(help_text="Whether DMs should be filtered.", null=True) + dm_ping_type = ArrayField( + models.CharField(max_length=20), + validators=(validate_ping_field,), + help_text="Who to ping when this filter triggers on a DM.", + null=True + ) + delete_messages = models.BooleanField( + help_text="Whether this filter should delete messages triggering it.", + null=True + ) + bypass_roles = ArrayField( + models.BigIntegerField(), + help_text="Roles and users who can bypass this filter.", + null=True + ) + enabled = models.BooleanField( + help_text="Whether this filter is currently enabled.", + null=True + ) + + # 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) def __str__(self) -> str: return f"Filter {self.content!r}" - - @staticmethod - def allow_null() -> bool: - """Allow null values for overrides.""" - return True diff --git a/pydis_site/apps/api/models/mixins.py b/pydis_site/apps/api/models/mixins.py index d32e6e72..5d75b78b 100644 --- a/pydis_site/apps/api/models/mixins.py +++ b/pydis_site/apps/api/models/mixins.py @@ -1,4 +1,3 @@ -from abc import ABCMeta from operator import itemgetter from django.db import models @@ -30,7 +29,3 @@ class ModelTimestampMixin(models.Model): """Metaconfig for the mixin.""" abstract = True - - -class AbstractModelMeta(ABCMeta, type(models.Model)): - """Metaclass for ABCModel class.""" diff --git a/pydis_site/apps/api/serializers.py b/pydis_site/apps/api/serializers.py index ff2bd929..4e92b3a0 100644 --- a/pydis_site/apps/api/serializers.py +++ b/pydis_site/apps/api/serializers.py @@ -113,6 +113,29 @@ class DocumentationLinkSerializer(ModelSerializer): fields = ('package', 'base_url', 'inventory_url') +ALWAYS_OPTIONAL_SETTINGS = ( + 'dm_content', + 'infraction_type', + 'infraction_reason', + 'infraction_duration', +) + +REQUIRED_FOR_FILTER_LIST_SETTINGS = ( + 'ping_type', + 'filter_dm', + 'dm_ping_type', + 'delete_messages', + 'bypass_roles', + 'enabled', + 'disallowed_channels', + 'disallowed_categories', + 'allowed_channels', + 'allowed_categories', +) + +SETTINGS_FIELDS = ALWAYS_OPTIONAL_SETTINGS + REQUIRED_FOR_FILTER_LIST_SETTINGS + + class FilterSerializer(ModelSerializer): """A class providing (de-)serialization of `Filter` instances.""" @@ -120,7 +143,16 @@ class FilterSerializer(ModelSerializer): """Metadata defined for the Django REST Framework.""" model = Filter - fields = ('id', 'content', 'description', 'additional_field', 'filter_list', 'override') + fields = ('id', 'content', 'description', 'additional_field', 'filter_list') + SETTINGS_FIELDS + extra_kwargs = { + 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}, + } class FilterListSerializer(ModelSerializer): @@ -132,18 +164,16 @@ class FilterListSerializer(ModelSerializer): """Metadata defined for the Django REST Framework.""" model = FilterList - fields = ( - 'id', - 'name', - 'list_type', - 'filters', - 'ping_type', - 'filter_dm', - 'dm_ping_type', - 'delete_messages', - 'bypass_roles', - '' - ) + fields = ('id', 'name', 'list_type', 'filters') + SETTINGS_FIELDS + extra_kwargs = { + 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}, + } # Ensure that we can only have one filter list with the same name and field validators = [ @@ -200,7 +230,7 @@ class InfractionSerializer(ModelSerializer): if hidden and infr_type in ('superstar', 'warning', 'voice_ban'): raise ValidationError({'hidden': [f'{infr_type} infractions cannot be hidden.']}) - if not hidden and infr_type in ('note', ): + if not hidden and infr_type in ('note',): raise ValidationError({'hidden': [f'{infr_type} infractions must be hidden.']}) return attrs |