From 732de97ec285fc1cf5e10ab082c1f5b374b4ca47 Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Thu, 30 Aug 2018 09:07:06 +0200 Subject: [PATCH] Make Consumer real async again --- openslides/core/apps.py | 9 +++++ openslides/core/views.py | 15 +++------ openslides/utils/constants.py | 40 +++++++++++++++++++++++ openslides/utils/consumers.py | 21 ++++-------- tests/conftest.py | 19 +++++++++++ tests/integration/utils/test_consumers.py | 15 +++++++++ 6 files changed, 93 insertions(+), 26 deletions(-) create mode 100644 openslides/utils/constants.py diff --git a/openslides/core/apps.py b/openslides/core/apps.py index 305778978..56fa8af78 100644 --- a/openslides/core/apps.py +++ b/openslides/core/apps.py @@ -4,6 +4,7 @@ from typing import Any, Dict, List from django.apps import AppConfig from django.conf import settings +from django.core.exceptions import ImproperlyConfigured from django.db.models.signals import post_migrate from ..utils.projector import register_projector_elements @@ -37,6 +38,14 @@ class CoreAppConfig(AppConfig): ProjectorViewSet, TagViewSet, ) + from ..utils.constants import set_constants, get_constants_from_apps + + # Set constants + try: + set_constants(get_constants_from_apps()) + except ImproperlyConfigured: + # Database is not loaded. This happens in tests. + pass # Define config variables and projector elements. config.update_config_variables(get_config_variables()) diff --git a/openslides/core/views.py b/openslides/core/views.py index 7e53e4530..7d987fed9 100644 --- a/openslides/core/views.py +++ b/openslides/core/views.py @@ -48,6 +48,7 @@ from .models import ( ProjectorMessage, Tag, ) +from ..utils.constants import get_constants # Special Django views @@ -133,17 +134,9 @@ class WebclientJavaScriptView(utils_views.View): # angular constants angular_constants = '' - for app in apps.get_app_configs(): - try: - # Each app can deliver values to angular when implementing this method. - # It should return a list with dicts containing the 'name' and 'value'. - get_angular_constants = app.get_angular_constants - except AttributeError: - # The app doesn't have this method. Continue to next app. - continue - for key, value in get_angular_constants().items(): - value = json.dumps(value) - angular_constants += ".constant('{}', {})".format(key, value) + for key, value in get_constants().items(): + value = json.dumps(value) + angular_constants += ".constant('{}', {})".format(key, value) # Use JavaScript loadScript function from # http://balpha.de/2011/10/jquery-script-insertion-and-its-consequences-for-debugging/ diff --git a/openslides/utils/constants.py b/openslides/utils/constants.py new file mode 100644 index 000000000..d0717aef4 --- /dev/null +++ b/openslides/utils/constants.py @@ -0,0 +1,40 @@ +from typing import Any, Dict + +from django.apps import apps + + +def get_constants_from_apps() -> Dict[str, Any]: + out: Dict[str, Any] = {} + for app in apps.get_app_configs(): + try: + # Each app can deliver values to angular when implementing this method. + # It should return a list with dicts containing the 'name' and 'value'. + get_angular_constants = app.get_angular_constants + except AttributeError: + # The app doesn't have this method. Continue to next app. + continue + out.update(get_angular_constants()) + return out + + +constants = None + + +def get_constants() -> Dict[str, Any]: + """ + Returns the constants. + + This method only returns a static dict, so it is fast and can be used in a + async context. + """ + if constants is None: + raise RuntimeError("Constants are not set.") + return constants + + +def set_constants(value: Dict[str, Any]) -> None: + """ + Sets the constants variable. + """ + global constants + constants = value diff --git a/openslides/utils/consumers.py b/openslides/utils/consumers.py index 1c91981ad..1fbe5edc7 100644 --- a/openslides/utils/consumers.py +++ b/openslides/utils/consumers.py @@ -4,7 +4,6 @@ import jsonschema from asgiref.sync import sync_to_async from channels.db import database_sync_to_async from channels.generic.websocket import AsyncJsonWebsocketConsumer -from django.apps import apps from ..core.config import config from ..core.models import Projector @@ -16,6 +15,7 @@ from .collection import ( format_for_autoupdate, from_channel_message, ) +from .constants import get_constants class ProtocollAsyncJsonWebsocketConsumer(AsyncJsonWebsocketConsumer): @@ -31,7 +31,7 @@ class ProtocollAsyncJsonWebsocketConsumer(AsyncJsonWebsocketConsumer): "type": { "description": "Defines what kind of packages is packed.", "type": "string", - "pattern": "notify|constantsRequest", # The server can sent other types + "pattern": "notify|constants", # The server can sent other types }, "content": { "description": "The content of the package.", @@ -137,19 +137,10 @@ class SiteConsumer(ProtocollAsyncJsonWebsocketConsumer): ) else: await self.send_json(type='error', content='Invalid notify message', in_response=id) - elif type == 'constantsRequest': - angular_constants: Dict[str, Any] = {} - for app in apps.get_app_configs(): - try: - # Each app can deliver values to angular when implementing this method. - # It should return a list with dicts containing the 'name' and 'value'. - get_angular_constants = app.get_angular_constants - except AttributeError: - # The app doesn't have this method. Continue to next app. - continue - constants = await database_sync_to_async(get_angular_constants)() - angular_constants.update(constants) - await self.send_json(type='constantsResponse', content=angular_constants, in_response=id) + + elif type == 'constants': + # Return all constants to the client. + await self.send_json(type='constants', content=get_constants(), in_response=id) async def send_notify(self, event: Dict[str, Any]) -> None: """ diff --git a/tests/conftest.py b/tests/conftest.py index d288c1458..d029d2942 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,6 @@ +import pytest from django.test import TestCase, TransactionTestCase +from pytest_django.django_compat import is_django_unittest from pytest_django.plugin import validate_django_db @@ -38,3 +40,20 @@ def pytest_collection_modifyitems(items): return 0 items.sort(key=weight_test_case) + + +@pytest.fixture(autouse=True) +def constants(request): + """ + Resets the constants on every test. + + Uses fake constants, if the db is not in use. + """ + from openslides.utils.constants import set_constants, get_constants_from_apps + + if 'django_db' in request.node.keywords or is_django_unittest(request): + # When the db is created, use the original constants + set_constants(get_constants_from_apps()) + else: + # Else: Use fake constants + set_constants({'constant1': 'value1', 'constant2': 'value2'}) diff --git a/tests/integration/utils/test_consumers.py b/tests/integration/utils/test_consumers.py index 1752d1102..40a16e017 100644 --- a/tests/integration/utils/test_consumers.py +++ b/tests/integration/utils/test_consumers.py @@ -241,3 +241,18 @@ async def test_send_unknown_type(communicator): response = await communicator.receive_json_from() assert response['type'] == 'error' assert response['in_response'] == 'test_id' + + +@pytest.mark.asyncio +async def test_request_constants(communicator, settings): + await set_config('general_system_enable_anonymous', True) + await communicator.connect() + # Await the startup data + await communicator.receive_json_from() + + await communicator.send_json_to({'type': 'constants', 'content': '', 'id': 'test_id'}) + + response = await communicator.receive_json_from() + assert response['type'] == 'constants' + # See conftest.py for the content of 'content' + assert response['content'] == {'constant1': 'value1', 'constant2': 'value2'}