Merge pull request #1529 from normanjaeckel/RESTConfig
Added config update view.
This commit is contained in:
commit
1ccd2ab91c
@ -36,6 +36,7 @@ Other:
|
|||||||
- Refactored projector API using metaclasses now.
|
- Refactored projector API using metaclasses now.
|
||||||
- Renamed SignalConnectMetaClass classmethod get_all_objects to get_all
|
- Renamed SignalConnectMetaClass classmethod get_all_objects to get_all
|
||||||
(private API).
|
(private API).
|
||||||
|
- Refactored config API.
|
||||||
- Used AngularJS with additional libraries for single page frontend.
|
- Used AngularJS with additional libraries for single page frontend.
|
||||||
- Removed use of 'django.views.i18n.javascript_catalog'. Used angular-gettext
|
- Removed use of 'django.views.i18n.javascript_catalog'. Used angular-gettext
|
||||||
now.
|
now.
|
||||||
|
@ -19,25 +19,21 @@ class ConfigHandler(object):
|
|||||||
return self[key]
|
return self[key]
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
def __setitem__(self, key, value):
|
||||||
# Save the new value to the database
|
# Check if the variable is defined.
|
||||||
|
if key not in self:
|
||||||
|
raise ConfigNotFound('The config variable %s was not found.' % key)
|
||||||
|
|
||||||
|
# Save the new value to the database.
|
||||||
updated_rows = ConfigStore.objects.filter(key=key).update(value=value)
|
updated_rows = ConfigStore.objects.filter(key=key).update(value=value)
|
||||||
if not updated_rows:
|
if not updated_rows:
|
||||||
ConfigStore.objects.create(key=key, value=value)
|
ConfigStore.objects.create(key=key, value=value)
|
||||||
|
|
||||||
# Update cache
|
# Update cache.
|
||||||
try:
|
self._cache[key] = value
|
||||||
self._cache[key] = value
|
|
||||||
except AttributeError:
|
|
||||||
# This happens, when a config-var is set, before __getitem__ was
|
|
||||||
# called. In this case nothing should happen.
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Call on_change callback
|
# Call on_change callback.
|
||||||
for receiver, config_collection in config_signal.send(sender='set_value'):
|
if self.get_config_variables()[key].on_change:
|
||||||
for config_variable in config_collection.variables:
|
self.get_config_variables()[key].on_change()
|
||||||
if config_variable.name == key and config_variable.on_change:
|
|
||||||
config_variable.on_change()
|
|
||||||
break
|
|
||||||
|
|
||||||
def items(self):
|
def items(self):
|
||||||
"""
|
"""
|
||||||
@ -47,15 +43,27 @@ class ConfigHandler(object):
|
|||||||
self.setup_cache()
|
self.setup_cache()
|
||||||
return self._cache.items()
|
return self._cache.items()
|
||||||
|
|
||||||
|
def get_config_variables(self):
|
||||||
|
"""
|
||||||
|
Returns a dictionary with all ConfigVariable instances of all
|
||||||
|
collections. The key is the name of the config variables.
|
||||||
|
"""
|
||||||
|
result = {}
|
||||||
|
for receiver, config_collection in config_signal.send(sender='get_config_variables'):
|
||||||
|
for config_variable in config_collection.variables:
|
||||||
|
if config_variable.name in result:
|
||||||
|
raise ConfigError('Too many values for config variable %s found.' % config_variable.name)
|
||||||
|
result[config_variable.name] = config_variable
|
||||||
|
return result
|
||||||
|
|
||||||
def get_default(self, key):
|
def get_default(self, key):
|
||||||
"""
|
"""
|
||||||
Returns the default value for 'key'.
|
Returns the default value for 'key'.
|
||||||
"""
|
"""
|
||||||
for receiver, config_collection in config_signal.send(sender='get_default'):
|
try:
|
||||||
for config_variable in config_collection.variables:
|
return self.get_config_variables()[key].default_value
|
||||||
if config_variable.name == key:
|
except KeyError:
|
||||||
return config_variable.default_value
|
raise ConfigNotFound('The config variable %s was not found.' % key)
|
||||||
raise ConfigNotFound('The config variable %s was not found.' % key)
|
|
||||||
|
|
||||||
def setup_cache(self):
|
def setup_cache(self):
|
||||||
"""
|
"""
|
||||||
@ -63,11 +71,8 @@ class ConfigHandler(object):
|
|||||||
save the default to the cache.
|
save the default to the cache.
|
||||||
"""
|
"""
|
||||||
self._cache = {}
|
self._cache = {}
|
||||||
for receiver, config_collection in config_signal.send(sender='setup_cache'):
|
for key, config_variable in self.get_config_variables().items():
|
||||||
for config_variable in config_collection.variables:
|
self._cache[key] = config_variable.default_value
|
||||||
if config_variable.name in self._cache:
|
|
||||||
raise ConfigError('Too many values for config variable %s found.' % config_variable.name)
|
|
||||||
self._cache[config_variable.name] = config_variable.default_value
|
|
||||||
for config_object in ConfigStore.objects.all():
|
for config_object in ConfigStore.objects.all():
|
||||||
self._cache[config_object.key] = config_object.value
|
self._cache[config_object.key] = config_object.value
|
||||||
|
|
||||||
@ -84,10 +89,9 @@ class ConfigHandler(object):
|
|||||||
Generator to get all config variables as strings when their values are
|
Generator to get all config variables as strings when their values are
|
||||||
intended to be translated.
|
intended to be translated.
|
||||||
"""
|
"""
|
||||||
for receiver, config_collection in config_signal.send(sender='get_all_translatable'):
|
for config_variable in self.get_config_variables().values():
|
||||||
for config_variable in config_collection.variables:
|
if config_variable.translatable:
|
||||||
if config_variable.translatable:
|
yield config_variable.name
|
||||||
yield config_variable.name
|
|
||||||
|
|
||||||
config = ConfigHandler()
|
config = ConfigHandler()
|
||||||
"""
|
"""
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
|
from django.core.exceptions import ValidationError as DjangoValidationError
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from openslides.utils.rest_api import Response, ViewSet
|
from openslides.utils.rest_api import Response, ValidationError, ViewSet
|
||||||
from openslides.utils.views import FormView
|
from openslides.utils.views import FormView
|
||||||
|
|
||||||
from .api import config
|
from .api import config
|
||||||
@ -119,7 +120,7 @@ class ConfigViewSet(ViewSet):
|
|||||||
|
|
||||||
def retrieve(self, request, *args, **kwargs):
|
def retrieve(self, request, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Retrieves one config variable.
|
Retrieves one config variable. Everybody can see it.
|
||||||
"""
|
"""
|
||||||
# TODO: Check if we need permission check here.
|
# TODO: Check if we need permission check here.
|
||||||
key = kwargs['pk']
|
key = kwargs['pk']
|
||||||
@ -129,12 +130,32 @@ class ConfigViewSet(ViewSet):
|
|||||||
raise Http404
|
raise Http404
|
||||||
return Response(data)
|
return Response(data)
|
||||||
|
|
||||||
def update(self, request, pk=None):
|
def update(self, request, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
TODO
|
Updates one config variable. Only managers can do this.
|
||||||
|
|
||||||
|
Example: {"value": 42}
|
||||||
"""
|
"""
|
||||||
|
# Check permission.
|
||||||
if not request.user.has_perm('config.can_manage'):
|
if not request.user.has_perm('config.can_manage'):
|
||||||
self.permission_denied(request)
|
self.permission_denied(request)
|
||||||
else:
|
|
||||||
# TODO: Implement update method
|
# Check if pk is a valid config variable key.
|
||||||
self.permission_denied(request)
|
key = kwargs['pk']
|
||||||
|
if key not in config:
|
||||||
|
raise Http404
|
||||||
|
|
||||||
|
# Validate value.
|
||||||
|
form_field = config.get_config_variables()[key].form_field
|
||||||
|
value = request.data['value']
|
||||||
|
if form_field:
|
||||||
|
try:
|
||||||
|
form_field.clean(value)
|
||||||
|
except DjangoValidationError as e:
|
||||||
|
raise ValidationError({'detail': e.messages[0]})
|
||||||
|
|
||||||
|
# Change value.
|
||||||
|
config[key] = value
|
||||||
|
|
||||||
|
# Return response.
|
||||||
|
return Response({'key': key, 'value': value})
|
||||||
|
0
tests/integration/config/__init__.py
Normal file
0
tests/integration/config/__init__.py
Normal file
60
tests/integration/config/test_views.py
Normal file
60
tests/integration/config/test_views.py
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
from django import forms
|
||||||
|
from django.dispatch import receiver
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
from rest_framework import status
|
||||||
|
from rest_framework.test import APIClient
|
||||||
|
|
||||||
|
from openslides.config.api import config, ConfigCollection, ConfigVariable
|
||||||
|
from openslides.config.signals import config_signal
|
||||||
|
from openslides.utils.test import TestCase
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigViewSet(TestCase):
|
||||||
|
"""
|
||||||
|
Tests requests to deal with config variables.
|
||||||
|
"""
|
||||||
|
def test_retrieve(self):
|
||||||
|
self.client.login(username='admin', password='admin')
|
||||||
|
config['test_var_aeW3Quahkah1phahCheo'] = 'test_value_Oovoojieme7eephaed2A'
|
||||||
|
response = self.client.get(reverse('config-detail', args=['test_var_aeW3Quahkah1phahCheo']))
|
||||||
|
self.assertEqual(
|
||||||
|
response.data,
|
||||||
|
{'key': 'test_var_aeW3Quahkah1phahCheo',
|
||||||
|
'value': 'test_value_Oovoojieme7eephaed2A'})
|
||||||
|
|
||||||
|
def test_update(self):
|
||||||
|
self.client = APIClient()
|
||||||
|
self.client.login(username='admin', password='admin')
|
||||||
|
response = self.client.put(
|
||||||
|
reverse('config-detail', args=['test_var_Xeiizi7ooH8Thuk5aida']),
|
||||||
|
{'value': 'test_value_Phohx3oopeichaiTheiw'})
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(config['test_var_Xeiizi7ooH8Thuk5aida'], 'test_value_Phohx3oopeichaiTheiw')
|
||||||
|
|
||||||
|
def test_update_wrong_datatype(self):
|
||||||
|
self.client = APIClient()
|
||||||
|
self.client.login(username='admin', password='admin')
|
||||||
|
response = self.client.put(
|
||||||
|
reverse('config-detail', args=['test_var_ohhii4iavoh5Phoh5ahg']),
|
||||||
|
{'value': 'test_value_string'})
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertEqual(response.data, {'detail': 'Enter a whole number.'})
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(config_signal, dispatch_uid='set_simple_config_view_integration_config_test')
|
||||||
|
def set_simple_config_view_integration_config_test(sender, **kwargs):
|
||||||
|
"""
|
||||||
|
Sets a simple config view with some config variables but without
|
||||||
|
grouping.
|
||||||
|
"""
|
||||||
|
return ConfigCollection(
|
||||||
|
title='Config vars for testing',
|
||||||
|
url='test_url_ieXao5Wae5Duoy6Wohtu',
|
||||||
|
variables=(ConfigVariable(name='test_var_aeW3Quahkah1phahCheo',
|
||||||
|
default_value=None),
|
||||||
|
ConfigVariable(name='test_var_Xeiizi7ooH8Thuk5aida',
|
||||||
|
default_value='',
|
||||||
|
form_field=forms.CharField()),
|
||||||
|
ConfigVariable(name='test_var_ohhii4iavoh5Phoh5ahg',
|
||||||
|
default_value=0,
|
||||||
|
form_field=forms.IntegerField())))
|
@ -67,9 +67,9 @@ class HandleConfigTest(TestCase):
|
|||||||
|
|
||||||
def test_set_value_before_getting_it(self):
|
def test_set_value_before_getting_it(self):
|
||||||
"""
|
"""
|
||||||
Try to call __setitem__ before __getitem.
|
Try to call __setitem__ before __getitem__.
|
||||||
"""
|
"""
|
||||||
config['my_config_var'] = 'value'
|
config['additional_config_var'] = 'value'
|
||||||
|
|
||||||
def test_on_change(self):
|
def test_on_change(self):
|
||||||
"""
|
"""
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
from django.dispatch import receiver
|
||||||
from django.template import Context, Template
|
from django.template import Context, Template
|
||||||
|
|
||||||
from openslides.config.api import config
|
from openslides.config.api import ConfigCollection, ConfigVariable, config
|
||||||
|
from openslides.config.signals import config_signal
|
||||||
from openslides.utils.test import TestCase
|
from openslides.utils.test import TestCase
|
||||||
|
|
||||||
|
|
||||||
@ -27,3 +29,18 @@ class ConfigTagAndFilter(TestCase):
|
|||||||
template = Template(template_code)
|
template = Template(template_code)
|
||||||
self.assertTrue('FdgfkR04jtg9f8bq' in template.render(Context({})))
|
self.assertTrue('FdgfkR04jtg9f8bq' in template.render(Context({})))
|
||||||
self.assertFalse('bad_e0fvkfHFD' in template.render(Context({})))
|
self.assertFalse('bad_e0fvkfHFD' in template.render(Context({})))
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(config_signal, dispatch_uid='set_simple_config_view_template_tag_test')
|
||||||
|
def set_simple_config_view_template_tag_test(sender, **kwargs):
|
||||||
|
"""
|
||||||
|
Sets a simple config view with some config variables but without
|
||||||
|
grouping.
|
||||||
|
"""
|
||||||
|
return ConfigCollection(
|
||||||
|
title='Config vars for testing with template tag',
|
||||||
|
url='testsimplepagetemplatetag',
|
||||||
|
variables=(ConfigVariable(name='taiNg3reQuooGha4', default_value=None),
|
||||||
|
ConfigVariable(name='fkjTze56ncuejWqs', default_value=None),
|
||||||
|
ConfigVariable(name='jfhsnezfh452w6Fg', default_value=None),
|
||||||
|
ConfigVariable(name='sdmvldkfgj4534gk', default_value=None)))
|
||||||
|
Loading…
Reference in New Issue
Block a user