Merge pull request #1529 from normanjaeckel/RESTConfig

Added config update view.
This commit is contained in:
Oskar Hahn 2015-06-16 16:18:33 +02:00
commit 1ccd2ab91c
7 changed files with 141 additions and 38 deletions

View File

@ -36,6 +36,7 @@ Other:
- Refactored projector API using metaclasses now.
- Renamed SignalConnectMetaClass classmethod get_all_objects to get_all
(private API).
- Refactored config API.
- Used AngularJS with additional libraries for single page frontend.
- Removed use of 'django.views.i18n.javascript_catalog'. Used angular-gettext
now.

View File

@ -19,25 +19,21 @@ class ConfigHandler(object):
return self[key]
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)
if not updated_rows:
ConfigStore.objects.create(key=key, value=value)
# Update cache
try:
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
# Update cache.
self._cache[key] = value
# Call on_change callback
for receiver, config_collection in config_signal.send(sender='set_value'):
for config_variable in config_collection.variables:
if config_variable.name == key and config_variable.on_change:
config_variable.on_change()
break
# Call on_change callback.
if self.get_config_variables()[key].on_change:
self.get_config_variables()[key].on_change()
def items(self):
"""
@ -47,15 +43,27 @@ class ConfigHandler(object):
self.setup_cache()
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):
"""
Returns the default value for 'key'.
"""
for receiver, config_collection in config_signal.send(sender='get_default'):
for config_variable in config_collection.variables:
if config_variable.name == key:
return config_variable.default_value
raise ConfigNotFound('The config variable %s was not found.' % key)
try:
return self.get_config_variables()[key].default_value
except KeyError:
raise ConfigNotFound('The config variable %s was not found.' % key)
def setup_cache(self):
"""
@ -63,11 +71,8 @@ class ConfigHandler(object):
save the default to the cache.
"""
self._cache = {}
for receiver, config_collection in config_signal.send(sender='setup_cache'):
for config_variable in config_collection.variables:
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 key, config_variable in self.get_config_variables().items():
self._cache[key] = config_variable.default_value
for config_object in ConfigStore.objects.all():
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
intended to be translated.
"""
for receiver, config_collection in config_signal.send(sender='get_all_translatable'):
for config_variable in config_collection.variables:
if config_variable.translatable:
yield config_variable.name
for config_variable in self.get_config_variables().values():
if config_variable.translatable:
yield config_variable.name
config = ConfigHandler()
"""

View File

@ -1,10 +1,11 @@
from django import forms
from django.contrib import messages
from django.core.urlresolvers import reverse
from django.core.exceptions import ValidationError as DjangoValidationError
from django.http import Http404
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 .api import config
@ -119,7 +120,7 @@ class ConfigViewSet(ViewSet):
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.
key = kwargs['pk']
@ -129,12 +130,32 @@ class ConfigViewSet(ViewSet):
raise Http404
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'):
self.permission_denied(request)
else:
# TODO: Implement update method
self.permission_denied(request)
# Check if pk is a valid config variable key.
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})

View File

View 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())))

View File

@ -67,9 +67,9 @@ class HandleConfigTest(TestCase):
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):
"""

View File

@ -1,6 +1,8 @@
from django.dispatch import receiver
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
@ -27,3 +29,18 @@ class ConfigTagAndFilter(TestCase):
template = Template(template_code)
self.assertTrue('FdgfkR04jtg9f8bq' 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)))