Browse Source

fix #914: moved api-specific utils and features from misago.core to misago.api

Rafał Pitoń 7 years ago
parent
commit
e5aa78dda3
38 changed files with 143 additions and 153 deletions
  1. 1 0
      misago/api/__init__.py
  2. 7 0
      misago/api/apps.py
  3. 7 0
      misago/api/context_processors.py
  4. 20 0
      misago/api/exceptionhandler.py
  5. 0 0
      misago/api/middleware.py
  6. 0 0
      misago/api/patch.py
  7. 0 0
      misago/api/rest_permissions.py
  8. 0 0
      misago/api/router.py
  9. 0 0
      misago/api/serializers.py
  10. 0 0
      misago/api/tests/__init__.py
  11. 26 0
      misago/api/tests/test_context_processors.py
  12. 52 0
      misago/api/tests/test_exceptionhandler.py
  13. 1 1
      misago/api/tests/test_frontendcontext_middleware.py
  14. 1 1
      misago/api/tests/test_patch.py
  15. 1 1
      misago/api/tests/test_serializers.py
  16. 1 1
      misago/categories/serializers.py
  17. 1 1
      misago/categories/urls/api.py
  18. 0 9
      misago/core/context_processors.py
  19. 0 17
      misago/core/exceptionhandler.py
  20. 0 19
      misago/core/tests/test_context_processors.py
  21. 0 38
      misago/core/tests/test_exceptionhandlers.py
  22. 1 35
      misago/core/tests/test_utils.py
  23. 0 7
      misago/core/utils.py
  24. 6 5
      misago/project_template/project_name/settings.py
  25. 0 1
      misago/threads/api/postendpoints/delete.py
  26. 1 1
      misago/threads/api/postendpoints/patch_event.py
  27. 1 1
      misago/threads/api/postendpoints/patch_post.py
  28. 1 1
      misago/threads/api/threadendpoints/patch.py
  29. 1 1
      misago/threads/serializers/feed.py
  30. 1 1
      misago/threads/serializers/post.py
  31. 1 1
      misago/threads/serializers/thread.py
  32. 1 1
      misago/threads/urls/api.py
  33. 1 1
      misago/users/api/users.py
  34. 1 1
      misago/users/forms/auth.py
  35. 3 3
      misago/users/serializers/ban.py
  36. 1 1
      misago/users/serializers/user.py
  37. 4 3
      misago/users/tests/test_auth_api.py
  38. 1 1
      misago/users/urls/api.py

+ 1 - 0
misago/api/__init__.py

@@ -0,0 +1 @@
+default_app_config = 'misago.api.apps.MisagoApiConfig'

+ 7 - 0
misago/api/apps.py

@@ -0,0 +1,7 @@
+from django.apps import AppConfig
+
+
+class MisagoApiConfig(AppConfig):
+    name = 'misago.api'
+    label = 'misago_api'
+    verbose_name = "Misago Api"

+ 7 - 0
misago/api/context_processors.py

@@ -0,0 +1,7 @@
+def frontend_context(request):
+    if request.include_frontend_context:
+        return {
+            'frontend_context': request.frontend_context,
+        }
+    else:
+        return {}

+ 20 - 0
misago/api/exceptionhandler.py

@@ -0,0 +1,20 @@
+from rest_framework.views import exception_handler as rest_exception_handler
+
+from django.core.exceptions import PermissionDenied
+from django.utils import six
+
+from misago.core.exceptions import Banned
+
+
+def handle_api_exception(exception, context):
+    response = rest_exception_handler(exception, context)
+    if response:
+        if isinstance(exception, Banned):
+            response.data = exception.ban.get_serialized_message()
+        elif isinstance(exception, PermissionDenied) and exception.args:
+            response.data = {
+                'detail': six.text_type(exception),
+            }
+        return response
+    else:
+        return None

+ 0 - 0
misago/core/middleware/frontendcontext.py → misago/api/middleware.py


+ 0 - 0
misago/core/apipatch.py → misago/api/patch.py


+ 0 - 0
misago/core/rest_permissions.py → misago/api/rest_permissions.py


+ 0 - 0
misago/core/apirouter.py → misago/api/router.py


