From d061ab1c138c7cecf8d138226990bdcf0761a5db Mon Sep 17 00:00:00 2001 From: RohanJnr Date: Wed, 19 May 2021 13:13:57 +0530 Subject: Add active field to OffTopicChannelName model. --- pydis_site/apps/api/models/bot/off_topic_channel_name.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'pydis_site/apps/api/models') 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..582c069e 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 @@ -18,7 +18,12 @@ class OffTopicChannelName(ModelReprMixin, models.Model): 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): -- cgit v1.2.3 From c998d475440cf4819bad7ebc3ed19f31ce82baf4 Mon Sep 17 00:00:00 2001 From: Johannes Christ Date: Tue, 8 Jun 2021 00:26:40 +0200 Subject: Move subdomains to query paths. In more detail: - Use Django URL namespaces (e.g. `api:bot:infractions`) instead of `django_hosts` host argument. - Update the hosts file setup documentation to remove subdomain entries. - Update the hosts file setup documentation to mention that the entry of `pythondiscord.local` is not required and mainly for convenience. - Rename the `APISubdomainTestCase` to the more fitting `AuthenticatedAPITestCase`, as authentication is all that is left that the class is doing. - Drop dependency to `django_hosts`. --- poetry.lock | 14 +-- pydis_site/apps/admin/urls.py | 1 + .../api/models/bot/message_deletion_context.py | 4 +- pydis_site/apps/api/tests/base.py | 27 +++--- pydis_site/apps/api/tests/test_deleted_messages.py | 16 ++-- .../apps/api/tests/test_documentation_links.py | 50 +++++----- pydis_site/apps/api/tests/test_filterlists.py | 16 ++-- pydis_site/apps/api/tests/test_healthcheck.py | 8 +- pydis_site/apps/api/tests/test_infractions.py | 104 ++++++++++----------- pydis_site/apps/api/tests/test_nominations.py | 86 ++++++++--------- .../apps/api/tests/test_off_topic_channel_names.py | 48 +++++----- .../apps/api/tests/test_offensive_message.py | 24 ++--- pydis_site/apps/api/tests/test_reminders.py | 48 +++++----- pydis_site/apps/api/tests/test_roles.py | 24 ++--- pydis_site/apps/api/tests/test_rules.py | 12 +-- pydis_site/apps/api/tests/test_users.py | 66 ++++++------- .../api/viewsets/bot/off_topic_channel_name.py | 4 +- .../guides/pydis-guides/contributing/bot.md | 1 - .../guides/pydis-guides/contributing/hosts-file.md | 7 +- .../guides/pydis-guides/contributing/site.md | 6 +- pydis_site/apps/events/tests/test_views.py | 10 +- pydis_site/apps/home/tests/test_views.py | 4 +- pydis_site/apps/home/urls.py | 8 +- pydis_site/apps/redirect/tests.py | 4 +- pydis_site/apps/resources/tests/test_views.py | 6 +- pydis_site/apps/staff/tests/test_logs_view.py | 22 ++--- pydis_site/hosts.py | 13 --- pydis_site/settings.py | 22 +---- pydis_site/templates/base/navbar.html | 2 +- pydis_site/templates/home/index.html | 4 +- pydis_site/urls.py | 16 +++- pyproject.toml | 1 - 32 files changed, 311 insertions(+), 367 deletions(-) delete mode 100644 pydis_site/hosts.py (limited to 'pydis_site/apps/api/models') diff --git a/poetry.lock b/poetry.lock index 76c8890b..b43cd7ff 100644 --- a/poetry.lock +++ b/poetry.lock @@ -149,14 +149,6 @@ python-versions = ">=3.4" [package.dependencies] Django = ">=1.11" -[[package]] -name = "django-hosts" -version = "4.0" -description = "Dynamic and static host resolving for Django. Maps hostnames to URLconfs." -category = "main" -optional = false -python-versions = ">=3.5" - [[package]] name = "django-simple-bulma" version = "2.2.0" @@ -706,7 +698,7 @@ brotli = ["brotli"] [metadata] lock-version = "1.1" python-versions = "3.9.*" -content-hash = "741444c18250124e2d998506b0643fb26240d7481389a13648b02fa1794435e3" +content-hash = "b5750a4a7ad3e6d1f51c3053164889e0e58b004bf7f92b908cd3d6316e7e9945" [metadata.files] appdirs = [ @@ -815,10 +807,6 @@ django-filter = [ {file = "django-filter-2.1.0.tar.gz", hash = "sha256:3dafb7d2810790498895c22a1f31b2375795910680ac9c1432821cbedb1e176d"}, {file = "django_filter-2.1.0-py3-none-any.whl", hash = "sha256:a3014de317bef0cd43075a0f08dfa1d319a7ccc5733c3901fb860da70b0dda68"}, ] -django-hosts = [ - {file = "django-hosts-4.0.tar.gz", hash = "sha256:59a870d453f113c889a7888bae5408888870350e83e362740f382dad569c2281"}, - {file = "django_hosts-4.0-py2.py3-none-any.whl", hash = "sha256:136ac225f34e7f2c007294441a38663ec2bba9637d870ad001def81bca87e390"}, -] django-simple-bulma = [ {file = "django-simple-bulma-2.2.0.tar.gz", hash = "sha256:dfc34839e050d5e4749498806ed7ee8c77794021efa54ab224a2de925c644fea"}, {file = "django_simple_bulma-2.2.0-py3-none-any.whl", hash = "sha256:38530d787b2b6a091b480f7cbb8841a3b3709733ebfa5e543ae339c3d8fece03"}, diff --git a/pydis_site/apps/admin/urls.py b/pydis_site/apps/admin/urls.py index 146c6496..a4f3e517 100644 --- a/pydis_site/apps/admin/urls.py +++ b/pydis_site/apps/admin/urls.py @@ -2,6 +2,7 @@ from django.contrib import admin from django.urls import path +app_name = 'admin' urlpatterns = ( path('', admin.site.urls), ) 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/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..63993978 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,7 +62,7 @@ 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) @@ -70,7 +70,7 @@ class ListTests(APISubdomainTestCase): 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) @@ -84,7 +84,7 @@ class ListTests(APISubdomainTestCase): def test_returns_single_item_with_random_items_param_set_to_1(self): """Return not-used name instead used.""" - url = reverse('bot:offtopicchannelname-list', host='api') + url = reverse('api:bot:offtopicchannelname-list') response = self.client.get(f'{url}?random_items=1') self.assertEqual(response.status_code, 200) @@ -93,25 +93,25 @@ class ListTests(APISubdomainTestCase): 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') + url = reverse('api:bot:offtopicchannelname-list') response = self.client.get(f'{url}?random_items=2') self.assertEqual(response.status_code, 200) self.assertEqual(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 +123,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 +132,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,7 +147,7 @@ class CreationTests(APISubdomainTestCase): }) -class DeletionTests(APISubdomainTestCase): +class DeletionTests(AuthenticatedAPITestCase): @classmethod def setUpTestData(cls): cls.test_name = OffTopicChannelName.objects.create(name='lemons-lemonade-stand') @@ -155,25 +155,25 @@ class DeletionTests(APISubdomainTestCase): 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') + 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..9b79b38c 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', @@ -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..d39cea4d 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 .base import AuthenticatedAPITestCase from ..models import Role -class CreationTests(APISubdomainTestCase): +class CreationTests(AuthenticatedAPITestCase): @classmethod def setUpTestData(cls): cls.admins_role = Role.objects.create( @@ -78,7 +78,7 @@ 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) @@ -92,7 +92,7 @@ class CreationTests(APISubdomainTestCase): 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 +107,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 +120,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 +133,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 +153,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 +165,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 +177,13 @@ 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_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..bed6342e 100644 --- a/pydis_site/apps/api/tests/test_users.py +++ b/pydis_site/apps/api/tests/test_users.py @@ -1,44 +1,44 @@ 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 -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 +57,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 +78,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 +103,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 +117,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 +128,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 +140,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 +160,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 +171,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 +195,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 +218,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 +234,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 +252,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 +268,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 +282,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 +297,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,7 +353,7 @@ 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 = [] @@ -367,7 +367,7 @@ 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"]) @@ -379,7 +379,7 @@ class UserPaginatorTests(APISubdomainTestCase): 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"]) @@ -390,12 +390,12 @@ class UserPaginatorTests(APISubdomainTestCase): 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,7 +413,7 @@ 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 @@ -430,7 +430,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 +441,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 +460,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 +475,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 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..922e6555 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 @@ -20,7 +20,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 +39,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 diff --git a/pydis_site/apps/content/resources/guides/pydis-guides/contributing/bot.md b/pydis_site/apps/content/resources/guides/pydis-guides/contributing/bot.md index 741bf28e..7419858e 100644 --- a/pydis_site/apps/content/resources/guides/pydis-guides/contributing/bot.md +++ b/pydis_site/apps/content/resources/guides/pydis-guides/contributing/bot.md @@ -43,7 +43,6 @@ For any staff member, since you have write permissions already to the original r # Development environment 1. [Clone your fork to a local project directory](../cloning-repository/) 2. [Install the project's dependencies](../installing-project-dependencies/) -3. [Prepare your hosts file (Optional)](../hosts-file/) --- # Test server and bot account diff --git a/pydis_site/apps/content/resources/guides/pydis-guides/contributing/hosts-file.md b/pydis_site/apps/content/resources/guides/pydis-guides/contributing/hosts-file.md index 5d55a7f3..bba5722d 100644 --- a/pydis_site/apps/content/resources/guides/pydis-guides/contributing/hosts-file.md +++ b/pydis_site/apps/content/resources/guides/pydis-guides/contributing/hosts-file.md @@ -8,16 +8,13 @@ toc: 3 # What's a hosts file? The hosts file maps a hostname/domain to an IP address, allowing you to visit a given domain on your browser and have it resolve by your system to the given IP address, even if it's pointed back to your own system or network. -When staging a local [Site](https://pythondiscord.com/pages/contributing/site/) project, you will need to add some entries to your hosts file so you can visit the site with the domain `http://pythondiscord.local` +When staging a local [Site](https://pythondiscord.com/pages/contributing/site/) project, you may want to add an entries to your hosts file so you can visit the site with the domain `http://pythondiscord.local`. This is purely for convenience, and you can use `localhost` or `127.0.0.1` instead if you prefer. # What to add -You would add the following entries to your hosts file. +You would add the following entry to your hosts file. ```plaintext 127.0.0.1 pythondiscord.local -127.0.0.1 api.pythondiscord.local -127.0.0.1 staff.pythondiscord.local -127.0.0.1 admin.pythondiscord.local ``` # How to add it diff --git a/pydis_site/apps/content/resources/guides/pydis-guides/contributing/site.md b/pydis_site/apps/content/resources/guides/pydis-guides/contributing/site.md index ada47931..c4f29887 100644 --- a/pydis_site/apps/content/resources/guides/pydis-guides/contributing/site.md +++ b/pydis_site/apps/content/resources/guides/pydis-guides/contributing/site.md @@ -43,7 +43,6 @@ For any Core Developers, since you have write permissions already to the origina 1. [Clone your fork to a local project directory](../cloning-repository/) 2. [Install the project's dependencies](../installing-project-dependencies/) -3. [Prepare your hosts file](../hosts-file/) ## Without Docker @@ -81,7 +80,8 @@ STATIC_ROOT=staticfiles #### Notes regarding `DATABASE_URL` -- If the database is hosted locally i.e. on the same machine as the webserver, then use `localhost` for the host. Windows and macOS users may need to use the [Docker host IP](../hosts-file/#windows) instead. +- If the database is hosted locally i.e. on the same machine as the webserver, then use `localhost` for the host. Windows and macOS users may + need to use the [Docker host IP](https://stackoverflow.com/questions/22944631/how-to-get-the-ip-address-of-the-docker-host-from-inside-a-docker-container) instead. - If the database is running in Docker, use port `7777`. Otherwise, use `5432` as that is the default port used by PostegreSQL. - If you configured PostgreSQL in a different manner or you are not hosting it locally, then you will need to determine the correct host and port yourself. The user, password, and database name should all still be `pysite` unless you deviated from the setup instructions in the previous section. @@ -142,4 +142,4 @@ Unless you are editing the Dockerfile or docker-compose.yml, you shouldn't need Django provides an interface for administration with which you can view and edit the models among other things. -It can be found at [http://admin.pythondiscord.local:8000](http://admin.pythondiscord.local:8000). The default credentials are `admin` for the username and `admin` for the password. +It can be found at [http://127.0.0.1:8000/admin/](http://127.0.0.1:8000/admin/). The default credentials are `admin` for the username and `admin` for the password. diff --git a/pydis_site/apps/events/tests/test_views.py b/pydis_site/apps/events/tests/test_views.py index 23c9e596..669fbf82 100644 --- a/pydis_site/apps/events/tests/test_views.py +++ b/pydis_site/apps/events/tests/test_views.py @@ -2,7 +2,7 @@ from pathlib import Path from django.conf import settings from django.test import TestCase, override_settings -from django_hosts.resolvers import reverse +from django.urls import reverse PAGES_PATH = Path(settings.BASE_DIR, "pydis_site", "templates", "events", "test-pages") @@ -21,8 +21,8 @@ class PageTests(TestCase): def test_valid_event_page_reponse_200(self): """Should return response code 200 when visiting valid event page.""" pages = ( - reverse("events:page", ("my-event",)), - reverse("events:page", ("my-event/subpage",)), + reverse("events:page", args=("my-event",)), + reverse("events:page", args=("my-event/subpage",)), ) for page in pages: with self.subTest(page=page): @@ -33,8 +33,8 @@ class PageTests(TestCase): def test_invalid_event_page_404(self): """Should return response code 404 when visiting invalid event page.""" pages = ( - reverse("events:page", ("invalid",)), - reverse("events:page", ("invalid/invalid",)) + reverse("events:page", args=("invalid",)), + reverse("events:page", args=("invalid/invalid",)) ) for page in pages: with self.subTest(page=page): diff --git a/pydis_site/apps/home/tests/test_views.py b/pydis_site/apps/home/tests/test_views.py index bd1671b1..b1215df4 100644 --- a/pydis_site/apps/home/tests/test_views.py +++ b/pydis_site/apps/home/tests/test_views.py @@ -1,10 +1,10 @@ from django.test import TestCase -from django_hosts.resolvers import reverse +from django.urls import reverse class TestIndexReturns200(TestCase): def test_index_returns_200(self): """Check that the index page returns a HTTP 200 response.""" - url = reverse('home') + url = reverse('home:home') resp = self.client.get(url) self.assertEqual(resp.status_code, 200) diff --git a/pydis_site/apps/home/urls.py b/pydis_site/apps/home/urls.py index 1e2af8f3..57abc942 100644 --- a/pydis_site/apps/home/urls.py +++ b/pydis_site/apps/home/urls.py @@ -1,15 +1,9 @@ -from django.contrib import admin -from django.urls import include, path +from django.urls import path from .views import HomeView, timeline app_name = 'home' urlpatterns = [ path('', HomeView.as_view(), name='home'), - path('', include('pydis_site.apps.redirect.urls')), - path('admin/', admin.site.urls), - path('resources/', include('pydis_site.apps.resources.urls')), - path('pages/', include('pydis_site.apps.content.urls')), - path('events/', include('pydis_site.apps.events.urls', namespace='events')), path('timeline/', timeline, name="timeline"), ] diff --git a/pydis_site/apps/redirect/tests.py b/pydis_site/apps/redirect/tests.py index 2cfa3478..c181d6e5 100644 --- a/pydis_site/apps/redirect/tests.py +++ b/pydis_site/apps/redirect/tests.py @@ -31,7 +31,7 @@ class RedirectTests(TestCase): ): resp = self.client.get( reverse( - f"home:redirect:{name}", + f"redirect:{name}", args=TESTING_ARGUMENTS.get(name, ()) ), follow=True @@ -53,7 +53,7 @@ class RedirectTests(TestCase): self.assertRedirects( resp, reverse( - f"home:{data['redirect_route']}", + f"{data['redirect_route']}", args=expected_args ), status_code=301 diff --git a/pydis_site/apps/resources/tests/test_views.py b/pydis_site/apps/resources/tests/test_views.py index 53685eef..3ad0b958 100644 --- a/pydis_site/apps/resources/tests/test_views.py +++ b/pydis_site/apps/resources/tests/test_views.py @@ -3,7 +3,7 @@ from unittest.mock import patch from django.conf import settings from django.test import TestCase -from django_hosts import reverse +from django.urls import reverse TESTING_RESOURCES_PATH = Path( settings.BASE_DIR, "pydis_site", "apps", "resources", "tests", "testing_resources" @@ -22,13 +22,13 @@ class TestResourcesListView(TestCase): @patch("pydis_site.apps.resources.views.resources_list.RESOURCES_PATH", TESTING_RESOURCES_PATH) def test_valid_resource_list_200(self): """Check does site return code 200 when visiting valid resource list.""" - url = reverse("resources:resources", ("testing",)) + url = reverse("resources:resources", args=("testing",)) response = self.client.get(url) self.assertEqual(response.status_code, 200) @patch("pydis_site.apps.resources.views.resources_list.RESOURCES_PATH", TESTING_RESOURCES_PATH) def test_invalid_resource_list_404(self): """Check does site return code 404 when trying to visit invalid resource list.""" - url = reverse("resources:resources", ("invalid",)) + url = reverse("resources:resources", args=("invalid",)) response = self.client.get(url) self.assertEqual(response.status_code, 404) diff --git a/pydis_site/apps/staff/tests/test_logs_view.py b/pydis_site/apps/staff/tests/test_logs_view.py index 00e0ab2f..45e9ce8f 100644 --- a/pydis_site/apps/staff/tests/test_logs_view.py +++ b/pydis_site/apps/staff/tests/test_logs_view.py @@ -1,6 +1,6 @@ -from django.test import Client, TestCase +from django.test import TestCase +from django.urls import reverse from django.utils import timezone -from django_hosts.resolvers import reverse, reverse_host from pydis_site.apps.api.models.bot import DeletedMessage, MessageDeletionContext, Role, User from pydis_site.apps.staff.templatetags.deletedmessage_filters import hex_colour @@ -105,22 +105,18 @@ class TestLogsView(TestCase): deletion_context=cls.deletion_context, ) - def setUp(self): - """Sets up a test client that automatically sets the correct HOST header.""" - self.client = Client(HTTP_HOST=reverse_host(host="staff")) - def test_logs_returns_200_for_existing_logs_pk(self): - url = reverse('logs', host="staff", args=(self.deletion_context.id,)) + url = reverse('staff:logs', args=(self.deletion_context.id,)) response = self.client.get(url) self.assertEqual(response.status_code, 200) def test_logs_returns_404_for_nonexisting_logs_pk(self): - url = reverse('logs', host="staff", args=(self.deletion_context.id + 100,)) + url = reverse('staff:logs', args=(self.deletion_context.id + 100,)) response = self.client.get(url) self.assertEqual(response.status_code, 404) def test_author_color_is_set_in_response(self): - url = reverse('logs', host="staff", args=(self.deletion_context.id,)) + url = reverse('staff:logs', args=(self.deletion_context.id,)) response = self.client.get(url) role_colour = hex_colour(self.developers_role.colour) html_needle = ( @@ -129,7 +125,7 @@ class TestLogsView(TestCase): self.assertInHTML(html_needle, response.content.decode()) def test_correct_messages_have_been_passed_to_template(self): - url = reverse('logs', host="staff", args=(self.deletion_context.id,)) + url = reverse('staff:logs', args=(self.deletion_context.id,)) response = self.client.get(url) self.assertIn("messages", response.context) self.assertListEqual( @@ -138,7 +134,7 @@ class TestLogsView(TestCase): ) def test_if_both_embeds_are_included_html_response(self): - url = reverse('logs', host="staff", args=(self.deletion_context.id,)) + url = reverse('staff:logs', args=(self.deletion_context.id,)) response = self.client.get(url) html_response = response.content.decode() @@ -151,7 +147,7 @@ class TestLogsView(TestCase): self.assertInHTML(embed_colour_needle.format(colour=embed_two_colour), html_response) def test_if_both_attachments_are_included_html_response(self): - url = reverse('logs', host="staff", args=(self.deletion_context.id,)) + url = reverse('staff:logs', args=(self.deletion_context.id,)) response = self.client.get(url) html_response = response.content.decode() @@ -166,7 +162,7 @@ class TestLogsView(TestCase): ) def test_if_html_in_content_is_properly_escaped(self): - url = reverse('logs', host="staff", args=(self.deletion_context.id,)) + url = reverse('staff:logs', args=(self.deletion_context.id,)) response = self.client.get(url) html_response = response.content.decode() diff --git a/pydis_site/hosts.py b/pydis_site/hosts.py deleted file mode 100644 index 5a837a8b..00000000 --- a/pydis_site/hosts.py +++ /dev/null @@ -1,13 +0,0 @@ -from django.conf import settings -from django_hosts import host, patterns - -host_patterns = patterns( - '', - host(r'admin', 'pydis_site.apps.admin.urls', name="admin"), - # External API ingress (over the net) - host(r'api', 'pydis_site.apps.api.urls', name='api'), - # Internal API ingress (cluster local) - host(r'pydis-api', 'pydis_site.apps.api.urls', name='internal_api'), - host(r'staff', 'pydis_site.apps.staff.urls', name='staff'), - host(r'.*', 'pydis_site.apps.home.urls', name=settings.DEFAULT_HOST) -) diff --git a/pydis_site/settings.py b/pydis_site/settings.py index 7df7ad85..f7c4401d 100644 --- a/pydis_site/settings.py +++ b/pydis_site/settings.py @@ -52,13 +52,7 @@ elif 'CI' in os.environ: else: ALLOWED_HOSTS = env.list( 'ALLOWED_HOSTS', - default=[ - 'pythondiscord.com', - 'admin.pythondiscord.com', - 'api.pythondiscord.com', - 'staff.pythondiscord.com', - 'pydis-api.default.svc.cluster.local', - ] + default=['pythondiscord.com'], ) SECRET_KEY = env('SECRET_KEY') @@ -80,7 +74,6 @@ INSTALLED_APPS = [ 'django.contrib.sites', 'django.contrib.staticfiles', - 'django_hosts', 'django_filters', 'django_simple_bulma', 'rest_framework', @@ -88,8 +81,6 @@ INSTALLED_APPS = [ ] MIDDLEWARE = [ - 'django_hosts.middleware.HostsRequestMiddleware', - 'django.middleware.security.SecurityMiddleware', 'whitenoise.middleware.WhiteNoiseMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', @@ -98,8 +89,6 @@ MIDDLEWARE = [ 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', - - 'django_hosts.middleware.HostsResponseMiddleware', ] ROOT_URLCONF = 'pydis_site.urls' @@ -109,10 +98,6 @@ TEMPLATES = [ 'DIRS': [os.path.join(BASE_DIR, 'pydis_site', 'templates')], 'APP_DIRS': True, 'OPTIONS': { - 'builtins': [ - 'django_hosts.templatetags.hosts_override', - ], - 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', @@ -174,11 +159,6 @@ STATICFILES_FINDERS = [ 'django_simple_bulma.finders.SimpleBulmaFinder', ] -# django-hosts -# https://django-hosts.readthedocs.io/en/latest/ -ROOT_HOSTCONF = 'pydis_site.hosts' -DEFAULT_HOST = 'home' - if DEBUG: PARENT_HOST = env('PARENT_HOST', default='pythondiscord.local:8000') diff --git a/pydis_site/templates/base/navbar.html b/pydis_site/templates/base/navbar.html index 4cc49dc6..7d5119c7 100644 --- a/pydis_site/templates/base/navbar.html +++ b/pydis_site/templates/base/navbar.html @@ -76,7 +76,7 @@ FAQ - + Timeline diff --git a/pydis_site/templates/home/index.html b/pydis_site/templates/home/index.html index 18f6b77b..cbede038 100644 --- a/pydis_site/templates/home/index.html +++ b/pydis_site/templates/home/index.html @@ -17,7 +17,7 @@
Thanks to all our members for helping us create this friendly and helpful community!

