aboutsummaryrefslogtreecommitdiffstats
path: root/pydis_site/apps/api
diff options
context:
space:
mode:
Diffstat (limited to 'pydis_site/apps/api')
-rw-r--r--pydis_site/apps/api/__init__.py1
-rw-r--r--pydis_site/apps/api/apps.py10
-rw-r--r--pydis_site/apps/api/migrations/0059_populate_filterlists.py58
-rw-r--r--pydis_site/apps/api/migrations/0070_auto_20210519_0545.py23
-rw-r--r--pydis_site/apps/api/migrations/0072_merge_20210724_1354.py14
-rw-r--r--pydis_site/apps/api/migrations/0073_otn_allow_GT_and_LT.py19
-rw-r--r--pydis_site/apps/api/migrations/0074_merge_20211105_0518.py14
-rw-r--r--pydis_site/apps/api/migrations/0074_reminder_failures.py18
-rw-r--r--pydis_site/apps/api/migrations/0075_add_redirects_filter.py18
-rw-r--r--pydis_site/apps/api/migrations/0075_infraction_dm_sent.py18
-rw-r--r--pydis_site/apps/api/migrations/0076_merge_20211125_1941.py14
-rw-r--r--pydis_site/apps/api/migrations/0077_use_generic_jsonfield.py25
-rw-r--r--pydis_site/apps/api/migrations/0078_merge_20211213_0552.py14
-rw-r--r--pydis_site/apps/api/models/bot/bot_setting.py3
-rw-r--r--pydis_site/apps/api/models/bot/filter_list.py1
-rw-r--r--pydis_site/apps/api/models/bot/infraction.py4
-rw-r--r--pydis_site/apps/api/models/bot/message.py2
-rw-r--r--pydis_site/apps/api/models/bot/message_deletion_context.py4
-rw-r--r--pydis_site/apps/api/models/bot/metricity.py24
-rw-r--r--pydis_site/apps/api/models/bot/off_topic_channel_name.py9
-rw-r--r--pydis_site/apps/api/models/bot/reminder.py4
-rw-r--r--pydis_site/apps/api/models/utils.py3
-rw-r--r--pydis_site/apps/api/serializers.py50
-rw-r--r--pydis_site/apps/api/signals.py12
-rw-r--r--pydis_site/apps/api/tests/base.py27
-rw-r--r--pydis_site/apps/api/tests/test_deleted_messages.py16
-rw-r--r--pydis_site/apps/api/tests/test_documentation_links.py50
-rw-r--r--pydis_site/apps/api/tests/test_filterlists.py16
-rw-r--r--pydis_site/apps/api/tests/test_healthcheck.py8
-rw-r--r--pydis_site/apps/api/tests/test_infractions.py104
-rw-r--r--pydis_site/apps/api/tests/test_nominations.py86
-rw-r--r--pydis_site/apps/api/tests/test_off_topic_channel_names.py107
-rw-r--r--pydis_site/apps/api/tests/test_offensive_message.py26
-rw-r--r--pydis_site/apps/api/tests/test_reminders.py48
-rw-r--r--pydis_site/apps/api/tests/test_roles.py50
-rw-r--r--pydis_site/apps/api/tests/test_rules.py12
-rw-r--r--pydis_site/apps/api/tests/test_users.py91
-rw-r--r--pydis_site/apps/api/urls.py2
-rw-r--r--pydis_site/apps/api/viewsets/bot/filter_list.py3
-rw-r--r--pydis_site/apps/api/viewsets/bot/infraction.py9
-rw-r--r--pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py32
-rw-r--r--pydis_site/apps/api/viewsets/bot/reminder.py13
-rw-r--r--pydis_site/apps/api/viewsets/bot/user.py12
43 files changed, 682 insertions, 392 deletions
diff --git a/pydis_site/apps/api/__init__.py b/pydis_site/apps/api/__init__.py
index e69de29b..afa5b4d5 100644
--- a/pydis_site/apps/api/__init__.py
+++ b/pydis_site/apps/api/__init__.py
@@ -0,0 +1 @@
+default_app_config = 'pydis_site.apps.api.apps.ApiConfig'
diff --git a/pydis_site/apps/api/apps.py b/pydis_site/apps/api/apps.py
index 76810b2e..18eda9e3 100644
--- a/pydis_site/apps/api/apps.py
+++ b/pydis_site/apps/api/apps.py
@@ -4,4 +4,12 @@ from django.apps import AppConfig
class ApiConfig(AppConfig):
"""Django AppConfig for the API app."""
- name = 'api'
+ name = 'pydis_site.apps.api'
+
+ def ready(self) -> None:
+ """
+ Gets called as soon as the registry is fully populated.
+
+ https://docs.djangoproject.com/en/3.2/ref/applications/#django.apps.AppConfig.ready
+ """
+ import pydis_site.apps.api.signals # noqa: F401
diff --git a/pydis_site/apps/api/migrations/0059_populate_filterlists.py b/pydis_site/apps/api/migrations/0059_populate_filterlists.py
index 8c550191..273db3d1 100644
--- a/pydis_site/apps/api/migrations/0059_populate_filterlists.py
+++ b/pydis_site/apps/api/migrations/0059_populate_filterlists.py
@@ -60,35 +60,35 @@ domain_name_blacklist = [
]
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),
+ (r"\bgoo+ks*\b", None, False),
+ (r"\bky+s+\b", None, False),
+ (r"\bki+ke+s*\b", None, False),
+ (r"\bbeaner+s?\b", None, False),
+ (r"\bcoo+ns*\b", None, False),
+ (r"\bnig+lets*\b", None, False),
+ (r"\bslant-eyes*\b", None, False),
+ (r"\btowe?l-?head+s*\b", None, False),
+ (r"\bchi*n+k+s*\b", None, False),
+ (r"\bspick*s*\b", None, False),
+ (r"\bkill* +(?:yo)?urself+\b", None, False),
+ (r"\bjew+s*\b", None, False),
+ (r"\bsuicide\b", None, False),
+ (r"\brape\b", None, False),
+ (r"\b(re+)tar+(d+|t+)(ed)?\b", None, False),
+ (r"\bta+r+d+\b", None, False),
+ (r"\bcunts*\b", None, False),
+ (r"\btrann*y\b", None, False),
+ (r"\bshemale\b", None, False),
+ (r"fa+g+s*", None, False),
+ (r"卐", None, False),
+ (r"卍", None, False),
+ (r"࿖", None, False),
+ (r"࿕", None, False),
+ (r"࿘", None, False),
+ (r"࿗", None, False),
+ (r"cuck(?!oo+)", None, False),
+ (r"nigg+(?:e*r+|a+h*?|u+h+)s?", None, False),
+ (r"fag+o+t+s*", None, False),
]
file_format_whitelist = [
diff --git a/pydis_site/apps/api/migrations/0070_auto_20210519_0545.py b/pydis_site/apps/api/migrations/0070_auto_20210519_0545.py
new file mode 100644
index 00000000..dbd7ac91
--- /dev/null
+++ b/pydis_site/apps/api/migrations/0070_auto_20210519_0545.py
@@ -0,0 +1,23 @@
+# Generated by Django 3.0.14 on 2021-05-19 05:45
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('api', '0069_documentationlink_validators'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='offtopicchannelname',
+ name='active',
+ field=models.BooleanField(default=True, help_text='Whether or not this name should be considered for naming channels.'),
+ ),
+ migrations.AlterField(
+ model_name='offtopicchannelname',
+ name='used',
+ field=models.BooleanField(default=False, help_text='Whether or not this name has already been used during this rotation.'),
+ ),
+ ]
diff --git a/pydis_site/apps/api/migrations/0072_merge_20210724_1354.py b/pydis_site/apps/api/migrations/0072_merge_20210724_1354.py
new file mode 100644
index 00000000..f12efab5
--- /dev/null
+++ b/pydis_site/apps/api/migrations/0072_merge_20210724_1354.py
@@ -0,0 +1,14 @@
+# Generated by Django 3.0.14 on 2021-07-24 13:54
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('api', '0071_increase_message_content_4000'),
+ ('api', '0070_auto_20210519_0545'),
+ ]
+
+ operations = [
+ ]
diff --git a/pydis_site/apps/api/migrations/0073_otn_allow_GT_and_LT.py b/pydis_site/apps/api/migrations/0073_otn_allow_GT_and_LT.py
new file mode 100644
index 00000000..09ad13da
--- /dev/null
+++ b/pydis_site/apps/api/migrations/0073_otn_allow_GT_and_LT.py
@@ -0,0 +1,19 @@
+# Generated by Django 3.0.14 on 2021-09-27 20:38
+
+import django.core.validators
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('api', '0072_doc_allow_blank_base_url'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='offtopicchannelname',
+ name='name',
+ field=models.CharField(help_text='The actual channel name that will be used on our Discord server.', max_length=96, primary_key=True, serialize=False, validators=[django.core.validators.RegexValidator(regex="^[a-z0-9\\U0001d5a0-\\U0001d5b9-ǃ?’'<>]+$")]),
+ ),
+ ]
diff --git a/pydis_site/apps/api/migrations/0074_merge_20211105_0518.py b/pydis_site/apps/api/migrations/0074_merge_20211105_0518.py
new file mode 100644
index 00000000..ebf5ae15
--- /dev/null
+++ b/pydis_site/apps/api/migrations/0074_merge_20211105_0518.py
@@ -0,0 +1,14 @@
+# Generated by Django 3.0.14 on 2021-11-05 05:18
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('api', '0072_merge_20210724_1354'),
+ ('api', '0073_otn_allow_GT_and_LT'),
+ ]
+
+ operations = [
+ ]
diff --git a/pydis_site/apps/api/migrations/0074_reminder_failures.py b/pydis_site/apps/api/migrations/0074_reminder_failures.py
new file mode 100644
index 00000000..2860046e
--- /dev/null
+++ b/pydis_site/apps/api/migrations/0074_reminder_failures.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.0.14 on 2021-10-27 17:44
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('api', '0073_otn_allow_GT_and_LT'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='reminder',
+ name='failures',
+ field=models.IntegerField(default=0, help_text='Number of times we attempted to send the reminder and failed.'),
+ ),
+ ]
diff --git a/pydis_site/apps/api/migrations/0075_add_redirects_filter.py b/pydis_site/apps/api/migrations/0075_add_redirects_filter.py
new file mode 100644
index 00000000..23dc176f
--- /dev/null
+++ b/pydis_site/apps/api/migrations/0075_add_redirects_filter.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.0.14 on 2021-11-17 10:24
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('api', '0074_reminder_failures'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='filterlist',
+ name='type',
+ field=models.CharField(choices=[('GUILD_INVITE', 'Guild Invite'), ('FILE_FORMAT', 'File Format'), ('DOMAIN_NAME', 'Domain Name'), ('FILTER_TOKEN', 'Filter Token'), ('REDIRECT', 'Redirect')], help_text='The type of allowlist this is on.', max_length=50),
+ ),
+ ]
diff --git a/pydis_site/apps/api/migrations/0075_infraction_dm_sent.py b/pydis_site/apps/api/migrations/0075_infraction_dm_sent.py
new file mode 100644
index 00000000..c0ac709d
--- /dev/null
+++ b/pydis_site/apps/api/migrations/0075_infraction_dm_sent.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.0.14 on 2021-11-10 22:06
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('api', '0074_reminder_failures'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='infraction',
+ name='dm_sent',
+ field=models.BooleanField(help_text='Whether a DM was sent to the user when infraction was applied.', null=True),
+ ),
+ ]
diff --git a/pydis_site/apps/api/migrations/0076_merge_20211125_1941.py b/pydis_site/apps/api/migrations/0076_merge_20211125_1941.py
new file mode 100644
index 00000000..097d0a0c
--- /dev/null
+++ b/pydis_site/apps/api/migrations/0076_merge_20211125_1941.py
@@ -0,0 +1,14 @@
+# Generated by Django 3.0.14 on 2021-11-25 19:41
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('api', '0075_infraction_dm_sent'),
+ ('api', '0075_add_redirects_filter'),
+ ]
+
+ operations = [
+ ]
diff --git a/pydis_site/apps/api/migrations/0077_use_generic_jsonfield.py b/pydis_site/apps/api/migrations/0077_use_generic_jsonfield.py
new file mode 100644
index 00000000..9e8f2fb9
--- /dev/null
+++ b/pydis_site/apps/api/migrations/0077_use_generic_jsonfield.py
@@ -0,0 +1,25 @@
+# Generated by Django 3.1.13 on 2021-11-27 12:27
+
+import django.contrib.postgres.fields
+from django.db import migrations, models
+import pydis_site.apps.api.models.utils
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('api', '0076_merge_20211125_1941'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='botsetting',
+ name='data',
+ field=models.JSONField(help_text='The actual settings of this setting.'),
+ ),
+ migrations.AlterField(
+ model_name='deletedmessage',
+ name='embeds',
+ field=django.contrib.postgres.fields.ArrayField(base_field=models.JSONField(validators=[pydis_site.apps.api.models.utils.validate_embed]), blank=True, help_text='Embeds attached to this message.', size=None),
+ ),
+ ]
diff --git a/pydis_site/apps/api/migrations/0078_merge_20211213_0552.py b/pydis_site/apps/api/migrations/0078_merge_20211213_0552.py
new file mode 100644
index 00000000..5ce0e871
--- /dev/null
+++ b/pydis_site/apps/api/migrations/0078_merge_20211213_0552.py
@@ -0,0 +1,14 @@
+# Generated by Django 3.1.14 on 2021-12-13 05:52
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('api', '0077_use_generic_jsonfield'),
+ ('api', '0074_merge_20211105_0518'),
+ ]
+
+ operations = [
+ ]
diff --git a/pydis_site/apps/api/models/bot/bot_setting.py b/pydis_site/apps/api/models/bot/bot_setting.py
index 2a3944f8..1bcb1ae6 100644
--- a/pydis_site/apps/api/models/bot/bot_setting.py
+++ b/pydis_site/apps/api/models/bot/bot_setting.py
@@ -1,4 +1,3 @@
-from django.contrib.postgres import fields as pgfields
from django.core.exceptions import ValidationError
from django.db import models
@@ -24,6 +23,6 @@ class BotSetting(ModelReprMixin, models.Model):
max_length=50,
validators=(validate_bot_setting_name,)
)
- data = pgfields.JSONField(
+ data = models.JSONField(
help_text="The actual settings of this setting."
)
diff --git a/pydis_site/apps/api/models/bot/filter_list.py b/pydis_site/apps/api/models/bot/filter_list.py
index d279e137..d30f7213 100644
--- a/pydis_site/apps/api/models/bot/filter_list.py
+++ b/pydis_site/apps/api/models/bot/filter_list.py
@@ -12,6 +12,7 @@ class FilterList(ModelTimestampMixin, ModelReprMixin, models.Model):
'FILE_FORMAT '
'DOMAIN_NAME '
'FILTER_TOKEN '
+ 'REDIRECT '
)
type = models.CharField(
max_length=50,
diff --git a/pydis_site/apps/api/models/bot/infraction.py b/pydis_site/apps/api/models/bot/infraction.py
index 60c1e8dd..913631d4 100644
--- a/pydis_site/apps/api/models/bot/infraction.py
+++ b/pydis_site/apps/api/models/bot/infraction.py
@@ -57,6 +57,10 @@ class Infraction(ModelReprMixin, models.Model):
default=False,
help_text="Whether the infraction is a shadow infraction."
)
+ dm_sent = models.BooleanField(
+ null=True,
+ help_text="Whether a DM was sent to the user when infraction was applied."
+ )
def __str__(self):
"""Returns some info on the current infraction, for display purposes."""
diff --git a/pydis_site/apps/api/models/bot/message.py b/pydis_site/apps/api/models/bot/message.py
index 60e2a553..bab3368d 100644
--- a/pydis_site/apps/api/models/bot/message.py
+++ b/pydis_site/apps/api/models/bot/message.py
@@ -48,7 +48,7 @@ class Message(ModelReprMixin, models.Model):
blank=True
)
embeds = pgfields.ArrayField(
- pgfields.JSONField(
+ models.JSONField(
validators=(validate_embed,)
),
blank=True,
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 1410250a..25741266 100644
--- a/pydis_site/apps/api/models/bot/message_deletion_context.py
+++ b/pydis_site/apps/api/models/bot/message_deletion_context.py
@@ -1,5 +1,5 @@
from django.db import models
-from django_hosts.resolvers import reverse
+from django.urls import reverse
from pydis_site.apps.api.models.bot.user import User
from pydis_site.apps.api.models.mixins import ModelReprMixin
@@ -33,7 +33,7 @@ class MessageDeletionContext(ModelReprMixin, models.Model):
@property
def log_url(self) -> str:
"""Create the url for the deleted message logs."""
- return reverse('logs', host="staff", args=(self.id,))
+ return reverse('staff:logs', args=(self.id,))
class Meta:
"""Set the ordering for list views to newest first."""
diff --git a/pydis_site/apps/api/models/bot/metricity.py b/pydis_site/apps/api/models/bot/metricity.py
index 00076248..abd25ef0 100644
--- a/pydis_site/apps/api/models/bot/metricity.py
+++ b/pydis_site/apps/api/models/bot/metricity.py
@@ -4,13 +4,13 @@ from django.db import connections
BLOCK_INTERVAL = 10 * 60 # 10 minute blocks
-EXCLUDE_CHANNELS = [
+EXCLUDE_CHANNELS = (
"267659945086812160", # Bot commands
"607247579608121354" # SeasonalBot commands
-]
+)
-class NotFound(Exception): # noqa: N818
+class NotFoundError(Exception): # noqa: N818
"""Raised when an entity cannot be found."""
pass
@@ -37,7 +37,7 @@ class Metricity:
values = self.cursor.fetchone()
if not values:
- raise NotFound()
+ raise NotFoundError()
return dict(zip(columns, values))
@@ -46,19 +46,19 @@ class Metricity:
self.cursor.execute(
"""
SELECT
- COUNT(*)
+ COUNT(*)
FROM messages
WHERE
- author_id = '%s'
- AND NOT is_deleted
- AND NOT %s::varchar[] @> ARRAY[channel_id]
+ author_id = '%s'
+ AND NOT is_deleted
+ AND channel_id NOT IN %s
""",
[user_id, EXCLUDE_CHANNELS]
)
values = self.cursor.fetchone()
if not values:
- raise NotFound()
+ raise NotFoundError()
return values[0]
@@ -79,7 +79,7 @@ class Metricity:
WHERE
author_id='%s'
AND NOT is_deleted
- AND NOT %s::varchar[] @> ARRAY[channel_id]
+ AND channel_id NOT IN %s
GROUP BY interval
) block_query;
""",
@@ -88,7 +88,7 @@ class Metricity:
values = self.cursor.fetchone()
if not values:
- raise NotFound()
+ raise NotFoundError()
return values[0]
@@ -127,6 +127,6 @@ class Metricity:
values = self.cursor.fetchall()
if not values:
- raise NotFound()
+ raise NotFoundError()
return values
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 403c7465..e9fec114 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
@@ -11,14 +11,19 @@ class OffTopicChannelName(ModelReprMixin, models.Model):
primary_key=True,
max_length=96,
validators=(
- RegexValidator(regex=r"^[a-z0-9\U0001d5a0-\U0001d5b9-ǃ?’']+$"),
+ RegexValidator(regex=r"^[a-z0-9\U0001d5a0-\U0001d5b9-ǃ?’'<>]+$"),
),
help_text="The actual channel name that will be used on our Discord server."
)
used = models.BooleanField(
default=False,
- help_text="Whether or not this name has already been used during this rotation",
+ help_text="Whether or not this name has already been used during this rotation.",
+ )
+
+ active = models.BooleanField(
+ default=True,
+ help_text="Whether or not this name should be considered for naming channels."
)
def __str__(self):
diff --git a/pydis_site/apps/api/models/bot/reminder.py b/pydis_site/apps/api/models/bot/reminder.py
index 7d968a0e..173900ee 100644
--- a/pydis_site/apps/api/models/bot/reminder.py
+++ b/pydis_site/apps/api/models/bot/reminder.py
@@ -59,6 +59,10 @@ class Reminder(ModelReprMixin, models.Model):
blank=True,
help_text="IDs of roles or users to ping with the reminder."
)
+ failures = models.IntegerField(
+ default=0,
+ help_text="Number of times we attempted to send the reminder and failed."
+ )
def __str__(self):
"""Returns some info on the current reminder, for display purposes."""
diff --git a/pydis_site/apps/api/models/utils.py b/pydis_site/apps/api/models/utils.py
index 0e220a1d..859394d2 100644
--- a/pydis_site/apps/api/models/utils.py
+++ b/pydis_site/apps/api/models/utils.py
@@ -103,11 +103,10 @@ def validate_embed(embed: Any) -> None:
Example:
- >>> from django.contrib.postgres import fields as pgfields
>>> from django.db import models
>>> from pydis_site.apps.api.models.utils import validate_embed
>>> class MyMessage(models.Model):
- ... embed = pgfields.JSONField(
+ ... embed = models.JSONField(
... validators=(
... validate_embed,
... )
diff --git a/pydis_site/apps/api/serializers.py b/pydis_site/apps/api/serializers.py
index f47bedca..ac05ebd4 100644
--- a/pydis_site/apps/api/serializers.py
+++ b/pydis_site/apps/api/serializers.py
@@ -145,7 +145,16 @@ class InfractionSerializer(ModelSerializer):
model = Infraction
fields = (
- 'id', 'inserted_at', 'expires_at', 'active', 'user', 'actor', 'type', 'reason', 'hidden'
+ 'id',
+ 'inserted_at',
+ 'expires_at',
+ 'active',
+ 'user',
+ 'actor',
+ 'type',
+ 'reason',
+ 'hidden',
+ 'dm_sent'
)
validators = [
UniqueTogetherValidator(
@@ -200,25 +209,30 @@ class ExpandedInfractionSerializer(InfractionSerializer):
return ret
+class OffTopicChannelNameListSerializer(ListSerializer):
+ """Custom ListSerializer to override to_representation() when list views are triggered."""
+
+ def to_representation(self, objects: list[OffTopicChannelName]) -> list[str]:
+ """
+ Return a list with all `OffTopicChannelName`s in the database.
+
+ This returns the list of off topic channel names. We want to only return
+ the name attribute, hence it is unnecessary to create a nested dictionary.
+ Additionally, this allows off topic channel name routes to simply return an
+ array of names instead of objects, saving on bandwidth.
+ """
+ return [obj.name for obj in objects]
+
+
class OffTopicChannelNameSerializer(ModelSerializer):
"""A class providing (de-)serialization of `OffTopicChannelName` instances."""
class Meta:
"""Metadata defined for the Django REST Framework."""
+ list_serializer_class = OffTopicChannelNameListSerializer
model = OffTopicChannelName
- fields = ('name',)
-
- def to_representation(self, obj: OffTopicChannelName) -> str:
- """
- Return the representation of this `OffTopicChannelName`.
-
- This only returns the name of the off topic channel name. As the model
- only has a single attribute, it is unnecessary to create a nested dictionary.
- Additionally, this allows off topic channel name routes to simply return an
- array of names instead of objects, saving on bandwidth.
- """
- return obj.name
+ fields = ('name', 'used', 'active')
class ReminderSerializer(ModelSerializer):
@@ -231,7 +245,15 @@ class ReminderSerializer(ModelSerializer):
model = Reminder
fields = (
- 'active', 'author', 'jump_url', 'channel_id', 'content', 'expiration', 'id', 'mentions'
+ 'active',
+ 'author',
+ 'jump_url',
+ 'channel_id',
+ 'content',
+ 'expiration',
+ 'id',
+ 'mentions',
+ 'failures'
)
diff --git a/pydis_site/apps/api/signals.py b/pydis_site/apps/api/signals.py
new file mode 100644
index 00000000..5c26bfb6
--- /dev/null
+++ b/pydis_site/apps/api/signals.py
@@ -0,0 +1,12 @@
+from django.db.models.signals import post_delete
+from django.dispatch import receiver
+
+from pydis_site.apps.api.models.bot import Role, User
+
+
+@receiver(signal=post_delete, sender=Role)
+def delete_role_from_user(sender: Role, instance: Role, **kwargs) -> None:
+ """Unassigns the Role (instance) that is being deleted from every user that has it."""
+ for user in User.objects.filter(roles__contains=[instance.id]):
+ del user.roles[user.roles.index(instance.id)]
+ user.save()
diff --git a/pydis_site/apps/api/tests/base.py b/pydis_site/apps/api/tests/base.py
index 61c23b0f..c9f3cb7e 100644
--- a/pydis_site/apps/api/tests/base.py
+++ b/pydis_site/apps/api/tests/base.py
@@ -11,7 +11,7 @@ test_user, _created = User.objects.get_or_create(
)
-class APISubdomainTestCase(APITestCase):
+class AuthenticatedAPITestCase(APITestCase):
"""
Configures the test client.
@@ -24,14 +24,13 @@ class APISubdomainTestCase(APITestCase):
`self.client.force_authenticate(user=created_user)` to force authentication
through the created user.
- Using this performs the following niceties for you which ease writing tests:
- - setting the `HTTP_HOST` request header to `api.pythondiscord.local:8000`, and
+ Using this performs the following nicety for you which eases writing tests:
- forcing authentication for the test user.
If you don't want to force authentication (for example, to test a route's response
for an unauthenticated user), un-force authentication by using the following:
- >>> from pydis_site.apps.api.tests.base import APISubdomainTestCase
- >>> class UnauthedUserTestCase(APISubdomainTestCase):
+ >>> from pydis_site.apps.api.tests.base import AuthenticatedAPITestCase
+ >>> class UnauthedUserTestCase(AuthenticatedAPITestCase):
... def setUp(self):
... super().setUp()
... self.client.force_authentication(user=None)
@@ -42,30 +41,26 @@ class APISubdomainTestCase(APITestCase):
... resp = self.client.delete('/my-publicly-readable-endpoint/42')
... self.assertEqual(resp.status_code, 401)
- Make sure to include the `super().setUp(self)` call, otherwise, you may get
- status code 404 for some URLs due to the missing `HTTP_HOST` header.
-
## Example
Using this in a test case is rather straightforward:
- >>> from pydis_site.apps.api.tests.base import APISubdomainTestCase
- >>> class MyAPITestCase(APISubdomainTestCase):
+ >>> from pydis_site.apps.api.tests.base import AuthenticatedAPITestCase
+ >>> class MyAPITestCase(AuthenticatedAPITestCase):
... def test_that_it_works(self):
... response = self.client.get('/my-endpoint')
... self.assertEqual(response.status_code, 200)
- To reverse URLs of the API host, you need to use `django_hosts`:
+ To reverse URLs of the API host, you need to use `django.urls`:
- >>> from django_hosts.resolvers import reverse
- >>> from pydis_site.apps.api.tests.base import APISubdomainTestCase
- >>> class MyReversedTestCase(APISubdomainTestCase):
+ >>> from django.urls import reverse
+ >>> from pydis_site.apps.api.tests.base import AuthenticatedAPITestCase
+ >>> class MyReversedTestCase(AuthenticatedAPITestCase):
... def test_my_endpoint(self):
- ... url = reverse('user-detail', host='api')
+ ... url = reverse('api:user-detail')
... response = self.client.get(url)
... self.assertEqual(response.status_code, 200)
"""
def setUp(self):
super().setUp()
- self.client.defaults['HTTP_HOST'] = 'api.pythondiscord.local:8000'
self.client.force_authenticate(test_user)
diff --git a/pydis_site/apps/api/tests/test_deleted_messages.py b/pydis_site/apps/api/tests/test_deleted_messages.py
index 40450844..1eb535d8 100644
--- a/pydis_site/apps/api/tests/test_deleted_messages.py
+++ b/pydis_site/apps/api/tests/test_deleted_messages.py
@@ -1,13 +1,13 @@
from datetime import datetime
+from django.urls import reverse
from django.utils import timezone
-from django_hosts.resolvers import reverse
-from .base import APISubdomainTestCase
+from .base import AuthenticatedAPITestCase
from ..models import MessageDeletionContext, User
-class DeletedMessagesWithoutActorTests(APISubdomainTestCase):
+class DeletedMessagesWithoutActorTests(AuthenticatedAPITestCase):
@classmethod
def setUpTestData(cls):
cls.author = User.objects.create(
@@ -40,14 +40,14 @@ class DeletedMessagesWithoutActorTests(APISubdomainTestCase):
}
def test_accepts_valid_data(self):
- url = reverse('bot:messagedeletioncontext-list', host='api')
+ url = reverse('api:bot:messagedeletioncontext-list')
response = self.client.post(url, data=self.data)
self.assertEqual(response.status_code, 201)
[context] = MessageDeletionContext.objects.all()
self.assertIsNone(context.actor)
-class DeletedMessagesWithActorTests(APISubdomainTestCase):
+class DeletedMessagesWithActorTests(AuthenticatedAPITestCase):
@classmethod
def setUpTestData(cls):
cls.author = cls.actor = User.objects.create(
@@ -72,14 +72,14 @@ class DeletedMessagesWithActorTests(APISubdomainTestCase):
}
def test_accepts_valid_data_and_sets_actor(self):
- url = reverse('bot:messagedeletioncontext-list', host='api')
+ url = reverse('api:bot:messagedeletioncontext-list')
response = self.client.post(url, data=self.data)
self.assertEqual(response.status_code, 201)
[context] = MessageDeletionContext.objects.all()
self.assertEqual(context.actor.id, self.actor.id)
-class DeletedMessagesLogURLTests(APISubdomainTestCase):
+class DeletedMessagesLogURLTests(AuthenticatedAPITestCase):
@classmethod
def setUpTestData(cls):
cls.author = cls.actor = User.objects.create(
@@ -94,6 +94,6 @@ class DeletedMessagesLogURLTests(APISubdomainTestCase):
)
def test_valid_log_url(self):
- expected_url = reverse('logs', host="staff", args=(1,))
+ expected_url = reverse('staff:logs', args=(1,))
[context] = MessageDeletionContext.objects.all()
self.assertEqual(context.log_url, expected_url)
diff --git a/pydis_site/apps/api/tests/test_documentation_links.py b/pydis_site/apps/api/tests/test_documentation_links.py
index 39fb08f3..4e238cbb 100644
--- a/pydis_site/apps/api/tests/test_documentation_links.py
+++ b/pydis_site/apps/api/tests/test_documentation_links.py
@@ -1,61 +1,61 @@
-from django_hosts.resolvers import reverse
+from django.urls import reverse
-from .base import APISubdomainTestCase
+from .base import AuthenticatedAPITestCase
from ..models import DocumentationLink
-class UnauthedDocumentationLinkAPITests(APISubdomainTestCase):
+class UnauthedDocumentationLinkAPITests(AuthenticatedAPITestCase):
def setUp(self):
super().setUp()
self.client.force_authenticate(user=None)
def test_detail_lookup_returns_401(self):
- url = reverse('bot:documentationlink-detail', args=('whatever',), host='api')
+ url = reverse('api:bot:documentationlink-detail', args=('whatever',))
response = self.client.get(url)
self.assertEqual(response.status_code, 401)
def test_list_returns_401(self):
- url = reverse('bot:documentationlink-list', host='api')
+ url = reverse('api:bot:documentationlink-list')
response = self.client.get(url)
self.assertEqual(response.status_code, 401)
def test_create_returns_401(self):
- url = reverse('bot:documentationlink-list', host='api')
+ url = reverse('api:bot:documentationlink-list')
response = self.client.post(url, data={'hi': 'there'})
self.assertEqual(response.status_code, 401)
def test_delete_returns_401(self):
- url = reverse('bot:documentationlink-detail', args=('whatever',), host='api')
+ url = reverse('api:bot:documentationlink-detail', args=('whatever',))
response = self.client.delete(url)
self.assertEqual(response.status_code, 401)
-class EmptyDatabaseDocumentationLinkAPITests(APISubdomainTestCase):
+class EmptyDatabaseDocumentationLinkAPITests(AuthenticatedAPITestCase):
def test_detail_lookup_returns_404(self):
- url = reverse('bot:documentationlink-detail', args=('whatever',), host='api')
+ url = reverse('api:bot:documentationlink-detail', args=('whatever',))
response = self.client.get(url)
self.assertEqual(response.status_code, 404)
def test_list_all_returns_empty_list(self):
- url = reverse('bot:documentationlink-list', host='api')
+ url = reverse('api:bot:documentationlink-list')
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), [])
def test_delete_returns_404(self):
- url = reverse('bot:documentationlink-detail', args=('whatever',), host='api')
+ url = reverse('api:bot:documentationlink-detail', args=('whatever',))
response = self.client.delete(url)
self.assertEqual(response.status_code, 404)
-class DetailLookupDocumentationLinkAPITests(APISubdomainTestCase):
+class DetailLookupDocumentationLinkAPITests(AuthenticatedAPITestCase):
@classmethod
def setUpTestData(cls):
cls.doc_link = DocumentationLink.objects.create(
@@ -71,27 +71,27 @@ class DetailLookupDocumentationLinkAPITests(APISubdomainTestCase):
}
def test_detail_lookup_unknown_package_returns_404(self):
- url = reverse('bot:documentationlink-detail', args=('whatever',), host='api')
+ url = reverse('api:bot:documentationlink-detail', args=('whatever',))
response = self.client.get(url)
self.assertEqual(response.status_code, 404)
def test_detail_lookup_created_package_returns_package(self):
- url = reverse('bot:documentationlink-detail', args=(self.doc_link.package,), host='api')
+ url = reverse('api:bot:documentationlink-detail', args=(self.doc_link.package,))
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), self.doc_json)
def test_list_all_packages_shows_created_package(self):
- url = reverse('bot:documentationlink-list', host='api')
+ url = reverse('api:bot:documentationlink-list')
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), [self.doc_json])
def test_create_invalid_body_returns_400(self):
- url = reverse('bot:documentationlink-list', host='api')
+ url = reverse('api:bot:documentationlink-list')
response = self.client.post(url, data={'i': 'am', 'totally': 'valid'})
self.assertEqual(response.status_code, 400)
@@ -103,7 +103,7 @@ class DetailLookupDocumentationLinkAPITests(APISubdomainTestCase):
'inventory_url': 'totally an url'
}
- url = reverse('bot:documentationlink-list', host='api')
+ url = reverse('api:bot:documentationlink-list')
response = self.client.post(url, data=body)
self.assertEqual(response.status_code, 400)
@@ -114,13 +114,13 @@ class DetailLookupDocumentationLinkAPITests(APISubdomainTestCase):
with self.subTest(package_name=case):
body = self.doc_json.copy()
body['package'] = case
- url = reverse('bot:documentationlink-list', host='api')
+ url = reverse('api:bot:documentationlink-list')
response = self.client.post(url, data=body)
self.assertEqual(response.status_code, 400)
-class DocumentationLinkCreationTests(APISubdomainTestCase):
+class DocumentationLinkCreationTests(AuthenticatedAPITestCase):
def setUp(self):
super().setUp()
@@ -130,27 +130,27 @@ class DocumentationLinkCreationTests(APISubdomainTestCase):
'inventory_url': 'https://docs.example.com'
}
- url = reverse('bot:documentationlink-list', host='api')
+ url = reverse('api:bot:documentationlink-list')
response = self.client.post(url, data=self.body)
self.assertEqual(response.status_code, 201)
def test_package_in_full_list(self):
- url = reverse('bot:documentationlink-list', host='api')
+ url = reverse('api:bot:documentationlink-list')
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), [self.body])
def test_detail_lookup_works_with_package(self):
- url = reverse('bot:documentationlink-detail', args=(self.body['package'],), host='api')
+ url = reverse('api:bot:documentationlink-detail', args=(self.body['package'],))
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), self.body)
-class DocumentationLinkDeletionTests(APISubdomainTestCase):
+class DocumentationLinkDeletionTests(AuthenticatedAPITestCase):
@classmethod
def setUpTestData(cls):
cls.doc_link = DocumentationLink.objects.create(
@@ -160,13 +160,13 @@ class DocumentationLinkDeletionTests(APISubdomainTestCase):
)
def test_unknown_package_returns_404(self):
- url = reverse('bot:documentationlink-detail', args=('whatever',), host='api')
+ url = reverse('api:bot:documentationlink-detail', args=('whatever',))
response = self.client.delete(url)
self.assertEqual(response.status_code, 404)
def test_delete_known_package_returns_204(self):
- url = reverse('bot:documentationlink-detail', args=(self.doc_link.package,), host='api')
+ url = reverse('api:bot:documentationlink-detail', args=(self.doc_link.package,))
response = self.client.delete(url)
self.assertEqual(response.status_code, 204)
diff --git a/pydis_site/apps/api/tests/test_filterlists.py b/pydis_site/apps/api/tests/test_filterlists.py
index 188c0fff..5a5bca60 100644
--- a/pydis_site/apps/api/tests/test_filterlists.py
+++ b/pydis_site/apps/api/tests/test_filterlists.py
@@ -1,9 +1,9 @@
-from django_hosts.resolvers import reverse
+from django.urls import reverse
from pydis_site.apps.api.models import FilterList
-from pydis_site.apps.api.tests.base import APISubdomainTestCase
+from pydis_site.apps.api.tests.base import AuthenticatedAPITestCase
-URL = reverse('bot:filterlist-list', host='api')
+URL = reverse('api:bot:filterlist-list')
JPEG_ALLOWLIST = {
"type": 'FILE_FORMAT',
"allowed": True,
@@ -16,7 +16,7 @@ PNG_ALLOWLIST = {
}
-class UnauthenticatedTests(APISubdomainTestCase):
+class UnauthenticatedTests(AuthenticatedAPITestCase):
def setUp(self):
super().setUp()
self.client.force_authenticate(user=None)
@@ -27,7 +27,7 @@ class UnauthenticatedTests(APISubdomainTestCase):
self.assertEqual(response.status_code, 401)
-class EmptyDatabaseTests(APISubdomainTestCase):
+class EmptyDatabaseTests(AuthenticatedAPITestCase):
@classmethod
def setUpTestData(cls):
FilterList.objects.all().delete()
@@ -39,7 +39,7 @@ class EmptyDatabaseTests(APISubdomainTestCase):
self.assertEqual(response.json(), [])
-class FetchTests(APISubdomainTestCase):
+class FetchTests(AuthenticatedAPITestCase):
@classmethod
def setUpTestData(cls):
FilterList.objects.all().delete()
@@ -68,7 +68,7 @@ class FetchTests(APISubdomainTestCase):
self.assertEquals(api_type[1], model_type[1])
-class CreationTests(APISubdomainTestCase):
+class CreationTests(AuthenticatedAPITestCase):
@classmethod
def setUpTestData(cls):
FilterList.objects.all().delete()
@@ -103,7 +103,7 @@ class CreationTests(APISubdomainTestCase):
self.assertEqual(response.status_code, 400)
-class DeletionTests(APISubdomainTestCase):
+class DeletionTests(AuthenticatedAPITestCase):
@classmethod
def setUpTestData(cls):
FilterList.objects.all().delete()
diff --git a/pydis_site/apps/api/tests/test_healthcheck.py b/pydis_site/apps/api/tests/test_healthcheck.py
index b0fd71bf..650403ad 100644
--- a/pydis_site/apps/api/tests/test_healthcheck.py
+++ b/pydis_site/apps/api/tests/test_healthcheck.py
@@ -1,15 +1,15 @@
-from django_hosts.resolvers import reverse
+from django.urls import reverse
-from .base import APISubdomainTestCase
+from .base import AuthenticatedAPITestCase
-class UnauthedHealthcheckAPITests(APISubdomainTestCase):
+class UnauthedHealthcheckAPITests(AuthenticatedAPITestCase):
def setUp(self):
super().setUp()
self.client.force_authenticate(user=None)
def test_can_access_healthcheck_view(self):
- url = reverse('healthcheck', host='api')
+ url = reverse('api:healthcheck')
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
diff --git a/pydis_site/apps/api/tests/test_infractions.py b/pydis_site/apps/api/tests/test_infractions.py
index 9aae16c0..b3dd16ee 100644
--- a/pydis_site/apps/api/tests/test_infractions.py
+++ b/pydis_site/apps/api/tests/test_infractions.py
@@ -4,44 +4,44 @@ from unittest.mock import patch
from urllib.parse import quote
from django.db.utils import IntegrityError
-from django_hosts.resolvers import reverse
+from django.urls import reverse
-from .base import APISubdomainTestCase
+from .base import AuthenticatedAPITestCase
from ..models import Infraction, User
from ..serializers import InfractionSerializer
-class UnauthenticatedTests(APISubdomainTestCase):
+class UnauthenticatedTests(AuthenticatedAPITestCase):
def setUp(self):
super().setUp()
self.client.force_authenticate(user=None)
def test_detail_lookup_returns_401(self):
- url = reverse('bot:infraction-detail', args=(6,), host='api')
+ url = reverse('api:bot:infraction-detail', args=(6,))
response = self.client.get(url)
self.assertEqual(response.status_code, 401)
def test_list_returns_401(self):
- url = reverse('bot:infraction-list', host='api')
+ url = reverse('api:bot:infraction-list')
response = self.client.get(url)
self.assertEqual(response.status_code, 401)
def test_create_returns_401(self):
- url = reverse('bot:infraction-list', host='api')
+ url = reverse('api:bot:infraction-list')
response = self.client.post(url, data={'reason': 'Have a nice day.'})
self.assertEqual(response.status_code, 401)
def test_partial_update_returns_401(self):
- url = reverse('bot:infraction-detail', args=(6,), host='api')
+ url = reverse('api:bot:infraction-detail', args=(6,))
response = self.client.patch(url, data={'reason': 'Have a nice day.'})
self.assertEqual(response.status_code, 401)
-class InfractionTests(APISubdomainTestCase):
+class InfractionTests(AuthenticatedAPITestCase):
@classmethod
def setUpTestData(cls):
cls.user = User.objects.create(
@@ -92,7 +92,7 @@ class InfractionTests(APISubdomainTestCase):
def test_list_all(self):
"""Tests the list-view, which should be ordered by inserted_at (newest first)."""
- url = reverse('bot:infraction-list', host='api')
+ url = reverse('api:bot:infraction-list')
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
@@ -106,7 +106,7 @@ class InfractionTests(APISubdomainTestCase):
self.assertEqual(infractions[4]['id'], self.ban_hidden.id)
def test_filter_search(self):
- url = reverse('bot:infraction-list', host='api')
+ url = reverse('api:bot:infraction-list')
pattern = quote(r'^James(\s\w+){3},')
response = self.client.get(f'{url}?search={pattern}')
@@ -117,7 +117,7 @@ class InfractionTests(APISubdomainTestCase):
self.assertEqual(infractions[0]['id'], self.ban_inactive.id)
def test_filter_field(self):
- url = reverse('bot:infraction-list', host='api')
+ url = reverse('api:bot:infraction-list')
response = self.client.get(f'{url}?type=ban&hidden=true')
self.assertEqual(response.status_code, 200)
@@ -127,7 +127,7 @@ class InfractionTests(APISubdomainTestCase):
self.assertEqual(infractions[0]['id'], self.ban_hidden.id)
def test_filter_permanent_false(self):
- url = reverse('bot:infraction-list', host='api')
+ url = reverse('api:bot:infraction-list')
response = self.client.get(f'{url}?type=mute&permanent=false')
self.assertEqual(response.status_code, 200)
@@ -136,7 +136,7 @@ class InfractionTests(APISubdomainTestCase):
self.assertEqual(len(infractions), 0)
def test_filter_permanent_true(self):
- url = reverse('bot:infraction-list', host='api')
+ url = reverse('api:bot:infraction-list')
response = self.client.get(f'{url}?type=mute&permanent=true')
self.assertEqual(response.status_code, 200)
@@ -145,7 +145,7 @@ class InfractionTests(APISubdomainTestCase):
self.assertEqual(infractions[0]['id'], self.mute_permanent.id)
def test_filter_after(self):
- url = reverse('bot:infraction-list', host='api')
+ url = reverse('api:bot:infraction-list')
target_time = datetime.datetime.utcnow() + datetime.timedelta(hours=5)
response = self.client.get(f'{url}?type=superstar&expires_after={target_time.isoformat()}')
@@ -154,7 +154,7 @@ class InfractionTests(APISubdomainTestCase):
self.assertEqual(len(infractions), 0)
def test_filter_before(self):
- url = reverse('bot:infraction-list', host='api')
+ url = reverse('api:bot:infraction-list')
target_time = datetime.datetime.utcnow() + datetime.timedelta(hours=5)
response = self.client.get(f'{url}?type=superstar&expires_before={target_time.isoformat()}')
@@ -164,21 +164,21 @@ class InfractionTests(APISubdomainTestCase):
self.assertEqual(infractions[0]['id'], self.superstar_expires_soon.id)
def test_filter_after_invalid(self):
- url = reverse('bot:infraction-list', host='api')
+ url = reverse('api:bot:infraction-list')
response = self.client.get(f'{url}?expires_after=gibberish')
self.assertEqual(response.status_code, 400)
self.assertEqual(list(response.json())[0], "expires_after")
def test_filter_before_invalid(self):
- url = reverse('bot:infraction-list', host='api')
+ url = reverse('api:bot:infraction-list')
response = self.client.get(f'{url}?expires_before=000000000')
self.assertEqual(response.status_code, 400)
self.assertEqual(list(response.json())[0], "expires_before")
def test_after_before_before(self):
- url = reverse('bot:infraction-list', host='api')
+ url = reverse('api:bot:infraction-list')
target_time = datetime.datetime.utcnow() + datetime.timedelta(hours=4)
target_time_late = datetime.datetime.utcnow() + datetime.timedelta(hours=6)
response = self.client.get(
@@ -191,7 +191,7 @@ class InfractionTests(APISubdomainTestCase):
self.assertEqual(response.json()[0]["id"], self.superstar_expires_soon.id)
def test_after_after_before_invalid(self):
- url = reverse('bot:infraction-list', host='api')
+ url = reverse('api:bot:infraction-list')
target_time = datetime.datetime.utcnow() + datetime.timedelta(hours=5)
target_time_late = datetime.datetime.utcnow() + datetime.timedelta(hours=9)
response = self.client.get(
@@ -205,7 +205,7 @@ class InfractionTests(APISubdomainTestCase):
self.assertIn("expires_after", errors)
def test_permanent_after_invalid(self):
- url = reverse('bot:infraction-list', host='api')
+ url = reverse('api:bot:infraction-list')
target_time = datetime.datetime.utcnow() + datetime.timedelta(hours=5)
response = self.client.get(f'{url}?permanent=true&expires_after={target_time.isoformat()}')
@@ -214,7 +214,7 @@ class InfractionTests(APISubdomainTestCase):
self.assertEqual("permanent", errors[0])
def test_permanent_before_invalid(self):
- url = reverse('bot:infraction-list', host='api')
+ url = reverse('api:bot:infraction-list')
target_time = datetime.datetime.utcnow() + datetime.timedelta(hours=5)
response = self.client.get(f'{url}?permanent=true&expires_before={target_time.isoformat()}')
@@ -223,7 +223,7 @@ class InfractionTests(APISubdomainTestCase):
self.assertEqual("permanent", errors[0])
def test_nonpermanent_before(self):
- url = reverse('bot:infraction-list', host='api')
+ url = reverse('api:bot:infraction-list')
target_time = datetime.datetime.utcnow() + datetime.timedelta(hours=6)
response = self.client.get(
f'{url}?permanent=false&expires_before={target_time.isoformat()}'
@@ -234,7 +234,7 @@ class InfractionTests(APISubdomainTestCase):
self.assertEqual(response.json()[0]["id"], self.superstar_expires_soon.id)
def test_filter_manytypes(self):
- url = reverse('bot:infraction-list', host='api')
+ url = reverse('api:bot:infraction-list')
response = self.client.get(f'{url}?types=mute,ban')
self.assertEqual(response.status_code, 200)
@@ -242,7 +242,7 @@ class InfractionTests(APISubdomainTestCase):
self.assertEqual(len(infractions), 3)
def test_types_type_invalid(self):
- url = reverse('bot:infraction-list', host='api')
+ url = reverse('api:bot:infraction-list')
response = self.client.get(f'{url}?types=mute,ban&type=superstar')
self.assertEqual(response.status_code, 400)
@@ -250,7 +250,7 @@ class InfractionTests(APISubdomainTestCase):
self.assertEqual("types", errors[0])
def test_sort_expiresby(self):
- url = reverse('bot:infraction-list', host='api')
+ url = reverse('api:bot:infraction-list')
response = self.client.get(f'{url}?ordering=expires_at&permanent=false')
self.assertEqual(response.status_code, 200)
infractions = response.json()
@@ -261,34 +261,34 @@ class InfractionTests(APISubdomainTestCase):
self.assertEqual(infractions[2]['id'], self.ban_hidden.id)
def test_returns_empty_for_no_match(self):
- url = reverse('bot:infraction-list', host='api')
+ url = reverse('api:bot:infraction-list')
response = self.client.get(f'{url}?type=ban&search=poop')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.json()), 0)
def test_ignores_bad_filters(self):
- url = reverse('bot:infraction-list', host='api')
+ url = reverse('api:bot:infraction-list')
response = self.client.get(f'{url}?type=ban&hidden=maybe&foo=bar')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.json()), 2)
def test_retrieve_single_from_id(self):
- url = reverse('bot:infraction-detail', args=(self.ban_inactive.id,), host='api')
+ url = reverse('api:bot:infraction-detail', args=(self.ban_inactive.id,))
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json()['id'], self.ban_inactive.id)
def test_retrieve_returns_404_for_absent_id(self):
- url = reverse('bot:infraction-detail', args=(1337,), host='api')
+ url = reverse('api:bot:infraction-detail', args=(1337,))
response = self.client.get(url)
self.assertEqual(response.status_code, 404)
def test_partial_update(self):
- url = reverse('bot:infraction-detail', args=(self.ban_hidden.id,), host='api')
+ url = reverse('api:bot:infraction-detail', args=(self.ban_hidden.id,))
data = {
'expires_at': '4143-02-15T21:04:31+00:00',
'active': False,
@@ -313,7 +313,7 @@ class InfractionTests(APISubdomainTestCase):
self.assertEqual(infraction.hidden, self.ban_hidden.hidden)
def test_partial_update_returns_400_for_frozen_field(self):
- url = reverse('bot:infraction-detail', args=(self.ban_hidden.id,), host='api')
+ url = reverse('api:bot:infraction-detail', args=(self.ban_hidden.id,))
data = {'user': 6}
response = self.client.patch(url, data=data)
@@ -323,7 +323,7 @@ class InfractionTests(APISubdomainTestCase):
})
-class CreationTests(APISubdomainTestCase):
+class CreationTests(AuthenticatedAPITestCase):
@classmethod
def setUpTestData(cls):
cls.user = User.objects.create(
@@ -338,7 +338,7 @@ class CreationTests(APISubdomainTestCase):
)
def test_accepts_valid_data(self):
- url = reverse('bot:infraction-list', host='api')
+ url = reverse('api:bot:infraction-list')
data = {
'user': self.user.id,
'actor': self.user.id,
@@ -367,7 +367,7 @@ class CreationTests(APISubdomainTestCase):
self.assertEqual(infraction.active, True)
def test_returns_400_for_missing_user(self):
- url = reverse('bot:infraction-list', host='api')
+ url = reverse('api:bot:infraction-list')
data = {
'actor': self.user.id,
'type': 'kick',
@@ -381,7 +381,7 @@ class CreationTests(APISubdomainTestCase):
})
def test_returns_400_for_bad_user(self):
- url = reverse('bot:infraction-list', host='api')
+ url = reverse('api:bot:infraction-list')
data = {
'user': 1337,
'actor': self.user.id,
@@ -396,7 +396,7 @@ class CreationTests(APISubdomainTestCase):
})
def test_returns_400_for_bad_type(self):
- url = reverse('bot:infraction-list', host='api')
+ url = reverse('api:bot:infraction-list')
data = {
'user': self.user.id,
'actor': self.user.id,
@@ -411,7 +411,7 @@ class CreationTests(APISubdomainTestCase):
})
def test_returns_400_for_bad_expired_at_format(self):
- url = reverse('bot:infraction-list', host='api')
+ url = reverse('api:bot:infraction-list')
data = {
'user': self.user.id,
'actor': self.user.id,
@@ -430,7 +430,7 @@ class CreationTests(APISubdomainTestCase):
})
def test_returns_400_for_expiring_non_expirable_type(self):
- url = reverse('bot:infraction-list', host='api')
+ url = reverse('api:bot:infraction-list')
for infraction_type in ('kick', 'warning'):
data = {
@@ -448,7 +448,7 @@ class CreationTests(APISubdomainTestCase):
})
def test_returns_400_for_hidden_non_hideable_type(self):
- url = reverse('bot:infraction-list', host='api')
+ url = reverse('api:bot:infraction-list')
for infraction_type in ('superstar', 'warning'):
data = {
@@ -466,7 +466,7 @@ class CreationTests(APISubdomainTestCase):
})
def test_returns_400_for_non_hidden_required_hidden_type(self):
- url = reverse('bot:infraction-list', host='api')
+ url = reverse('api:bot:infraction-list')
data = {
'user': self.user.id,
@@ -484,7 +484,7 @@ class CreationTests(APISubdomainTestCase):
def test_returns_400_for_active_infraction_of_type_that_cannot_be_active(self):
"""Test if the API rejects active infractions for types that cannot be active."""
- url = reverse('bot:infraction-list', host='api')
+ url = reverse('api:bot:infraction-list')
restricted_types = (
('note', True),
('warning', False),
@@ -511,7 +511,7 @@ class CreationTests(APISubdomainTestCase):
def test_returns_400_for_second_active_infraction_of_the_same_type(self):
"""Test if the API rejects a second active infraction of the same type for a given user."""
- url = reverse('bot:infraction-list', host='api')
+ url = reverse('api:bot:infraction-list')
active_infraction_types = ('mute', 'ban', 'superstar')
for infraction_type in active_infraction_types:
@@ -550,7 +550,7 @@ class CreationTests(APISubdomainTestCase):
def test_returns_201_for_second_active_infraction_of_different_type(self):
"""Test if the API accepts a second active infraction of a different type than the first."""
- url = reverse('bot:infraction-list', host='api')
+ url = reverse('api:bot:infraction-list')
first_active_infraction = {
'user': self.user.id,
'actor': self.user.id,
@@ -677,7 +677,7 @@ class CreationTests(APISubdomainTestCase):
)
-class InfractionDeletionTests(APISubdomainTestCase):
+class InfractionDeletionTests(AuthenticatedAPITestCase):
@classmethod
def setUpTestData(cls):
cls.user = User.objects.create(
@@ -694,20 +694,20 @@ class InfractionDeletionTests(APISubdomainTestCase):
)
def test_delete_unknown_infraction_returns_404(self):
- url = reverse('bot:infraction-detail', args=('something',), host='api')
+ url = reverse('api:bot:infraction-detail', args=('something',))
response = self.client.delete(url)
self.assertEqual(response.status_code, 404)
def test_delete_known_infraction_returns_204(self):
- url = reverse('bot:infraction-detail', args=(self.warning.id,), host='api')
+ url = reverse('api:bot:infraction-detail', args=(self.warning.id,))
response = self.client.delete(url)
self.assertEqual(response.status_code, 204)
self.assertRaises(Infraction.DoesNotExist, Infraction.objects.get, id=self.warning.id)
-class ExpandedTests(APISubdomainTestCase):
+class ExpandedTests(AuthenticatedAPITestCase):
@classmethod
def setUpTestData(cls):
cls.user = User.objects.create(
@@ -735,7 +735,7 @@ class ExpandedTests(APISubdomainTestCase):
self.assertTrue(field in obj, msg=f'field "{field}" missing from {key}')
def test_list_expanded(self):
- url = reverse('bot:infraction-list-expanded', host='api')
+ url = reverse('api:bot:infraction-list-expanded')
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
@@ -747,7 +747,7 @@ class ExpandedTests(APISubdomainTestCase):
self.check_expanded_fields(infraction)
def test_create_expanded(self):
- url = reverse('bot:infraction-list-expanded', host='api')
+ url = reverse('api:bot:infraction-list-expanded')
data = {
'user': self.user.id,
'actor': self.user.id,
@@ -762,7 +762,7 @@ class ExpandedTests(APISubdomainTestCase):
self.check_expanded_fields(response.json())
def test_retrieve_expanded(self):
- url = reverse('bot:infraction-detail-expanded', args=(self.warning.id,), host='api')
+ url = reverse('api:bot:infraction-detail-expanded', args=(self.warning.id,))
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
@@ -772,7 +772,7 @@ class ExpandedTests(APISubdomainTestCase):
self.check_expanded_fields(infraction)
def test_partial_update_expanded(self):
- url = reverse('bot:infraction-detail-expanded', args=(self.kick.id,), host='api')
+ url = reverse('api:bot:infraction-detail-expanded', args=(self.kick.id,))
data = {'active': False}
response = self.client.patch(url, data=data)
@@ -783,7 +783,7 @@ class ExpandedTests(APISubdomainTestCase):
self.check_expanded_fields(response.json())
-class SerializerTests(APISubdomainTestCase):
+class SerializerTests(AuthenticatedAPITestCase):
@classmethod
def setUpTestData(cls):
cls.user = User.objects.create(
diff --git a/pydis_site/apps/api/tests/test_nominations.py b/pydis_site/apps/api/tests/test_nominations.py
index 9cefbd8f..62b2314c 100644
--- a/pydis_site/apps/api/tests/test_nominations.py
+++ b/pydis_site/apps/api/tests/test_nominations.py
@@ -1,12 +1,12 @@
from datetime import datetime as dt, timedelta, timezone
-from django_hosts.resolvers import reverse
+from django.urls import reverse
-from .base import APISubdomainTestCase
+from .base import AuthenticatedAPITestCase
from ..models import Nomination, NominationEntry, User
-class CreationTests(APISubdomainTestCase):
+class CreationTests(AuthenticatedAPITestCase):
@classmethod
def setUpTestData(cls):
cls.user = User.objects.create(
@@ -21,7 +21,7 @@ class CreationTests(APISubdomainTestCase):
)
def test_accepts_valid_data(self):
- url = reverse('bot:nomination-list', host='api')
+ url = reverse('api:bot:nomination-list')
data = {
'actor': self.user.id,
'reason': 'Joe Dart on Fender Bass',
@@ -46,7 +46,7 @@ class CreationTests(APISubdomainTestCase):
self.assertEqual(nomination.active, True)
def test_returns_200_on_second_active_nomination_by_different_user(self):
- url = reverse('bot:nomination-list', host='api')
+ url = reverse('api:bot:nomination-list')
first_data = {
'actor': self.user.id,
'reason': 'Joe Dart on Fender Bass',
@@ -65,7 +65,7 @@ class CreationTests(APISubdomainTestCase):
self.assertEqual(response2.status_code, 201)
def test_returns_400_on_second_active_nomination_by_existing_nominator(self):
- url = reverse('bot:nomination-list', host='api')
+ url = reverse('api:bot:nomination-list')
data = {
'actor': self.user.id,
'reason': 'Joe Dart on Fender Bass',
@@ -82,7 +82,7 @@ class CreationTests(APISubdomainTestCase):
})
def test_returns_400_for_missing_user(self):
- url = reverse('bot:nomination-list', host='api')
+ url = reverse('api:bot:nomination-list')
data = {
'actor': self.user.id,
'reason': 'Joe Dart on Fender Bass',
@@ -95,7 +95,7 @@ class CreationTests(APISubdomainTestCase):
})
def test_returns_400_for_missing_actor(self):
- url = reverse('bot:nomination-list', host='api')
+ url = reverse('api:bot:nomination-list')
data = {
'user': self.user.id,
'reason': 'Joe Dart on Fender Bass',
@@ -108,7 +108,7 @@ class CreationTests(APISubdomainTestCase):
})
def test_returns_201_for_missing_reason(self):
- url = reverse('bot:nomination-list', host='api')
+ url = reverse('api:bot:nomination-list')
data = {
'user': self.user.id,
'actor': self.user.id,
@@ -118,7 +118,7 @@ class CreationTests(APISubdomainTestCase):
self.assertEqual(response.status_code, 201)
def test_returns_400_for_bad_user(self):
- url = reverse('bot:nomination-list', host='api')
+ url = reverse('api:bot:nomination-list')
data = {
'user': 1024,
'reason': 'Joe Dart on Fender Bass',
@@ -132,7 +132,7 @@ class CreationTests(APISubdomainTestCase):
})
def test_returns_400_for_bad_actor(self):
- url = reverse('bot:nomination-list', host='api')
+ url = reverse('api:bot:nomination-list')
data = {
'user': self.user.id,
'reason': 'Joe Dart on Fender Bass',
@@ -146,7 +146,7 @@ class CreationTests(APISubdomainTestCase):
})
def test_returns_400_for_end_reason_at_creation(self):
- url = reverse('bot:nomination-list', host='api')
+ url = reverse('api:bot:nomination-list')
data = {
'user': self.user.id,
'reason': 'Joe Dart on Fender Bass',
@@ -161,7 +161,7 @@ class CreationTests(APISubdomainTestCase):
})
def test_returns_400_for_ended_at_at_creation(self):
- url = reverse('bot:nomination-list', host='api')
+ url = reverse('api:bot:nomination-list')
data = {
'user': self.user.id,
'reason': 'Joe Dart on Fender Bass',
@@ -176,7 +176,7 @@ class CreationTests(APISubdomainTestCase):
})
def test_returns_400_for_inserted_at_at_creation(self):
- url = reverse('bot:nomination-list', host='api')
+ url = reverse('api:bot:nomination-list')
data = {
'user': self.user.id,
'reason': 'Joe Dart on Fender Bass',
@@ -191,7 +191,7 @@ class CreationTests(APISubdomainTestCase):
})
def test_returns_400_for_active_at_creation(self):
- url = reverse('bot:nomination-list', host='api')
+ url = reverse('api:bot:nomination-list')
data = {
'user': self.user.id,
'reason': 'Joe Dart on Fender Bass',
@@ -206,7 +206,7 @@ class CreationTests(APISubdomainTestCase):
})
-class NominationTests(APISubdomainTestCase):
+class NominationTests(AuthenticatedAPITestCase):
@classmethod
def setUpTestData(cls):
cls.user = User.objects.create(
@@ -236,7 +236,7 @@ class NominationTests(APISubdomainTestCase):
)
def test_returns_200_update_reason_on_active_with_actor(self):
- url = reverse('bot:nomination-detail', args=(self.active_nomination.id,), host='api')
+ url = reverse('api:bot:nomination-detail', args=(self.active_nomination.id,))
data = {
'reason': "He's one funky duck",
'actor': self.user.id
@@ -252,7 +252,7 @@ class NominationTests(APISubdomainTestCase):
self.assertEqual(nomination_entry.reason, data['reason'])
def test_returns_400_on_frozen_field_update(self):
- url = reverse('bot:nomination-detail', args=(self.active_nomination.id,), host='api')
+ url = reverse('api:bot:nomination-detail', args=(self.active_nomination.id,))
data = {
'user': "Theo Katzman"
}
@@ -264,7 +264,7 @@ class NominationTests(APISubdomainTestCase):
})
def test_returns_400_update_end_reason_on_active(self):
- url = reverse('bot:nomination-detail', args=(self.active_nomination.id,), host='api')
+ url = reverse('api:bot:nomination-detail', args=(self.active_nomination.id,))
data = {
'end_reason': 'He started playing jazz'
}
@@ -276,7 +276,7 @@ class NominationTests(APISubdomainTestCase):
})
def test_returns_200_update_reason_on_inactive(self):
- url = reverse('bot:nomination-detail', args=(self.inactive_nomination.id,), host='api')
+ url = reverse('api:bot:nomination-detail', args=(self.inactive_nomination.id,))
data = {
'reason': "He's one funky duck",
'actor': self.user.id
@@ -292,7 +292,7 @@ class NominationTests(APISubdomainTestCase):
self.assertEqual(nomination_entry.reason, data['reason'])
def test_returns_200_update_end_reason_on_inactive(self):
- url = reverse('bot:nomination-detail', args=(self.inactive_nomination.id,), host='api')
+ url = reverse('api:bot:nomination-detail', args=(self.inactive_nomination.id,))
data = {
'end_reason': 'He started playing jazz'
}
@@ -305,9 +305,8 @@ class NominationTests(APISubdomainTestCase):
def test_returns_200_on_valid_end_nomination(self):
url = reverse(
- 'bot:nomination-detail',
+ 'api:bot:nomination-detail',
args=(self.active_nomination.id,),
- host='api'
)
data = {
'active': False,
@@ -328,9 +327,8 @@ class NominationTests(APISubdomainTestCase):
def test_returns_400_on_invalid_field_end_nomination(self):
url = reverse(
- 'bot:nomination-detail',
+ 'api:bot:nomination-detail',
args=(self.active_nomination.id,),
- host='api'
)
data = {
'active': False,
@@ -344,9 +342,8 @@ class NominationTests(APISubdomainTestCase):
def test_returns_400_on_missing_end_reason_end_nomination(self):
url = reverse(
- 'bot:nomination-detail',
+ 'api:bot:nomination-detail',
args=(self.active_nomination.id,),
- host='api'
)
data = {
'active': False,
@@ -360,9 +357,8 @@ class NominationTests(APISubdomainTestCase):
def test_returns_400_on_invalid_use_of_active(self):
url = reverse(
- 'bot:nomination-detail',
+ 'api:bot:nomination-detail',
args=(self.inactive_nomination.id,),
- host='api'
)
data = {
'active': False,
@@ -376,9 +372,8 @@ class NominationTests(APISubdomainTestCase):
def test_returns_404_on_get_unknown_nomination(self):
url = reverse(
- 'bot:nomination-detail',
+ 'api:bot:nomination-detail',
args=(9999,),
- host='api'
)
response = self.client.get(url, data={})
@@ -389,9 +384,8 @@ class NominationTests(APISubdomainTestCase):
def test_returns_404_on_patch_unknown_nomination(self):
url = reverse(
- 'bot:nomination-detail',
+ 'api:bot:nomination-detail',
args=(9999,),
- host='api'
)
response = self.client.patch(url, data={})
@@ -401,7 +395,7 @@ class NominationTests(APISubdomainTestCase):
})
def test_returns_405_on_list_put(self):
- url = reverse('bot:nomination-list', host='api')
+ url = reverse('api:bot:nomination-list')
response = self.client.put(url, data={})
self.assertEqual(response.status_code, 405)
@@ -410,7 +404,7 @@ class NominationTests(APISubdomainTestCase):
})
def test_returns_405_on_list_patch(self):
- url = reverse('bot:nomination-list', host='api')
+ url = reverse('api:bot:nomination-list')
response = self.client.patch(url, data={})
self.assertEqual(response.status_code, 405)
@@ -419,7 +413,7 @@ class NominationTests(APISubdomainTestCase):
})
def test_returns_405_on_list_delete(self):
- url = reverse('bot:nomination-list', host='api')
+ url = reverse('api:bot:nomination-list')
response = self.client.delete(url, data={})
self.assertEqual(response.status_code, 405)
@@ -428,7 +422,7 @@ class NominationTests(APISubdomainTestCase):
})
def test_returns_405_on_detail_post(self):
- url = reverse('bot:nomination-detail', args=(self.active_nomination.id,), host='api')
+ url = reverse('api:bot:nomination-detail', args=(self.active_nomination.id,))
response = self.client.post(url, data={})
self.assertEqual(response.status_code, 405)
@@ -437,7 +431,7 @@ class NominationTests(APISubdomainTestCase):
})
def test_returns_405_on_detail_delete(self):
- url = reverse('bot:nomination-detail', args=(self.active_nomination.id,), host='api')
+ url = reverse('api:bot:nomination-detail', args=(self.active_nomination.id,))
response = self.client.delete(url, data={})
self.assertEqual(response.status_code, 405)
@@ -446,7 +440,7 @@ class NominationTests(APISubdomainTestCase):
})
def test_returns_405_on_detail_put(self):
- url = reverse('bot:nomination-detail', args=(self.active_nomination.id,), host='api')
+ url = reverse('api:bot:nomination-detail', args=(self.active_nomination.id,))
response = self.client.put(url, data={})
self.assertEqual(response.status_code, 405)
@@ -455,7 +449,7 @@ class NominationTests(APISubdomainTestCase):
})
def test_filter_returns_0_objects_unknown_user__id(self):
- url = reverse('bot:nomination-list', host='api')
+ url = reverse('api:bot:nomination-list')
response = self.client.get(
url,
@@ -470,7 +464,7 @@ class NominationTests(APISubdomainTestCase):
self.assertEqual(len(infractions), 0)
def test_filter_returns_2_objects_for_testdata(self):
- url = reverse('bot:nomination-list', host='api')
+ url = reverse('api:bot:nomination-list')
response = self.client.get(
url,
@@ -485,14 +479,14 @@ class NominationTests(APISubdomainTestCase):
self.assertEqual(len(infractions), 2)
def test_patch_nomination_set_reviewed_of_active_nomination(self):
- url = reverse('api:nomination-detail', args=(self.active_nomination.id,), host='api')
+ url = reverse('api:bot:nomination-detail', args=(self.active_nomination.id,))
data = {'reviewed': True}
response = self.client.patch(url, data=data)
self.assertEqual(response.status_code, 200)
def test_patch_nomination_set_reviewed_of_inactive_nomination(self):
- url = reverse('api:nomination-detail', args=(self.inactive_nomination.id,), host='api')
+ url = reverse('api:bot:nomination-detail', args=(self.inactive_nomination.id,))
data = {'reviewed': True}
response = self.client.patch(url, data=data)
@@ -502,7 +496,7 @@ class NominationTests(APISubdomainTestCase):
})
def test_patch_nomination_set_reviewed_and_end(self):
- url = reverse('api:nomination-detail', args=(self.active_nomination.id,), host='api')
+ url = reverse('api:bot:nomination-detail', args=(self.active_nomination.id,))
data = {'reviewed': True, 'active': False, 'end_reason': "What?"}
response = self.client.patch(url, data=data)
@@ -512,7 +506,7 @@ class NominationTests(APISubdomainTestCase):
})
def test_modifying_reason_without_actor(self):
- url = reverse('api:nomination-detail', args=(self.active_nomination.id,), host='api')
+ url = reverse('api:bot:nomination-detail', args=(self.active_nomination.id,))
data = {'reason': 'That is my reason!'}
response = self.client.patch(url, data=data)
@@ -522,7 +516,7 @@ class NominationTests(APISubdomainTestCase):
})
def test_modifying_reason_with_unknown_actor(self):
- url = reverse('api:nomination-detail', args=(self.active_nomination.id,), host='api')
+ url = reverse('api:bot:nomination-detail', args=(self.active_nomination.id,))
data = {'reason': 'That is my reason!', 'actor': 90909090909090}
response = self.client.patch(url, data=data)
diff --git a/pydis_site/apps/api/tests/test_off_topic_channel_names.py b/pydis_site/apps/api/tests/test_off_topic_channel_names.py
index 3ab8b22d..2d273756 100644
--- a/pydis_site/apps/api/tests/test_off_topic_channel_names.py
+++ b/pydis_site/apps/api/tests/test_off_topic_channel_names.py
@@ -1,33 +1,33 @@
-from django_hosts.resolvers import reverse
+from django.urls import reverse
-from .base import APISubdomainTestCase
+from .base import AuthenticatedAPITestCase
from ..models import OffTopicChannelName
-class UnauthenticatedTests(APISubdomainTestCase):
+class UnauthenticatedTests(AuthenticatedAPITestCase):
def setUp(self):
super().setUp()
self.client.force_authenticate(user=None)
def test_cannot_read_off_topic_channel_name_list(self):
"""Return a 401 response when not authenticated."""
- url = reverse('bot:offtopicchannelname-list', host='api')
+ url = reverse('api:bot:offtopicchannelname-list')
response = self.client.get(url)
self.assertEqual(response.status_code, 401)
def test_cannot_read_off_topic_channel_name_list_with_random_item_param(self):
"""Return a 401 response when `random_items` provided and not authenticated."""
- url = reverse('bot:offtopicchannelname-list', host='api')
+ url = reverse('api:bot:offtopicchannelname-list')
response = self.client.get(f'{url}?random_items=no')
self.assertEqual(response.status_code, 401)
-class EmptyDatabaseTests(APISubdomainTestCase):
+class EmptyDatabaseTests(AuthenticatedAPITestCase):
def test_returns_empty_object(self):
"""Return empty list when no names in database."""
- url = reverse('bot:offtopicchannelname-list', host='api')
+ url = reverse('api:bot:offtopicchannelname-list')
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
@@ -35,7 +35,7 @@ class EmptyDatabaseTests(APISubdomainTestCase):
def test_returns_empty_list_with_get_all_param(self):
"""Return empty list when no names and `random_items` param provided."""
- url = reverse('bot:offtopicchannelname-list', host='api')
+ url = reverse('api:bot:offtopicchannelname-list')
response = self.client.get(f'{url}?random_items=5')
self.assertEqual(response.status_code, 200)
@@ -43,7 +43,7 @@ class EmptyDatabaseTests(APISubdomainTestCase):
def test_returns_400_for_bad_random_items_param(self):
"""Return error message when passing not integer as `random_items`."""
- url = reverse('bot:offtopicchannelname-list', host='api')
+ url = reverse('api:bot:offtopicchannelname-list')
response = self.client.get(f'{url}?random_items=totally-a-valid-integer')
self.assertEqual(response.status_code, 400)
@@ -53,7 +53,7 @@ class EmptyDatabaseTests(APISubdomainTestCase):
def test_returns_400_for_negative_random_items_param(self):
"""Return error message when passing negative int as `random_items`."""
- url = reverse('bot:offtopicchannelname-list', host='api')
+ url = reverse('api:bot:offtopicchannelname-list')
response = self.client.get(f'{url}?random_items=-5')
self.assertEqual(response.status_code, 400)
@@ -62,56 +62,89 @@ class EmptyDatabaseTests(APISubdomainTestCase):
})
-class ListTests(APISubdomainTestCase):
+class ListTests(AuthenticatedAPITestCase):
@classmethod
def setUpTestData(cls):
- cls.test_name = OffTopicChannelName.objects.create(name='lemons-lemonade-stand', used=False)
- cls.test_name_2 = OffTopicChannelName.objects.create(name='bbq-with-bisk', used=True)
+ cls.test_name = OffTopicChannelName.objects.create(
+ name='lemons-lemonade-stand', used=False, active=True
+ )
+ cls.test_name_2 = OffTopicChannelName.objects.create(
+ name='bbq-with-bisk', used=False, active=True
+ )
+ cls.test_name_3 = OffTopicChannelName.objects.create(
+ name="frozen-with-iceman", used=True, active=False
+ )
def test_returns_name_in_list(self):
"""Return all off-topic channel names."""
- url = reverse('bot:offtopicchannelname-list', host='api')
+ url = reverse('api:bot:offtopicchannelname-list')
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertEqual(
- response.json(),
- [
+ set(response.json()),
+ {
self.test_name.name,
- self.test_name_2.name
- ]
+ self.test_name_2.name,
+ self.test_name_3.name
+ }
)
- def test_returns_single_item_with_random_items_param_set_to_1(self):
+ def test_returns_two_items_with_random_items_param_set_to_2(self):
"""Return not-used name instead used."""
- url = reverse('bot:offtopicchannelname-list', host='api')
- response = self.client.get(f'{url}?random_items=1')
+ url = reverse('api:bot:offtopicchannelname-list')
+ response = self.client.get(f'{url}?random_items=2')
self.assertEqual(response.status_code, 200)
- self.assertEqual(len(response.json()), 1)
- self.assertEqual(response.json(), [self.test_name.name])
+ self.assertEqual(len(response.json()), 2)
+ self.assertEqual(set(response.json()), {self.test_name.name, self.test_name_2.name})
def test_running_out_of_names_with_random_parameter(self):
"""Reset names `used` parameter to `False` when running out of names."""
- url = reverse('bot:offtopicchannelname-list', host='api')
- response = self.client.get(f'{url}?random_items=2')
+ url = reverse('api:bot:offtopicchannelname-list')
+ response = self.client.get(f'{url}?random_items=3')
self.assertEqual(response.status_code, 200)
- self.assertEqual(response.json(), [self.test_name.name, self.test_name_2.name])
+ self.assertEqual(
+ set(response.json()),
+ {self.test_name.name, self.test_name_2.name, self.test_name_3.name}
+ )
+
+ def test_returns_inactive_ot_names(self):
+ """Return inactive off topic names."""
+ url = reverse('api:bot:offtopicchannelname-list')
+ response = self.client.get(f"{url}?active=false")
+
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(
+ response.json(),
+ [self.test_name_3.name]
+ )
+
+ def test_returns_active_ot_names(self):
+ """Return active off topic names."""
+ url = reverse('api:bot:offtopicchannelname-list')
+ response = self.client.get(f"{url}?active=true")
+
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(
+ set(response.json()),
+ {self.test_name.name, self.test_name_2.name}
+ )
-class CreationTests(APISubdomainTestCase):
+class CreationTests(AuthenticatedAPITestCase):
def setUp(self):
super().setUp()
- url = reverse('bot:offtopicchannelname-list', host='api')
+ url = reverse('api:bot:offtopicchannelname-list')
self.name = "abcdefghijklmnopqrstuvwxyz-0123456789"
response = self.client.post(f'{url}?name={self.name}')
self.assertEqual(response.status_code, 201)
def test_returns_201_for_unicode_chars(self):
"""Accept all valid characters."""
- url = reverse('bot:offtopicchannelname-list', host='api')
+ url = reverse('api:bot:offtopicchannelname-list')
names = (
'𝖠𝖡𝖢𝖣𝖤𝖥𝖦𝖧𝖨𝖩𝖪𝖫𝖬𝖭𝖮𝖯𝖰𝖱𝖲𝖳𝖴𝖵𝖶𝖷𝖸𝖹',
'ǃ?’',
@@ -123,7 +156,7 @@ class CreationTests(APISubdomainTestCase):
def test_returns_400_for_missing_name_param(self):
"""Return error message when name not provided."""
- url = reverse('bot:offtopicchannelname-list', host='api')
+ url = reverse('api:bot:offtopicchannelname-list')
response = self.client.post(url)
self.assertEqual(response.status_code, 400)
self.assertEqual(response.json(), {
@@ -132,7 +165,7 @@ class CreationTests(APISubdomainTestCase):
def test_returns_400_for_bad_name_param(self):
"""Return error message when invalid characters provided."""
- url = reverse('bot:offtopicchannelname-list', host='api')
+ url = reverse('api:bot:offtopicchannelname-list')
invalid_names = (
'space between words',
'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
@@ -147,33 +180,33 @@ class CreationTests(APISubdomainTestCase):
})
-class DeletionTests(APISubdomainTestCase):
+class DeletionTests(AuthenticatedAPITestCase):
@classmethod
def setUpTestData(cls):
cls.test_name = OffTopicChannelName.objects.create(name='lemons-lemonade-stand')
cls.test_name_2 = OffTopicChannelName.objects.create(name='bbq-with-bisk')
def test_deleting_unknown_name_returns_404(self):
- """Return 404 reponse when trying to delete unknown name."""
- url = reverse('bot:offtopicchannelname-detail', args=('unknown-name',), host='api')
+ """Return 404 response when trying to delete unknown name."""
+ url = reverse('api:bot:offtopicchannelname-detail', args=('unknown-name',))
response = self.client.delete(url)
self.assertEqual(response.status_code, 404)
def test_deleting_known_name_returns_204(self):
"""Return 204 response when deleting was successful."""
- url = reverse('bot:offtopicchannelname-detail', args=(self.test_name.name,), host='api')
+ url = reverse('api:bot:offtopicchannelname-detail', args=(self.test_name.name,))
response = self.client.delete(url)
self.assertEqual(response.status_code, 204)
def test_name_gets_deleted(self):
"""Name gets actually deleted."""
- url = reverse('bot:offtopicchannelname-detail', args=(self.test_name_2.name,), host='api')
+ url = reverse('api:bot:offtopicchannelname-detail', args=(self.test_name_2.name,))
response = self.client.delete(url)
self.assertEqual(response.status_code, 204)
- url = reverse('bot:offtopicchannelname-list', host='api')
+ url = reverse('api:bot:offtopicchannelname-list')
response = self.client.get(url)
self.assertNotIn(self.test_name_2.name, response.json())
diff --git a/pydis_site/apps/api/tests/test_offensive_message.py b/pydis_site/apps/api/tests/test_offensive_message.py
index 0f3dbffa..3cf95b75 100644
--- a/pydis_site/apps/api/tests/test_offensive_message.py
+++ b/pydis_site/apps/api/tests/test_offensive_message.py
@@ -1,14 +1,14 @@
import datetime
-from django_hosts.resolvers import reverse
+from django.urls import reverse
-from .base import APISubdomainTestCase
+from .base import AuthenticatedAPITestCase
from ..models import OffensiveMessage
-class CreationTests(APISubdomainTestCase):
+class CreationTests(AuthenticatedAPITestCase):
def test_accept_valid_data(self):
- url = reverse('bot:offensivemessage-list', host='api')
+ url = reverse('api:bot:offensivemessage-list')
delete_at = datetime.datetime.now() + datetime.timedelta(days=1)
data = {
'id': '602951077675139072',
@@ -31,7 +31,7 @@ class CreationTests(APISubdomainTestCase):
self.assertEqual(data['channel_id'], str(offensive_message.channel_id))
def test_returns_400_on_non_future_date(self):
- url = reverse('bot:offensivemessage-list', host='api')
+ url = reverse('api:bot:offensivemessage-list')
delete_at = datetime.datetime.now() - datetime.timedelta(days=1)
data = {
'id': '602951077675139072',
@@ -45,7 +45,7 @@ class CreationTests(APISubdomainTestCase):
})
def test_returns_400_on_negative_id_or_channel_id(self):
- url = reverse('bot:offensivemessage-list', host='api')
+ url = reverse('api:bot:offensivemessage-list')
delete_at = datetime.datetime.now() + datetime.timedelta(days=1)
data = {
'id': '602951077675139072',
@@ -58,7 +58,7 @@ class CreationTests(APISubdomainTestCase):
)
for field, invalid_value in cases:
- with self.subTest(fied=field, invalid_value=invalid_value):
+ with self.subTest(field=field, invalid_value=invalid_value):
test_data = data.copy()
test_data.update({field: invalid_value})
@@ -69,7 +69,7 @@ class CreationTests(APISubdomainTestCase):
})
-class ListTests(APISubdomainTestCase):
+class ListTests(AuthenticatedAPITestCase):
@classmethod
def setUpTestData(cls):
delete_at = datetime.datetime.now() + datetime.timedelta(days=1)
@@ -100,7 +100,7 @@ class ListTests(APISubdomainTestCase):
cls.messages[1]['delete_date'] = delete_at.isoformat() + 'Z'
def test_get_data(self):
- url = reverse('bot:offensivemessage-list', host='api')
+ url = reverse('api:bot:offensivemessage-list')
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
@@ -108,7 +108,7 @@ class ListTests(APISubdomainTestCase):
self.assertEqual(response.json(), self.messages)
-class DeletionTests(APISubdomainTestCase):
+class DeletionTests(AuthenticatedAPITestCase):
@classmethod
def setUpTestData(cls):
delete_at = datetime.datetime.now(tz=datetime.timezone.utc) + datetime.timedelta(days=1)
@@ -121,7 +121,7 @@ class DeletionTests(APISubdomainTestCase):
def test_delete_data(self):
url = reverse(
- 'bot:offensivemessage-detail', host='api', args=(self.valid_offensive_message.id,)
+ 'api:bot:offensivemessage-detail', args=(self.valid_offensive_message.id,)
)
response = self.client.delete(url)
@@ -132,7 +132,7 @@ class DeletionTests(APISubdomainTestCase):
)
-class NotAllowedMethodsTests(APISubdomainTestCase):
+class NotAllowedMethodsTests(AuthenticatedAPITestCase):
@classmethod
def setUpTestData(cls):
delete_at = datetime.datetime.now(tz=datetime.timezone.utc) + datetime.timedelta(days=1)
@@ -145,7 +145,7 @@ class NotAllowedMethodsTests(APISubdomainTestCase):
def test_returns_405_for_patch_and_put_requests(self):
url = reverse(
- 'bot:offensivemessage-detail', host='api', args=(self.valid_offensive_message.id,)
+ 'api:bot:offensivemessage-detail', args=(self.valid_offensive_message.id,)
)
not_allowed_methods = (self.client.patch, self.client.put)
diff --git a/pydis_site/apps/api/tests/test_reminders.py b/pydis_site/apps/api/tests/test_reminders.py
index 9dffb668..709685bc 100644
--- a/pydis_site/apps/api/tests/test_reminders.py
+++ b/pydis_site/apps/api/tests/test_reminders.py
@@ -1,52 +1,52 @@
from datetime import datetime
from django.forms.models import model_to_dict
-from django_hosts.resolvers import reverse
+from django.urls import reverse
-from .base import APISubdomainTestCase
+from .base import AuthenticatedAPITestCase
from ..models import Reminder, User
-class UnauthedReminderAPITests(APISubdomainTestCase):
+class UnauthedReminderAPITests(AuthenticatedAPITestCase):
def setUp(self):
super().setUp()
self.client.force_authenticate(user=None)
def test_list_returns_401(self):
- url = reverse('bot:reminder-list', host='api')
+ url = reverse('api:bot:reminder-list')
response = self.client.get(url)
self.assertEqual(response.status_code, 401)
def test_create_returns_401(self):
- url = reverse('bot:reminder-list', host='api')
+ url = reverse('api:bot:reminder-list')
response = self.client.post(url, data={'not': 'important'})
self.assertEqual(response.status_code, 401)
def test_delete_returns_401(self):
- url = reverse('bot:reminder-detail', args=('1234',), host='api')
+ url = reverse('api:bot:reminder-detail', args=('1234',))
response = self.client.delete(url)
self.assertEqual(response.status_code, 401)
-class EmptyDatabaseReminderAPITests(APISubdomainTestCase):
+class EmptyDatabaseReminderAPITests(AuthenticatedAPITestCase):
def test_list_all_returns_empty_list(self):
- url = reverse('bot:reminder-list', host='api')
+ url = reverse('api:bot:reminder-list')
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), [])
def test_delete_returns_404(self):
- url = reverse('bot:reminder-detail', args=('1234',), host='api')
+ url = reverse('api:bot:reminder-detail', args=('1234',))
response = self.client.delete(url)
self.assertEqual(response.status_code, 404)
-class ReminderCreationTests(APISubdomainTestCase):
+class ReminderCreationTests(AuthenticatedAPITestCase):
@classmethod
def setUpTestData(cls):
cls.author = User.objects.create(
@@ -64,7 +64,7 @@ class ReminderCreationTests(APISubdomainTestCase):
'channel_id': 123,
'mentions': [8888, 9999],
}
- url = reverse('bot:reminder-list', host='api')
+ url = reverse('api:bot:reminder-list')
response = self.client.post(url, data=data)
self.assertEqual(response.status_code, 201)
self.assertIsNotNone(Reminder.objects.filter(id=1).first())
@@ -73,13 +73,13 @@ class ReminderCreationTests(APISubdomainTestCase):
data = {
'author': self.author.id, # Missing multiple required fields
}
- url = reverse('bot:reminder-list', host='api')
+ url = reverse('api:bot:reminder-list')
response = self.client.post(url, data=data)
self.assertEqual(response.status_code, 400)
self.assertRaises(Reminder.DoesNotExist, Reminder.objects.get, id=1)
-class ReminderDeletionTests(APISubdomainTestCase):
+class ReminderDeletionTests(AuthenticatedAPITestCase):
@classmethod
def setUpTestData(cls):
cls.author = User.objects.create(
@@ -97,20 +97,20 @@ class ReminderDeletionTests(APISubdomainTestCase):
)
def test_delete_unknown_reminder_returns_404(self):
- url = reverse('bot:reminder-detail', args=('something',), host='api')
+ url = reverse('api:bot:reminder-detail', args=('something',))
response = self.client.delete(url)
self.assertEqual(response.status_code, 404)
def test_delete_known_reminder_returns_204(self):
- url = reverse('bot:reminder-detail', args=(self.reminder.id,), host='api')
+ url = reverse('api:bot:reminder-detail', args=(self.reminder.id,))
response = self.client.delete(url)
self.assertEqual(response.status_code, 204)
self.assertRaises(Reminder.DoesNotExist, Reminder.objects.get, id=self.reminder.id)
-class ReminderListTests(APISubdomainTestCase):
+class ReminderListTests(AuthenticatedAPITestCase):
@classmethod
def setUpTestData(cls):
cls.author = User.objects.create(
@@ -142,28 +142,28 @@ class ReminderListTests(APISubdomainTestCase):
cls.rem_dict_two['expiration'] += 'Z' # Massaging a quirk of the response time format
def test_reminders_in_full_list(self):
- url = reverse('bot:reminder-list', host='api')
+ url = reverse('api:bot:reminder-list')
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertCountEqual(response.json(), [self.rem_dict_one, self.rem_dict_two])
def test_filter_search(self):
- url = reverse('bot:reminder-list', host='api')
+ url = reverse('api:bot:reminder-list')
response = self.client.get(f'{url}?search={self.author.name}')
self.assertEqual(response.status_code, 200)
self.assertCountEqual(response.json(), [self.rem_dict_one, self.rem_dict_two])
def test_filter_field(self):
- url = reverse('bot:reminder-list', host='api')
+ url = reverse('api:bot:reminder-list')
response = self.client.get(f'{url}?active=true')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), [self.rem_dict_one])
-class ReminderRetrieveTests(APISubdomainTestCase):
+class ReminderRetrieveTests(AuthenticatedAPITestCase):
@classmethod
def setUpTestData(cls):
cls.author = User.objects.create(
@@ -181,17 +181,17 @@ class ReminderRetrieveTests(APISubdomainTestCase):
)
def test_retrieve_unknown_returns_404(self):
- url = reverse('bot:reminder-detail', args=("not_an_id",), host='api')
+ url = reverse('api:bot:reminder-detail', args=("not_an_id",))
response = self.client.get(url)
self.assertEqual(response.status_code, 404)
def test_retrieve_known_returns_200(self):
- url = reverse('bot:reminder-detail', args=(self.reminder.id,), host='api')
+ url = reverse('api:bot:reminder-detail', args=(self.reminder.id,))
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
-class ReminderUpdateTests(APISubdomainTestCase):
+class ReminderUpdateTests(AuthenticatedAPITestCase):
@classmethod
def setUpTestData(cls):
cls.author = User.objects.create(
@@ -211,7 +211,7 @@ class ReminderUpdateTests(APISubdomainTestCase):
cls.data = {'content': 'Oops I forgot'}
def test_patch_updates_record(self):
- url = reverse('bot:reminder-detail', args=(self.reminder.id,), host='api')
+ url = reverse('api:bot:reminder-detail', args=(self.reminder.id,))
response = self.client.patch(url, data=self.data)
self.assertEqual(response.status_code, 200)
diff --git a/pydis_site/apps/api/tests/test_roles.py b/pydis_site/apps/api/tests/test_roles.py
index 4d1a430c..73c80c77 100644
--- a/pydis_site/apps/api/tests/test_roles.py
+++ b/pydis_site/apps/api/tests/test_roles.py
@@ -1,10 +1,10 @@
-from django_hosts.resolvers import reverse
+from django.urls import reverse
-from .base import APISubdomainTestCase
-from ..models import Role
+from .base import AuthenticatedAPITestCase
+from ..models import Role, User
-class CreationTests(APISubdomainTestCase):
+class CreationTests(AuthenticatedAPITestCase):
@classmethod
def setUpTestData(cls):
cls.admins_role = Role.objects.create(
@@ -35,6 +35,20 @@ class CreationTests(APISubdomainTestCase):
permissions=6,
position=0,
)
+ cls.role_to_delete = Role.objects.create(
+ id=7,
+ name="role to delete",
+ colour=7,
+ permissions=7,
+ position=0,
+ )
+ cls.role_unassigned_test_user = User.objects.create(
+ id=8,
+ name="role_unassigned_test_user",
+ discriminator="0000",
+ roles=[cls.role_to_delete.id],
+ in_guild=True
+ )
def _validate_roledict(self, role_dict: dict) -> None:
"""Helper method to validate a dict representing a role."""
@@ -78,21 +92,21 @@ class CreationTests(APISubdomainTestCase):
def test_role_list(self):
"""Tests the GET list-view and validates the contents."""
- url = reverse('bot:role-list', host='api')
+ url = reverse('api:bot:role-list')
response = self.client.get(url)
- self.assertContains(response, text="id", count=4, status_code=200)
+ self.assertContains(response, text="id", count=5, status_code=200)
roles = response.json()
self.assertIsInstance(roles, list)
- self.assertEqual(len(roles), 4)
+ self.assertEqual(len(roles), 5)
for role in roles:
self._validate_roledict(role)
def test_role_get_detail_success(self):
"""Tests GET detail view of an existing role."""
- url = reverse('bot:role-detail', host='api', args=(self.admins_role.id, ))
+ url = reverse('api:bot:role-detail', args=(self.admins_role.id, ))
response = self.client.get(url)
self.assertContains(response, text="id", count=1, status_code=200)
@@ -107,7 +121,7 @@ class CreationTests(APISubdomainTestCase):
def test_role_post_201(self):
"""Tests creation of a role with a valid request."""
- url = reverse('bot:role-list', host='api')
+ url = reverse('api:bot:role-list')
data = {
"id": 1234567890,
"name": "Role Creation Test",
@@ -120,7 +134,7 @@ class CreationTests(APISubdomainTestCase):
def test_role_post_invalid_request_body(self):
"""Tests creation of a role with an invalid request body."""
- url = reverse('bot:role-list', host='api')
+ url = reverse('api:bot:role-list')
data = {
"name": "Role Creation Test",
"permissions": 0b01010010101,
@@ -133,7 +147,7 @@ class CreationTests(APISubdomainTestCase):
def test_role_put_200(self):
"""Tests PUT role request with valid request body."""
- url = reverse('bot:role-detail', host='api', args=(self.admins_role.id,))
+ url = reverse('api:bot:role-detail', args=(self.admins_role.id,))
data = {
"id": 123454321,
"name": "Role Put Alteration Test",
@@ -153,7 +167,7 @@ class CreationTests(APISubdomainTestCase):
def test_role_put_invalid_request_body(self):
"""Tests PUT role request with invalid request body."""
- url = reverse('bot:role-detail', host='api', args=(self.admins_role.id,))
+ url = reverse('api:bot:role-detail', args=(self.admins_role.id,))
data = {
"name": "Role Put Alteration Test",
"permissions": 255,
@@ -165,7 +179,7 @@ class CreationTests(APISubdomainTestCase):
def test_role_patch_200(self):
"""Tests PATCH role request with valid request body."""
- url = reverse('bot:role-detail', host='api', args=(self.admins_role.id,))
+ url = reverse('api:bot:role-detail', args=(self.admins_role.id,))
data = {
"name": "Owners"
}
@@ -177,13 +191,19 @@ class CreationTests(APISubdomainTestCase):
def test_role_delete_200(self):
"""Tests DELETE requests for existing role."""
- url = reverse('bot:role-detail', host='api', args=(self.admins_role.id,))
+ url = reverse('api:bot:role-detail', args=(self.admins_role.id,))
response = self.client.delete(url)
self.assertEqual(response.status_code, 204)
+ def test_role_delete_unassigned(self):
+ """Tests if the deleted Role gets unassigned from the user."""
+ self.role_to_delete.delete()
+ self.role_unassigned_test_user.refresh_from_db()
+ self.assertEqual(self.role_unassigned_test_user.roles, [])
+
def test_role_detail_404_all_methods(self):
"""Tests detail view with non-existing ID."""
- url = reverse('bot:role-detail', host='api', args=(20190815,))
+ url = reverse('api:bot:role-detail', args=(20190815,))
for method in ('get', 'put', 'patch', 'delete'):
response = getattr(self.client, method)(url)
diff --git a/pydis_site/apps/api/tests/test_rules.py b/pydis_site/apps/api/tests/test_rules.py
index c94f89cc..d08c5fae 100644
--- a/pydis_site/apps/api/tests/test_rules.py
+++ b/pydis_site/apps/api/tests/test_rules.py
@@ -1,23 +1,23 @@
-from django_hosts.resolvers import reverse
+from django.urls import reverse
-from .base import APISubdomainTestCase
+from .base import AuthenticatedAPITestCase
from ..views import RulesView
-class RuleAPITests(APISubdomainTestCase):
+class RuleAPITests(AuthenticatedAPITestCase):
def setUp(self):
super().setUp()
self.client.force_authenticate(user=None)
def test_can_access_rules_view(self):
- url = reverse('rules', host='api')
+ url = reverse('api:rules')
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertIsInstance(response.json(), list)
def test_link_format_query_param_produces_different_results(self):
- url = reverse('rules', host='api')
+ url = reverse('api:rules')
markdown_links_response = self.client.get(url + '?link_format=md')
html_links_response = self.client.get(url + '?link_format=html')
self.assertNotEqual(
@@ -30,6 +30,6 @@ class RuleAPITests(APISubdomainTestCase):
RulesView._format_link("a", "b", "c")
def test_get_returns_400_for_wrong_link_format(self):
- url = reverse('rules', host='api')
+ url = reverse('api:rules')
response = self.client.get(url + '?link_format=unknown')
self.assertEqual(response.status_code, 400)
diff --git a/pydis_site/apps/api/tests/test_users.py b/pydis_site/apps/api/tests/test_users.py
index c43b916a..9b91380b 100644
--- a/pydis_site/apps/api/tests/test_users.py
+++ b/pydis_site/apps/api/tests/test_users.py
@@ -1,44 +1,45 @@
from unittest.mock import patch
from django.core.exceptions import ObjectDoesNotExist
-from django_hosts.resolvers import reverse
+from django.urls import reverse
-from .base import APISubdomainTestCase
+from .base import AuthenticatedAPITestCase
from ..models import Role, User
-from ..models.bot.metricity import NotFound
+from ..models.bot.metricity import NotFoundError
+from ..viewsets.bot.user import UserListPagination
-class UnauthedUserAPITests(APISubdomainTestCase):
+class UnauthedUserAPITests(AuthenticatedAPITestCase):
def setUp(self):
super().setUp()
self.client.force_authenticate(user=None)
def test_detail_lookup_returns_401(self):
- url = reverse('bot:user-detail', args=('whatever',), host='api')
+ url = reverse('api:bot:user-detail', args=('whatever',))
response = self.client.get(url)
self.assertEqual(response.status_code, 401)
def test_list_returns_401(self):
- url = reverse('bot:user-list', host='api')
+ url = reverse('api:bot:user-list')
response = self.client.get(url)
self.assertEqual(response.status_code, 401)
def test_create_returns_401(self):
- url = reverse('bot:user-list', host='api')
+ url = reverse('api:bot:user-list')
response = self.client.post(url, data={'hi': 'there'})
self.assertEqual(response.status_code, 401)
def test_delete_returns_401(self):
- url = reverse('bot:user-detail', args=('whatever',), host='api')
+ url = reverse('api:bot:user-detail', args=('whatever',))
response = self.client.delete(url)
self.assertEqual(response.status_code, 401)
-class CreationTests(APISubdomainTestCase):
+class CreationTests(AuthenticatedAPITestCase):
@classmethod
def setUpTestData(cls):
cls.role = Role.objects.create(
@@ -57,7 +58,7 @@ class CreationTests(APISubdomainTestCase):
)
def test_accepts_valid_data(self):
- url = reverse('bot:user-list', host='api')
+ url = reverse('api:bot:user-list')
data = {
'id': 42,
'name': "Test",
@@ -78,7 +79,7 @@ class CreationTests(APISubdomainTestCase):
self.assertEqual(user.in_guild, data['in_guild'])
def test_supports_multi_creation(self):
- url = reverse('bot:user-list', host='api')
+ url = reverse('api:bot:user-list')
data = [
{
'id': 5,
@@ -103,7 +104,7 @@ class CreationTests(APISubdomainTestCase):
self.assertEqual(response.json(), [])
def test_returns_400_for_unknown_role_id(self):
- url = reverse('bot:user-list', host='api')
+ url = reverse('api:bot:user-list')
data = {
'id': 5,
'name': "test man",
@@ -117,7 +118,7 @@ class CreationTests(APISubdomainTestCase):
self.assertEqual(response.status_code, 400)
def test_returns_400_for_bad_data(self):
- url = reverse('bot:user-list', host='api')
+ url = reverse('api:bot:user-list')
data = {
'id': True,
'discriminator': "totally!"
@@ -128,7 +129,7 @@ class CreationTests(APISubdomainTestCase):
def test_returns_400_for_user_recreation(self):
"""Return 201 if User is already present in database as it skips User creation."""
- url = reverse('bot:user-list', host='api')
+ url = reverse('api:bot:user-list')
data = [{
'id': 11,
'name': 'You saw nothing.',
@@ -140,7 +141,7 @@ class CreationTests(APISubdomainTestCase):
def test_returns_400_for_duplicate_request_users(self):
"""Return 400 if 2 Users with same ID is passed in the request data."""
- url = reverse('bot:user-list', host='api')
+ url = reverse('api:bot:user-list')
data = [
{
'id': 11,
@@ -160,7 +161,7 @@ class CreationTests(APISubdomainTestCase):
def test_returns_400_for_existing_user(self):
"""Returns 400 if user is already present in DB."""
- url = reverse('bot:user-list', host='api')
+ url = reverse('api:bot:user-list')
data = {
'id': 11,
'name': 'You saw nothing part 3.',
@@ -171,7 +172,7 @@ class CreationTests(APISubdomainTestCase):
self.assertEqual(response.status_code, 400)
-class MultiPatchTests(APISubdomainTestCase):
+class MultiPatchTests(AuthenticatedAPITestCase):
@classmethod
def setUpTestData(cls):
cls.role_developer = Role.objects.create(
@@ -195,7 +196,7 @@ class MultiPatchTests(APISubdomainTestCase):
)
def test_multiple_users_patch(self):
- url = reverse("bot:user-bulk-patch", host="api")
+ url = reverse("api:bot:user-bulk-patch")
data = [
{
"id": 1,
@@ -218,7 +219,7 @@ class MultiPatchTests(APISubdomainTestCase):
self.assertEqual(user_2.name, data[1]["name"])
def test_returns_400_for_missing_user_id(self):
- url = reverse("bot:user-bulk-patch", host="api")
+ url = reverse("api:bot:user-bulk-patch")
data = [
{
"name": "I am ghost user!",
@@ -234,7 +235,7 @@ class MultiPatchTests(APISubdomainTestCase):
self.assertEqual(response.status_code, 400)
def test_returns_404_for_not_found_user(self):
- url = reverse("bot:user-bulk-patch", host="api")
+ url = reverse("api:bot:user-bulk-patch")
data = [
{
"id": 1,
@@ -252,7 +253,7 @@ class MultiPatchTests(APISubdomainTestCase):
self.assertEqual(response.status_code, 404)
def test_returns_400_for_bad_data(self):
- url = reverse("bot:user-bulk-patch", host="api")
+ url = reverse("api:bot:user-bulk-patch")
data = [
{
"id": 1,
@@ -268,7 +269,7 @@ class MultiPatchTests(APISubdomainTestCase):
self.assertEqual(response.status_code, 400)
def test_returns_400_for_insufficient_data(self):
- url = reverse("bot:user-bulk-patch", host="api")
+ url = reverse("api:bot:user-bulk-patch")
data = [
{
"id": 1,
@@ -282,7 +283,7 @@ class MultiPatchTests(APISubdomainTestCase):
def test_returns_400_for_duplicate_request_users(self):
"""Return 400 if 2 Users with same ID is passed in the request data."""
- url = reverse("bot:user-bulk-patch", host="api")
+ url = reverse("api:bot:user-bulk-patch")
data = [
{
'id': 1,
@@ -297,7 +298,7 @@ class MultiPatchTests(APISubdomainTestCase):
self.assertEqual(response.status_code, 400)
-class UserModelTests(APISubdomainTestCase):
+class UserModelTests(AuthenticatedAPITestCase):
@classmethod
def setUpTestData(cls):
cls.role_top = Role.objects.create(
@@ -353,11 +354,11 @@ class UserModelTests(APISubdomainTestCase):
self.assertEqual(self.user_with_roles.username, "Test User with two roles#0001")
-class UserPaginatorTests(APISubdomainTestCase):
+class UserPaginatorTests(AuthenticatedAPITestCase):
@classmethod
def setUpTestData(cls):
users = []
- for i in range(1, 10_001):
+ for i in range(1, UserListPagination.page_size + 1):
users.append(User(
id=i,
name=f"user{i}",
@@ -367,35 +368,37 @@ class UserPaginatorTests(APISubdomainTestCase):
cls.users = User.objects.bulk_create(users)
def test_returns_single_page_response(self):
- url = reverse("bot:user-list", host="api")
+ url = reverse("api:bot:user-list")
response = self.client.get(url).json()
self.assertIsNone(response["next_page_no"])
self.assertIsNone(response["previous_page_no"])
def test_returns_next_page_number(self):
+ user_id = UserListPagination.page_size + 1
User.objects.create(
- id=10_001,
- name="user10001",
+ id=user_id,
+ name=f"user{user_id}",
discriminator=1111,
in_guild=True
)
- url = reverse("bot:user-list", host="api")
+ url = reverse("api:bot:user-list")
response = self.client.get(url).json()
self.assertEqual(2, response["next_page_no"])
def test_returns_previous_page_number(self):
+ user_id = UserListPagination.page_size + 1
User.objects.create(
- id=10_001,
- name="user10001",
+ id=user_id,
+ name=f"user{user_id}",
discriminator=1111,
in_guild=True
)
- url = reverse("bot:user-list", host="api")
+ url = reverse("api:bot:user-list")
response = self.client.get(url, {"page": 2}).json()
self.assertEqual(1, response["previous_page_no"])
-class UserMetricityTests(APISubdomainTestCase):
+class UserMetricityTests(AuthenticatedAPITestCase):
@classmethod
def setUpTestData(cls):
User.objects.create(
@@ -413,12 +416,12 @@ class UserMetricityTests(APISubdomainTestCase):
self.mock_metricity_user(joined_at, total_messages, total_blocks, [])
# When
- url = reverse('bot:user-metricity-data', args=[0], host='api')
+ url = reverse('api:bot:user-metricity-data', args=[0])
response = self.client.get(url)
# Then
self.assertEqual(response.status_code, 200)
- self.assertEqual(response.json(), {
+ self.assertCountEqual(response.json(), {
"joined_at": joined_at,
"total_messages": total_messages,
"voice_banned": False,
@@ -430,7 +433,7 @@ class UserMetricityTests(APISubdomainTestCase):
self.mock_no_metricity_user()
# When
- url = reverse('bot:user-metricity-data', args=[0], host='api')
+ url = reverse('api:bot:user-metricity-data', args=[0])
response = self.client.get(url)
# Then
@@ -441,7 +444,7 @@ class UserMetricityTests(APISubdomainTestCase):
self.mock_no_metricity_user()
# When
- url = reverse('bot:user-metricity-review-data', args=[0], host='api')
+ url = reverse('api:bot:user-metricity-review-data', args=[0])
response = self.client.get(url)
# Then
@@ -460,7 +463,7 @@ class UserMetricityTests(APISubdomainTestCase):
with patch("pydis_site.apps.api.viewsets.bot.user.Infraction.objects.get") as p:
p.side_effect = case['exception']
- url = reverse('bot:user-metricity-data', args=[0], host='api')
+ url = reverse('api:bot:user-metricity-data', args=[0])
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
@@ -475,7 +478,7 @@ class UserMetricityTests(APISubdomainTestCase):
self.mock_metricity_user(joined_at, total_messages, total_blocks, channel_activity)
# When
- url = reverse('bot:user-metricity-review-data', args=[0], host='api')
+ url = reverse('api:bot:user-metricity-review-data', args=[0])
response = self.client.get(url)
# Then
@@ -501,7 +504,7 @@ class UserMetricityTests(APISubdomainTestCase):
self.metricity = patcher.start()
self.addCleanup(patcher.stop)
self.metricity = self.metricity.return_value.__enter__.return_value
- self.metricity.user.side_effect = NotFound()
- self.metricity.total_messages.side_effect = NotFound()
- self.metricity.total_message_blocks.side_effect = NotFound()
- self.metricity.top_channel_activity.side_effect = NotFound()
+ self.metricity.user.side_effect = NotFoundError()
+ self.metricity.total_messages.side_effect = NotFoundError()
+ self.metricity.total_message_blocks.side_effect = NotFoundError()
+ self.metricity.top_channel_activity.side_effect = NotFoundError()
diff --git a/pydis_site/apps/api/urls.py b/pydis_site/apps/api/urls.py
index 2e1ef0b4..b0ab545b 100644
--- a/pydis_site/apps/api/urls.py
+++ b/pydis_site/apps/api/urls.py
@@ -16,7 +16,7 @@ from .viewsets import (
UserViewSet
)
-# http://www.django-rest-framework.org/api-guide/routers/#defaultrouter
+# https://www.django-rest-framework.org/api-guide/routers/#defaultrouter
bot_router = DefaultRouter(trailing_slash=False)
bot_router.register(
'filter-lists',
diff --git a/pydis_site/apps/api/viewsets/bot/filter_list.py b/pydis_site/apps/api/viewsets/bot/filter_list.py
index 2cb21ab9..4b05acee 100644
--- a/pydis_site/apps/api/viewsets/bot/filter_list.py
+++ b/pydis_site/apps/api/viewsets/bot/filter_list.py
@@ -59,7 +59,8 @@ class FilterListViewSet(ModelViewSet):
... ["GUILD_INVITE","Guild Invite"],
... ["FILE_FORMAT","File Format"],
... ["DOMAIN_NAME","Domain Name"],
- ... ["FILTER_TOKEN","Filter Token"]
+ ... ["FILTER_TOKEN","Filter Token"],
+ ... ["REDIRECT", "Redirect"]
... ]
#### Status codes
diff --git a/pydis_site/apps/api/viewsets/bot/infraction.py b/pydis_site/apps/api/viewsets/bot/infraction.py
index f8b0cb9d..8a48ed1f 100644
--- a/pydis_site/apps/api/viewsets/bot/infraction.py
+++ b/pydis_site/apps/api/viewsets/bot/infraction.py
@@ -70,7 +70,8 @@ class InfractionViewSet(
... 'actor': 125435062127820800,
... 'type': 'ban',
... 'reason': 'He terk my jerb!',
- ... 'hidden': True
+ ... 'hidden': True,
+ ... 'dm_sent': True
... }
... ]
@@ -100,7 +101,8 @@ class InfractionViewSet(
... 'hidden': True,
... 'type': 'ban',
... 'reason': 'He terk my jerb!',
- ... 'user': 172395097705414656
+ ... 'user': 172395097705414656,
+ ... 'dm_sent': False
... }
#### Response format
@@ -118,7 +120,8 @@ class InfractionViewSet(
>>> {
... 'active': True,
... 'expires_at': '4143-02-15T21:04:31+00:00',
- ... 'reason': 'durka derr'
+ ... 'reason': 'durka derr',
+ ... 'dm_sent': True
... }
#### Response format
diff --git a/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py b/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py
index 826ad25e..78f8c340 100644
--- a/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py
+++ b/pydis_site/apps/api/viewsets/bot/off_topic_channel_name.py
@@ -1,18 +1,17 @@
from django.db.models import Case, Value, When
from django.db.models.query import QuerySet
-from django.http.request import HttpRequest
from django.shortcuts import get_object_or_404
from rest_framework.exceptions import ParseError
-from rest_framework.mixins import DestroyModelMixin
+from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.status import HTTP_201_CREATED
-from rest_framework.viewsets import ViewSet
+from rest_framework.viewsets import ModelViewSet
from pydis_site.apps.api.models.bot.off_topic_channel_name import OffTopicChannelName
from pydis_site.apps.api.serializers import OffTopicChannelNameSerializer
-class OffTopicChannelNameViewSet(DestroyModelMixin, ViewSet):
+class OffTopicChannelNameViewSet(ModelViewSet):
"""
View of off-topic channel names used by the bot to rotate our off-topic names on a daily basis.
@@ -20,7 +19,7 @@ class OffTopicChannelNameViewSet(DestroyModelMixin, ViewSet):
### GET /bot/off-topic-channel-names
Return all known off-topic channel names from the database.
If the `random_items` query parameter is given, for example using...
- $ curl api.pythondiscord.local:8000/bot/off-topic-channel-names?random_items=5
+ $ curl 127.0.0.1:8000/api/bot/off-topic-channel-names?random_items=5
... then the API will return `5` random items from the database
that is not used in current rotation.
When running out of names, API will mark all names to not used and start new rotation.
@@ -39,7 +38,7 @@ class OffTopicChannelNameViewSet(DestroyModelMixin, ViewSet):
### POST /bot/off-topic-channel-names
Create a new off-topic-channel name in the database.
The name must be given as a query parameter, for example:
- $ curl api.pythondiscord.local:8000/bot/off-topic-channel-names?name=lemons-lemonade-shop
+ $ curl 127.0.0.1:8000/api/bot/off-topic-channel-names?name=lemons-lemonade-shop
#### Status codes
- 201: returned on success
@@ -58,6 +57,7 @@ class OffTopicChannelNameViewSet(DestroyModelMixin, ViewSet):
lookup_field = 'name'
serializer_class = OffTopicChannelNameSerializer
+ queryset = OffTopicChannelName.objects.all()
def get_object(self) -> OffTopicChannelName:
"""
@@ -65,15 +65,14 @@ class OffTopicChannelNameViewSet(DestroyModelMixin, ViewSet):
If it doesn't, a HTTP 404 is returned by way of throwing an exception.
"""
- queryset = self.get_queryset()
name = self.kwargs[self.lookup_field]
- return get_object_or_404(queryset, name=name)
+ return get_object_or_404(self.queryset, name=name)
def get_queryset(self) -> QuerySet:
"""Returns a queryset that covers the entire OffTopicChannelName table."""
return OffTopicChannelName.objects.all()
- def create(self, request: HttpRequest) -> Response:
+ def create(self, request: Request, *args, **kwargs) -> Response:
"""
DRF method for creating a new OffTopicChannelName.
@@ -91,7 +90,7 @@ class OffTopicChannelNameViewSet(DestroyModelMixin, ViewSet):
'name': ["This query parameter is required."]
})
- def list(self, request: HttpRequest) -> Response:
+ def list(self, request: Request, *args, **kwargs) -> Response:
"""
DRF method for listing OffTopicChannelName entries.
@@ -109,13 +108,13 @@ class OffTopicChannelNameViewSet(DestroyModelMixin, ViewSet):
'random_items': ["Must be a positive integer."]
})
- queryset = self.get_queryset().order_by('used', '?')[:random_count]
+ queryset = self.queryset.order_by('used', '?')[:random_count]
# When any name is used in our listing then this means we reached end of round
# and we need to reset all other names `used` to False
if any(offtopic_name.used for offtopic_name in queryset):
# These names that we just got have to be excluded from updating used to False
- self.get_queryset().update(
+ self.queryset.update(
used=Case(
When(
name__in=(offtopic_name.name for offtopic_name in queryset),
@@ -126,13 +125,18 @@ class OffTopicChannelNameViewSet(DestroyModelMixin, ViewSet):
)
else:
# Otherwise mark selected names `used` to True
- self.get_queryset().filter(
+ self.queryset.filter(
name__in=(offtopic_name.name for offtopic_name in queryset)
).update(used=True)
serialized = self.serializer_class(queryset, many=True)
return Response(serialized.data)
- queryset = self.get_queryset()
+ params = {}
+
+ if active_param := request.query_params.get("active"):
+ params["active"] = active_param.lower() == "true"
+
+ queryset = self.queryset.filter(**params)
serialized = self.serializer_class(queryset, many=True)
return Response(serialized.data)
diff --git a/pydis_site/apps/api/viewsets/bot/reminder.py b/pydis_site/apps/api/viewsets/bot/reminder.py
index 111660d9..78d7cb3b 100644
--- a/pydis_site/apps/api/viewsets/bot/reminder.py
+++ b/pydis_site/apps/api/viewsets/bot/reminder.py
@@ -42,7 +42,8 @@ class ReminderViewSet(
... 'expiration': '5018-11-20T15:52:00Z',
... 'id': 11,
... 'channel_id': 634547009956872193,
- ... 'jump_url': "https://discord.com/channels/<guild_id>/<channel_id>/<message_id>"
+ ... 'jump_url': "https://discord.com/channels/<guild_id>/<channel_id>/<message_id>",
+ ... 'failures': 3
... },
... ...
... ]
@@ -67,7 +68,8 @@ class ReminderViewSet(
... 'expiration': '5018-11-20T15:52:00Z',
... 'id': 11,
... 'channel_id': 634547009956872193,
- ... 'jump_url': "https://discord.com/channels/<guild_id>/<channel_id>/<message_id>"
+ ... 'jump_url': "https://discord.com/channels/<guild_id>/<channel_id>/<message_id>",
+ ... 'failures': 3
... }
#### Status codes
@@ -80,7 +82,7 @@ class ReminderViewSet(
#### Request body
>>> {
... 'author': int,
- ... 'mentions': List[int],
+ ... 'mentions': list[int],
... 'content': str,
... 'expiration': str, # ISO-formatted datetime
... 'channel_id': int,
@@ -98,9 +100,10 @@ class ReminderViewSet(
#### Request body
>>> {
- ... 'mentions': List[int],
+ ... 'mentions': list[int],
... 'content': str,
- ... 'expiration': str # ISO-formatted datetime
+ ... 'expiration': str, # ISO-formatted datetime
+ ... 'failures': int
... }
#### Status codes
diff --git a/pydis_site/apps/api/viewsets/bot/user.py b/pydis_site/apps/api/viewsets/bot/user.py
index 25722f5a..1a5e79f8 100644
--- a/pydis_site/apps/api/viewsets/bot/user.py
+++ b/pydis_site/apps/api/viewsets/bot/user.py
@@ -11,7 +11,7 @@ from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet
from pydis_site.apps.api.models.bot.infraction import Infraction
-from pydis_site.apps.api.models.bot.metricity import Metricity, NotFound
+from pydis_site.apps.api.models.bot.metricity import Metricity, NotFoundError
from pydis_site.apps.api.models.bot.user import User
from pydis_site.apps.api.serializers import UserSerializer
@@ -19,7 +19,7 @@ from pydis_site.apps.api.serializers import UserSerializer
class UserListPagination(PageNumberPagination):
"""Custom pagination class for the User Model."""
- page_size = 10000
+ page_size = 2500
page_size_query_param = "page_size"
def get_next_page_number(self) -> typing.Optional[int]:
@@ -271,11 +271,13 @@ class UserViewSet(ModelViewSet):
with Metricity() as metricity:
try:
data = metricity.user(user.id)
+
data["total_messages"] = metricity.total_messages(user.id)
- data["voice_banned"] = voice_banned
data["activity_blocks"] = metricity.total_message_blocks(user.id)
+
+ data["voice_banned"] = voice_banned
return Response(data, status=status.HTTP_200_OK)
- except NotFound:
+ except NotFoundError:
return Response(dict(detail="User not found in metricity"),
status=status.HTTP_404_NOT_FOUND)
@@ -290,6 +292,6 @@ class UserViewSet(ModelViewSet):
data["total_messages"] = metricity.total_messages(user.id)
data["top_channel_activity"] = metricity.top_channel_activity(user.id)
return Response(data, status=status.HTTP_200_OK)
- except NotFound:
+ except NotFoundError:
return Response(dict(detail="User not found in metricity"),
status=status.HTTP_404_NOT_FOUND)