+ 0 - 0
misago/core/serializers.py → misago/api/serializers.py


+ 0 - 0
misago/api/tests/__init__.py


+ 26 - 0
misago/api/tests/test_context_processors.py

@@ -0,0 +1,26 @@
+from django.test import TestCase
+
+from misago.api import context_processors
+
+
+class MockRequest(object):
+    pass
+
+
+class FrontendContextTests(TestCase):
+    def test_frontend_context(self):
+        """frontend_context is available in templates"""
+        mock_request = MockRequest()
+        mock_request.include_frontend_context = True
+        mock_request.frontend_context = {'someValue': 'Something'}
+
+        self.assertEqual(
+            context_processors.frontend_context(mock_request), {
+                'frontend_context': {
+                    'someValue': 'Something',
+                },
+            }
+        )
+
+        mock_request.include_frontend_context = False
+        self.assertEqual(context_processors.frontend_context(mock_request), {})

+ 52 - 0
misago/api/tests/test_exceptionhandler.py

@@ -0,0 +1,52 @@
+from django.core import exceptions as django_exceptions
+from django.core.exceptions import PermissionDenied
+from django.http import Http404
+from django.test import TestCase
+
+from misago.api import exceptionhandler
+from misago.core.exceptions import Banned
+from misago.users.models import Ban
+
+
+INVALID_EXCEPTIONS = [
+    django_exceptions.ObjectDoesNotExist,
+    django_exceptions.ViewDoesNotExist,
+    TypeError,
+    ValueError,
+    KeyError,
+]
+
+
+class HandleAPIExceptionTests(TestCase):
+    def test_banned(self):
+        """banned exception is correctly handled"""
+        ban = Ban(user_message="This is test ban!")
+
+        response = exceptionhandler.handle_api_exception(Banned(ban), None)
+
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.data['detail']['html'], "<p>This is test ban!</p>")
+        self.assertIn('expires_on', response.data)
+
+    def test_permission_denied(self):
+        """permission denied exception is correctly handled"""
+        response = exceptionhandler.handle_api_exception(PermissionDenied(), None)
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.data['detail'], "Permission denied.")
+
+    def test_permission_message_denied(self):
+        """permission denied with message is correctly handled"""
+        exception = PermissionDenied("You shall not pass!")
+        response = exceptionhandler.handle_api_exception(exception, None)
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.data['detail'], "You shall not pass!")
+
+    def test_unhandled_exception(self):
+        """our exception handler is not interrupting other exceptions"""
+        for exception in INVALID_EXCEPTIONS:
+            response = exceptionhandler.handle_api_exception(exception(), None)
+            self.assertIsNone(response)
+
+        response = exceptionhandler.handle_api_exception(Http404(), None)
+        self.assertEqual(response.status_code, 404)
+        self.assertEqual(response.data['detail'], "Not found.")

+ 1 - 1
misago/core/tests/test_frontendcontext_middleware.py → misago/api/tests/test_frontendcontext_middleware.py

@@ -1,6 +1,6 @@
 from django.test import TestCase
 from django.test import TestCase
 
 
-from misago.core.middleware.frontendcontext import FrontendContextMiddleware
+from misago.api.middleware import FrontendContextMiddleware
 
 
 
 
 class MockRequest(object):
 class MockRequest(object):

+ 1 - 1
misago/core/tests/test_apipatch.py → misago/api/tests/test_patch.py

@@ -2,7 +2,7 @@ from django.core.exceptions import PermissionDenied
 from django.http import Http404
 from django.http import Http404
 from django.test import TestCase
 from django.test import TestCase
 
 
-from misago.core.apipatch import ApiPatch, InvalidAction
+from misago.api.patch import ApiPatch, InvalidAction
 
 
 
 
 class MockRequest(object):
 class MockRequest(object):

+ 1 - 1
misago/core/tests/test_serializers.py → misago/api/tests/test_serializers.py

@@ -2,8 +2,8 @@ from rest_framework import serializers
 
 
 from django.test import TestCase
 from django.test import TestCase
 
 
+from misago.api.serializers import MutableFields
 from misago.categories.models import Category
 from misago.categories.models import Category