- As a nice treat, we've created a
Timeline page for people + As a nice treat, we've created a Timeline page for people to discover the events that made our community what it is today. Be sure to check it out!
@@ -108,7 +108,7 @@

- + Check it out! diff --git a/pydis_site/urls.py b/pydis_site/urls.py index 47cf0ba1..3c9fe347 100644 --- a/pydis_site/urls.py +++ b/pydis_site/urls.py @@ -1,7 +1,21 @@ +from django.contrib import admin from django.urls import include, path urlpatterns = ( - path('', include('pydis_site.apps.home.urls', namespace='home')), + path('admin/', admin.site.urls), + + # External API ingress (over the net) + path('api/', include('pydis_site.apps.api.urls', namespace='api')), + # Internal API ingress (cluster local) + path('pydis-api/', include('pydis_site.apps.api.urls', namespace='internal_api')), + + # This must be mounted before the `content` app to prevent Django + # from wildcard matching all requests to `pages/...`. + path('', include('pydis_site.apps.redirect.urls')), + path('pages/', include('pydis_site.apps.content.urls', namespace='content')), + path('resources/', include('pydis_site.apps.resources.urls')), + path('events/', include('pydis_site.apps.events.urls', namespace='events')), path('staff/', include('pydis_site.apps.staff.urls', namespace='staff')), + path('', include('pydis_site.apps.home.urls', namespace='home')), ) diff --git a/pyproject.toml b/pyproject.toml index 5bbf86ee..0b546781 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,6 @@ python = "3.9.*" django = "~=3.0.4" django-environ = "~=0.4.5" django-filter = "~=2.1.0" -django-hosts = "~=4.0" djangorestframework = "~=3.11.0" psycopg2-binary = "~=2.8" django-simple-bulma = "~=2.1" -- cgit v1.2.3 From 6abf47abd515ec789ad2f271dcbc48403a3e0d7e Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Wed, 22 Sep 2021 10:49:36 +0100 Subject: Rename an error to have a correct error suffix --- pydis_site/apps/api/models/bot/metricity.py | 10 +++++----- pydis_site/apps/api/tests/test_users.py | 10 +++++----- pydis_site/apps/api/viewsets/bot/user.py | 6 +++--- 3 files changed, 13 insertions(+), 13 deletions(-) (limited to 'pydis_site/apps/api/models') diff --git a/pydis_site/apps/api/models/bot/metricity.py b/pydis_site/apps/api/models/bot/metricity.py index 5daa5c66..33fb7ad7 100644 --- a/pydis_site/apps/api/models/bot/metricity.py +++ b/pydis_site/apps/api/models/bot/metricity.py @@ -10,7 +10,7 @@ EXCLUDE_CHANNELS = [ ] -class NotFound(Exception): +class NotFoundError(Exception): """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)) @@ -58,7 +58,7 @@ class Metricity: values = self.cursor.fetchone() if not values: - raise NotFound() + raise NotFoundError() return values[0] @@ -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/tests/test_users.py b/pydis_site/apps/api/tests/test_users.py index c43b916a..ed5b4ce3 100644 --- a/pydis_site/apps/api/tests/test_users.py +++ b/pydis_site/apps/api/tests/test_users.py @@ -5,7 +5,7 @@ from django_hosts.resolvers import reverse from .base import APISubdomainTestCase from ..models import Role, User -from ..models.bot.metricity import NotFound +from ..models.bot.metricity import NotFoundError class UnauthedUserAPITests(APISubdomainTestCase): @@ -501,7 +501,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/viewsets/bot/user.py b/pydis_site/apps/api/viewsets/bot/user.py index 25722f5a..0356e193 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 @@ -275,7 +275,7 @@ class UserViewSet(ModelViewSet): data["voice_banned"] = voice_banned data["activity_blocks"] = metricity.total_message_blocks(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) @@ -290,6 +290,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) -- cgit v1.2.3 From 3cb0a6bdcd7e7fca560fbea4d2bc3ff50bbd8cfa Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Mon, 27 Sep 2021 21:35:28 +0100 Subject: Add LT and GT to supported otn chars --- .../apps/api/migrations/0073_otn_allow_GT_and_LT.py | 19 +++++++++++++++++++ .../apps/api/models/bot/off_topic_channel_name.py | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 pydis_site/apps/api/migrations/0073_otn_allow_GT_and_LT.py (limited to 'pydis_site/apps/api/models') 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/models/bot/off_topic_channel_name.py b/pydis_site/apps/api/models/bot/off_topic_channel_name.py index 403c7465..8999e560 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,7 +11,7 @@ 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." ) -- cgit v1.2.3 From 367c01a7b3cd5cc5ee25644ae92f917ceb00562c Mon Sep 17 00:00:00 2001 From: Hassan Abouelela Date: Fri, 29 Oct 2021 03:19:37 +0400 Subject: Adds Failures Field To Reminders Adds an integer field to reminders, to keep track of how many failed attempts at delivering the reminder were made. Updates documentation and serialization. Signed-off-by: Hassan Abouelela --- .../apps/api/migrations/0074_reminder_failures.py | 18 ++++++++++++++++++ pydis_site/apps/api/models/bot/reminder.py | 4 ++++ pydis_site/apps/api/serializers.py | 10 +++++++++- pydis_site/apps/api/viewsets/bot/reminder.py | 13 ++++++++----- 4 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 pydis_site/apps/api/migrations/0074_reminder_failures.py (limited to 'pydis_site/apps/api/models') 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/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/serializers.py b/pydis_site/apps/api/serializers.py index f47bedca..3e213d43 100644 --- a/pydis_site/apps/api/serializers.py +++ b/pydis_site/apps/api/serializers.py @@ -231,7 +231,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/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///" + ... 'jump_url': "https://discord.com/channels///", + ... '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///" + ... 'jump_url': "https://discord.com/channels///", + ... '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 -- cgit v1.2.3 From 7353e1ed7003a1198fb00a1e40251b95cf2fdf7e Mon Sep 17 00:00:00 2001 From: Hassan Abouelela Date: Wed, 17 Nov 2021 14:27:45 +0400 Subject: Adds Redirect Filter List Adds a new filter list for URLs which should be treated as redirects and unfurled. Signed-off-by: Hassan Abouelela --- .../apps/api/migrations/0075_add_redirects_filter.py | 18 ++++++++++++++++++ pydis_site/apps/api/models/bot/filter_list.py | 1 + pydis_site/apps/api/viewsets/bot/filter_list.py | 3 ++- 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 pydis_site/apps/api/migrations/0075_add_redirects_filter.py (limited to 'pydis_site/apps/api/models') 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/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/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 -- cgit v1.2.3 From 5b1bb82165d188a571c8999f17ef52f3856c9518 Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Tue, 23 Nov 2021 16:29:06 +0000 Subject: Alter message query to leverage index Previously this query would convert each row to an array just to check if it matched or not. By changing EXCLUDE_CHANNELS to a tuple instead of a list, it doesn't get passed as an array, so we can do a simple NOT IN check. This will also allow us to add an index with this condition to speed it up further. --- pydis_site/apps/api/models/bot/metricity.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'pydis_site/apps/api/models') diff --git a/pydis_site/apps/api/models/bot/metricity.py b/pydis_site/apps/api/models/bot/metricity.py index 33fb7ad7..901f191a 100644 --- a/pydis_site/apps/api/models/bot/metricity.py +++ b/pydis_site/apps/api/models/bot/metricity.py @@ -4,10 +4,10 @@ from django.db import connections BLOCK_INTERVAL = 10 * 60 # 10 minute blocks -EXCLUDE_CHANNELS = [ +EXCLUDE_CHANNELS = ( "267659945086812160", # Bot commands "607247579608121354" # SeasonalBot commands -] +) class NotFoundError(Exception): @@ -46,12 +46,12 @@ 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] ) @@ -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; """, -- cgit v1.2.3 From 61f9daed8d71046351e60c57ccdbf34443085561 Mon Sep 17 00:00:00 2001 From: Izan Date: Wed, 10 Nov 2021 22:44:20 +0000 Subject: Add `dm_sent` field to infractions model & serializer --- .../apps/api/migrations/0075_infraction_dm_sent.py | 18 ++++++++++++++++++ pydis_site/apps/api/models/bot/infraction.py | 4 ++++ pydis_site/apps/api/serializers.py | 2 +- 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 pydis_site/apps/api/migrations/0075_infraction_dm_sent.py (limited to 'pydis_site/apps/api/models') 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/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/serializers.py b/pydis_site/apps/api/serializers.py index 3e213d43..f6801597 100644 --- a/pydis_site/apps/api/serializers.py +++ b/pydis_site/apps/api/serializers.py @@ -145,7 +145,7 @@ 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( -- cgit v1.2.3 From 3f4adf3768123c870f9da38a23b189862a7b9502 Mon Sep 17 00:00:00 2001 From: Johannes Christ Date: Sat, 27 Nov 2021 13:28:23 +0100 Subject: Migrate to generic `JSONField` --- .../api/migrations/0077_use_generic_jsonfield.py | 25 ++++++++++++++++++++++ pydis_site/apps/api/models/bot/bot_setting.py | 3 +-- pydis_site/apps/api/models/bot/message.py | 2 +- pydis_site/apps/api/models/utils.py | 3 +-- 4 files changed, 28 insertions(+), 5 deletions(-) create mode 100644 pydis_site/apps/api/migrations/0077_use_generic_jsonfield.py (limited to 'pydis_site/apps/api/models') 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/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/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/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, ... ) -- cgit v1.2.3 From 953f232d47ff4e07baeaffa8add7912ff05d6457 Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Sat, 11 Dec 2021 13:59:40 +0000 Subject: Use new approx message count view We have added a new view to metricity that will keep track of an approximate message count, updating every 10 seconds. By doing this, we avoid running a query against the whole message table every time we want to get a user's messages. --- pydis_site/apps/api/models/bot/metricity.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'pydis_site/apps/api/models') diff --git a/pydis_site/apps/api/models/bot/metricity.py b/pydis_site/apps/api/models/bot/metricity.py index 901f191a..52e946ac 100644 --- a/pydis_site/apps/api/models/bot/metricity.py +++ b/pydis_site/apps/api/models/bot/metricity.py @@ -46,14 +46,12 @@ class Metricity: self.cursor.execute( """ SELECT - COUNT(*) - FROM messages + message_count + FROM user_has_approx_message_count WHERE author_id = '%s' - AND NOT is_deleted - AND channel_id NOT IN %s """, - [user_id, EXCLUDE_CHANNELS] + [user_id] ) values = self.cursor.fetchone() -- cgit v1.2.3 From f65184b5e454437c3c524d1236041f170fff3ea4 Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Tue, 14 Dec 2021 18:59:49 +0000 Subject: Query message count directly from messages This was changed due to performance reasons, but after some tweaking in the database, such as increasing work memory and adding an index, this query runs much faster now. To test this, I want to revert this change, so that we can stop the materialised view from refreshing, to see if the act of refreshing is what's causing this query to seem faster when runing against the database. --- pydis_site/apps/api/models/bot/metricity.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'pydis_site/apps/api/models') diff --git a/pydis_site/apps/api/models/bot/metricity.py b/pydis_site/apps/api/models/bot/metricity.py index 52e946ac..901f191a 100644 --- a/pydis_site/apps/api/models/bot/metricity.py +++ b/pydis_site/apps/api/models/bot/metricity.py @@ -46,12 +46,14 @@ class Metricity: self.cursor.execute( """ SELECT - message_count - FROM user_has_approx_message_count + COUNT(*) + FROM messages WHERE author_id = '%s' + AND NOT is_deleted + AND channel_id NOT IN %s """, - [user_id] + [user_id, EXCLUDE_CHANNELS] ) values = self.cursor.fetchone() -- cgit v1.2.3