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. - 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.

View File

@ -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()
""" """

View File

@ -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})

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): 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):
""" """

View File

@ -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)))