-from misago.core.serializers import MutableFields
 from misago.threads import testutils
 from misago.threads import testutils
 from misago.threads.models import Thread
 from misago.threads.models import Thread
 
 

+ 1 - 1
misago/categories/serializers.py

@@ -2,7 +2,7 @@ from rest_framework import serializers
 
 
 from django.urls import reverse
 from django.urls import reverse
 
 
-from misago.core.serializers import MutableFields
+from misago.api.serializers import MutableFields
 from misago.core.utils import format_plaintext_for_html
 from misago.core.utils import format_plaintext_for_html
 
 
 from .models import Category
 from .models import Category

+ 1 - 1
misago/categories/urls/api.py

@@ -1,5 +1,5 @@
+from misago.api.router import MisagoApiRouter
 from misago.categories.api import CategoryViewSet
 from misago.categories.api import CategoryViewSet
-from misago.core.apirouter import MisagoApiRouter
 
 
 
 
 router = MisagoApiRouter()
 router = MisagoApiRouter()

+ 0 - 9
misago/core/context_processors.py

@@ -25,12 +25,3 @@ def momentjs_locale(request):
     return {
     return {
         'MOMENTJS_LOCALE_URL': get_locale_url(get_language()),
         'MOMENTJS_LOCALE_URL': get_locale_url(get_language()),
     }
     }
-
-
-def frontend_context(request):
-    if request.include_frontend_context:
-        return {
-            'frontend_context': request.frontend_context,
-        }
-    else:
-        return {}

+ 0 - 17
misago/core/exceptionhandler.py

@@ -1,5 +1,3 @@
-from rest_framework.views import exception_handler as rest_exception_handler
-
 from django.core.exceptions import PermissionDenied
 from django.core.exceptions import PermissionDenied
 from django.http import Http404, HttpResponsePermanentRedirect, JsonResponse
 from django.http import Http404, HttpResponsePermanentRedirect, JsonResponse
 from django.urls import reverse
 from django.urls import reverse
@@ -87,18 +85,3 @@ def get_exception_handler(exception):
 def handle_misago_exception(request, exception):
 def handle_misago_exception(request, exception):
     handler = get_exception_handler(exception)
     handler = get_exception_handler(exception)
     return handler(request, exception)
     return handler(request, exception)
-
-
-def handle_api_exception(exception, context):
-    response = rest_exception_handler(exception, context)
-    if response:
-        if isinstance(exception, Banned):
-            response.data['ban'] = exception.ban.get_serialized_message()
-        elif isinstance(exception, PermissionDenied):
-            try:
-                response.data['detail'] = exception.args[0]
-            except IndexError:
-                pass
-        return response
-    else:
-        return None

+ 0 - 19
misago/core/tests/test_context_processors.py

@@ -79,22 +79,3 @@ class SiteAddressTests(TestCase):
                 'SITE_PROTOCOL': 'https',
                 'SITE_PROTOCOL': 'https',
             }
             }
         )
         )
-
-
-class FrontendContextTests(TestCase):
-    def test_frontend_context(self):
-        """frontend_context is available in templates"""
-        mock_request = MockRequest(False, 'somewhere.com')
-        mock_request.include_frontend_context = True
-        mock_request.frontend_context = {'someValue': 'Something'}
-
-        self.assertEqual(
-            context_processors.frontend_context(mock_request), {
-                'frontend_context': {
-                    'someValue': 'Something',
-                },
-            }
-        )
-
-        mock_request.include_frontend_context = False
-        self.assertEqual(context_processors.frontend_context(mock_request), {})

+ 0 - 38
misago/core/tests/test_exceptionhandlers.py

@@ -1,11 +1,7 @@
 from django.core import exceptions as django_exceptions
 from django.core import exceptions as django_exceptions
-from django.core.exceptions import PermissionDenied
-from django.http import Http404
 from django.test import TestCase
 from django.test import TestCase
 
 
 from misago.core import exceptionhandler
 from misago.core import exceptionhandler
-from misago.core.exceptions import Banned
-from misago.users.models import Ban
 
 
 
 
 INVALID_EXCEPTIONS = [
 INVALID_EXCEPTIONS = [
@@ -46,37 +42,3 @@ class GetExceptionHandlerTests(TestCase):
         for exception in INVALID_EXCEPTIONS:
         for exception in INVALID_EXCEPTIONS:
             with self.assertRaises(ValueError):
             with self.assertRaises(ValueError):
                 exceptionhandler.get_exception_handler(exception())
                 exceptionhandler.get_exception_handler(exception())
-
-
-class HandleAPIExceptionTests(TestCase):
-    def test_banned(self):
-        """banned exception is correctly handled"""
-        ban = Ban(user_message="This is test ban!")
-
-        response = exceptionhandler.handle_api_exception(Banned(ban), None)
-
-        self.assertEqual(response.status_code, 403)
-        self.assertEqual(response.data['ban']['message']['html'], "<p>This is test ban!</p>")
-
-    def test_permission_denied(self):
-        """permission denied exception is correctly handled"""
-        response = exceptionhandler.handle_api_exception(PermissionDenied(), None)
-        self.assertEqual(response.status_code, 403)
-        self.assertEqual(response.data['detail'], "Permission denied.")
-
-    def test_permission_message_denied(self):
-        """permission denied with message is correctly handled"""
-        exception = PermissionDenied("You shall not pass!")
-        response = exceptionhandler.handle_api_exception(exception, None)
-        self.assertEqual(response.status_code, 403)
-        self.assertEqual(response.data['detail'], "You shall not pass!")
-
-    def test_unhandled_exception(self):
-        """our exception handler is not interrupting other exceptions"""
-        for exception in INVALID_EXCEPTIONS:
-            response = exceptionhandler.handle_api_exception(exception(), None)
-            self.assertIsNone(response)
-
-        response = exceptionhandler.handle_api_exception(Http404(), None)
-        self.assertEqual(response.status_code, 404)
-        self.assertEqual(response.data['detail'], "Not found.")

+ 1 - 35
misago/core/tests/test_utils.py

@@ -9,7 +9,7 @@ from django.utils import six
 
 
 from misago.core.utils import (
 from misago.core.utils import (
     clean_return_path, format_plaintext_for_html, is_referer_local, is_request_to_misago,
     clean_return_path, format_plaintext_for_html, is_referer_local, is_request_to_misago,
-    parse_iso8601_string, slugify, get_exception_message, clean_ids_list)
+    parse_iso8601_string, slugify, get_exception_message)
 
 
 
 
 VALID_PATHS = ("/", "/threads/", )
 VALID_PATHS = ("/", "/threads/", )
@@ -266,37 +266,3 @@ class GetExceptionMessageTests(TestCase):
 
 
         message = get_exception_message(default_message='Lorem Ipsum')
         message = get_exception_message(default_message='Lorem Ipsum')
         self.assertEqual(message, 'Lorem Ipsum')
         self.assertEqual(message, 'Lorem Ipsum')
-
-
-class CleanIdsListTests(TestCase):
-    def test_valid_list(self):
-        """list of valid ids is cleaned"""
-        self.assertEqual(clean_ids_list(['1', 3, '42'], None), [1, 3, 42])
-
-    def test_empty_list(self):
-        """empty list passes validation"""
-        self.assertEqual(clean_ids_list([], None), [])
-
-    def test_string_list(self):
-        """string list passes validation"""
-        self.assertEqual(clean_ids_list('1234', None), [1, 2, 3, 4])
-
-    def test_message(self):
-        """utility uses passed message for exception"""
-        with self.assertRaisesMessage(PermissionDenied, "Test error message!"):
-            clean_ids_list(None, "Test error message!")
-
-    def test_invalid_inputs(self):
-        """utility raises exception for invalid inputs"""
-        INVALID_INPUTS = (
-            None,
-            'abc',
-            [None],
-            [1, 2, 'a', 4],
-            [1, None, 3],
-            {1: 2, 'a': 4},
-        )
-
-        for invalid_input in INVALID_INPUTS:
-            with self.assertRaisesMessage(PermissionDenied, "Test error message!"):
-                clean_ids_list(invalid_input, "Test error message!")

+ 0 - 7
misago/core/utils.py

@@ -147,10 +147,3 @@ def get_exception_message(exception=None, default_message=None):
         return exception.args[0]
         return exception.args[0]
     except IndexError:
     except IndexError:
         return default_message
         return default_message
-
-
-def clean_ids_list(ids_list, error_message):
-    try:
-        return list(map(int, ids_list))
-    except (ValueError, TypeError):
-        raise PermissionDenied(error_message)

+ 6 - 5
misago/project_template/project_name/settings.py

@@ -188,8 +188,9 @@ INSTALLED_APPS = [
     'rest_framework',
     'rest_framework',
 
 
     # Misago apps
     # Misago apps
-    'misago.admin',
     'misago.acl',
     'misago.acl',
+    'misago.admin',
+    'misago.api',
     'misago.core',
     'misago.core',
     'misago.conf',
     'misago.conf',
     'misago.markup',
     'misago.markup',
@@ -215,7 +216,7 @@ MIDDLEWARE = [
     'debug_toolbar.middleware.DebugToolbarMiddleware',
     'debug_toolbar.middleware.DebugToolbarMiddleware',
 
 
     'misago.users.middleware.RealIPMiddleware',
     'misago.users.middleware.RealIPMiddleware',
-    'misago.core.middleware.frontendcontext.FrontendContextMiddleware',
+    'misago.api.middleware.FrontendContextMiddleware',
 
 
     'django.middleware.security.SecurityMiddleware',
     'django.middleware.security.SecurityMiddleware',
     'django.contrib.sessions.middleware.SessionMiddleware',
     'django.contrib.sessions.middleware.SessionMiddleware',
@@ -266,7 +267,7 @@ TEMPLATES = [
 
 
                 # Note: keep frontend_context processor last for previous processors
                 # Note: keep frontend_context processor last for previous processors
                 # to be able to expose data UI app via request.frontend_context
                 # to be able to expose data UI app via request.frontend_context
-                'misago.core.context_processors.frontend_context',
+                'misago.api.context_processors.frontend_context',
             ],
             ],
         },
         },
     },
     },
@@ -307,12 +308,12 @@ DEBUG_TOOLBAR_PANELS = [
 
 
 REST_FRAMEWORK = {
 REST_FRAMEWORK = {
     'DEFAULT_PERMISSION_CLASSES': [
     'DEFAULT_PERMISSION_CLASSES': [
-        'misago.core.rest_permissions.IsAuthenticatedOrReadOnly',
+        'misago.api.rest_permissions.IsAuthenticatedOrReadOnly',
     ],
     ],
     'DEFAULT_RENDERER_CLASSES': [
     'DEFAULT_RENDERER_CLASSES': [
         'rest_framework.renderers.JSONRenderer',
         'rest_framework.renderers.JSONRenderer',
     ],
     ],
-    'EXCEPTION_HANDLER': 'misago.core.exceptionhandler.handle_api_exception',
+    'EXCEPTION_HANDLER': 'misago.api.exceptionhandler.handle_api_exception',
     'UNAUTHENTICATED_USER': 'misago.users.models.AnonymousUser',
     'UNAUTHENTICATED_USER': 'misago.users.models.AnonymousUser',
     'URL_FORMAT_OVERRIDE': None,
     'URL_FORMAT_OVERRIDE': None,
 }
 }

+ 0 - 1
misago/threads/api/postendpoints/delete.py

@@ -5,7 +5,6 @@ from django.utils.translation import ugettext as _
 from django.utils.translation import ungettext
 from django.utils.translation import ungettext
 
 
 from misago.conf import settings
 from misago.conf import settings
-from misago.core.utils import clean_ids_list
 from misago.threads.moderation import posts as moderation
 from misago.threads.moderation import posts as moderation
 from misago.threads.permissions import allow_delete_event, allow_delete_post
 from misago.threads.permissions import allow_delete_event, allow_delete_post
 from misago.threads.permissions import exclude_invisible_posts
 from misago.threads.permissions import exclude_invisible_posts

+ 1 - 1
misago/threads/api/postendpoints/patch_event.py

@@ -2,7 +2,7 @@ from django.core.exceptions import PermissionDenied
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
 
 
 from misago.acl import add_acl
 from misago.acl import add_acl
-from misago.core.apipatch import ApiPatch
+from misago.api.patch import ApiPatch
 from misago.threads.moderation import posts as moderation
 from misago.threads.moderation import posts as moderation
 from misago.threads.permissions import allow_hide_event, allow_unhide_event
 from misago.threads.permissions import allow_hide_event, allow_unhide_event
 
 

+ 1 - 1
misago/threads/api/postendpoints/patch_post.py

@@ -5,8 +5,8 @@ from django.core.exceptions import PermissionDenied
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
 
 
 from misago.acl import add_acl
 from misago.acl import add_acl
+from misago.api.patch import ApiPatch
 from misago.conf import settings
 from misago.conf import settings
-from misago.core.apipatch import ApiPatch
 from misago.threads.models import PostLike
 from misago.threads.models import PostLike
 from misago.threads.moderation import posts as moderation
 from misago.threads.moderation import posts as moderation
 from misago.threads.permissions import (
 from misago.threads.permissions import (

+ 1 - 1
misago/threads/api/threadendpoints/patch.py

@@ -9,11 +9,11 @@ from django.utils import six
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
 
 
 from misago.acl import add_acl
 from misago.acl import add_acl
+from misago.api.patch import ApiPatch
 from misago.categories.models import Category
 from misago.categories.models import Category
 from misago.categories.permissions import allow_browse_category, allow_see_category
 from misago.categories.permissions import allow_browse_category, allow_see_category
 from misago.categories.serializers import CategorySerializer
 from misago.categories.serializers import CategorySerializer
 from misago.conf import settings
 from misago.conf import settings
-from misago.core.apipatch import ApiPatch
 from misago.core.shortcuts import get_int_or_404
 from misago.core.shortcuts import get_int_or_404
 from misago.threads.moderation import threads as moderation
 from misago.threads.moderation import threads as moderation
 from misago.threads.participants import (
 from misago.threads.participants import (

+ 1 - 1
misago/threads/serializers/feed.py

@@ -1,7 +1,7 @@
 from rest_framework import serializers
 from rest_framework import serializers
 
 
+from misago.api.serializers import MutableFields
 from misago.categories.serializers import CategorySerializer
 from misago.categories.serializers import CategorySerializer
-from misago.core.serializers import MutableFields
 from misago.threads.models import Post
 from misago.threads.models import Post
 from misago.users.serializers import UserSerializer
 from misago.users.serializers import UserSerializer
 
 

+ 1 - 1
misago/threads/serializers/post.py

@@ -2,7 +2,7 @@ from rest_framework import serializers
 
 
 from django.urls import reverse
 from django.urls import reverse
 
 
-from misago.core.serializers import MutableFields
+from misago.api.serializers import MutableFields
 from misago.threads.models import Post
 from misago.threads.models import Post
 from misago.users.serializers import UserSerializer as BaseUserSerializer
 from misago.users.serializers import UserSerializer as BaseUserSerializer
 
 

+ 1 - 1
misago/threads/serializers/thread.py

@@ -2,8 +2,8 @@ from rest_framework import serializers
 
 
 from django.urls import reverse
 from django.urls import reverse
 
 
+from misago.api.serializers import MutableFields
 from misago.categories.serializers import CategorySerializer
 from misago.categories.serializers import CategorySerializer
-from misago.core.serializers import MutableFields
 from misago.threads.models import Thread
 from misago.threads.models import Thread
 
 
 from .poll import PollSerializer
 from .poll import PollSerializer

+ 1 - 1
misago/threads/urls/api.py

@@ -1,4 +1,4 @@
-from misago.core.apirouter import MisagoApiRouter
+from misago.api.router import MisagoApiRouter
 from misago.threads.api.attachments import AttachmentViewSet
 from misago.threads.api.attachments import AttachmentViewSet
 from misago.threads.api.threadpoll import ThreadPollViewSet
 from misago.threads.api.threadpoll import ThreadPollViewSet
 from misago.threads.api.threadposts import PrivateThreadPostsViewSet, ThreadPostsViewSet
 from misago.threads.api.threadposts import PrivateThreadPostsViewSet, ThreadPostsViewSet

+ 1 - 1
misago/users/api/users.py

@@ -12,8 +12,8 @@ from django.shortcuts import get_object_or_404
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
 
 
 from misago.acl import add_acl
 from misago.acl import add_acl
+from misago.api.rest_permissions import IsAuthenticatedOrReadOnly
 from misago.categories.models import Category
 from misago.categories.models import Category
-from misago.core.rest_permissions import IsAuthenticatedOrReadOnly
 from misago.core.shortcuts import get_int_or_404
 from misago.core.shortcuts import get_int_or_404
 from misago.threads.moderation import hide_post, hide_thread
 from misago.threads.moderation import hide_post, hide_thread
 from misago.users.bans import get_user_ban
 from misago.users.bans import get_user_ban

+ 1 - 1
misago/users/forms/auth.py

@@ -37,7 +37,7 @@ class MisagoAuthMixin(object):
     def get_errors_dict(self):
     def get_errors_dict(self):
         error = self.errors.as_data()['__all__'][0]
         error = self.errors.as_data()['__all__'][0]
         if error.code == 'banned':
         if error.code == 'banned':
-            error.message = self.user_ban.ban.get_serialized_message()
+            return self.user_ban.ban.get_serialized_message()
         else:
         else:
             error.message = error.messages[0]
             error.message = error.messages[0]
 
 

+ 3 - 3
misago/users/serializers/ban.py

@@ -23,16 +23,16 @@ def serialize_message(message):
 
 
 
 
 class BanMessageSerializer(serializers.ModelSerializer):
 class BanMessageSerializer(serializers.ModelSerializer):
-    message = serializers.SerializerMethodField()
+    detail = serializers.SerializerMethodField()
 
 
     class Meta:
     class Meta:
         model = Ban
         model = Ban
         fields = [
         fields = [
-            'message',
+            'detail',
             'expires_on',
             'expires_on',
         ]
         ]
 
 
-    def get_message(self, obj):
+    def get_detail(self, obj):
         if obj.user_message:
         if obj.user_message:
             message = obj.user_message
             message = obj.user_message
         elif obj.check_type == Ban.IP:
         elif obj.check_type == Ban.IP:

+ 1 - 1
misago/users/serializers/user.py

@@ -3,7 +3,7 @@ from rest_framework import serializers
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model
 from django.urls import reverse
 from django.urls import reverse
 
 
-from misago.core.serializers import MutableFields
+from misago.api.serializers import MutableFields
 
 
 from . import RankSerializer
 from . import RankSerializer
 
 

+ 4 - 3
misago/users/tests/test_auth_api.py

@@ -113,11 +113,12 @@ class GatewayTests(TestCase):
         self.assertEqual(response.status_code, 400)
         self.assertEqual(response.status_code, 400)
 
 
         response_json = response.json()
         response_json = response.json()
-        self.assertEqual(response_json['code'], 'banned')
-        self.assertEqual(response_json['detail']['message']['plain'], ban.user_message)
+
+        self.assertEqual(response_json['detail']['plain'], ban.user_message)
         self.assertEqual(
         self.assertEqual(
-            response_json['detail']['message']['html'], '<p>%s</p>' % ban.user_message
+            response_json['detail']['html'], '<p>%s</p>' % ban.user_message
         )
         )
+        self.assertIn('expires_on', response_json)
 
 
         response = self.client.get('/api/auth/')
         response = self.client.get('/api/auth/')
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)

+ 1 - 1
misago/users/urls/api.py

@@ -1,6 +1,6 @@
 from django.conf.urls import url
 from django.conf.urls import url
 
 
-from misago.core.apirouter import MisagoApiRouter
+from misago.api.router import MisagoApiRouter
 from misago.users.api import auth, captcha, mention
 from misago.users.api import auth, captcha, mention
 from misago.users.api.ranks import RanksViewSet
 from misago.users.api.ranks import RanksViewSet
 from misago.users.api.usernamechanges import UsernameChangesViewSet
 from misago.users.api.usernamechanges import UsernameChangesViewSet