Show More
@@ -9,6 +9,25 b' from tornado import web' | |||||
9 |
|
9 | |||
10 | from ...base.handlers import IPythonHandler, json_errors |
|
10 | from ...base.handlers import IPythonHandler, json_errors | |
11 |
|
11 | |||
|
12 | def recursive_update(target, new): | |||
|
13 | """Recursively update one dictionary using another. | |||
|
14 | ||||
|
15 | None values will delete their keys. | |||
|
16 | """ | |||
|
17 | for k, v in new.items(): | |||
|
18 | if isinstance(v, dict): | |||
|
19 | if k not in target: | |||
|
20 | target[k] = {} | |||
|
21 | recursive_update(target[k], v) | |||
|
22 | if not target[k]: | |||
|
23 | # Prune empty subdicts | |||
|
24 | del target[k] | |||
|
25 | ||||
|
26 | elif v is None: | |||
|
27 | target.pop(k, None) | |||
|
28 | ||||
|
29 | else: | |||
|
30 | target[k] = v | |||
12 |
|
31 | |||
13 | class ConfigHandler(IPythonHandler): |
|
32 | class ConfigHandler(IPythonHandler): | |
14 | SUPPORTED_METHODS = ('GET', 'PUT', 'PATCH') |
|
33 | SUPPORTED_METHODS = ('GET', 'PUT', 'PATCH') | |
@@ -46,11 +65,8 b' class ConfigHandler(IPythonHandler):' | |||||
46 | else: |
|
65 | else: | |
47 | section = {} |
|
66 | section = {} | |
48 |
|
67 | |||
49 |
|
|
68 | update = self.get_json_body() | |
50 | if v is None: |
|
69 | recursive_update(section, update) | |
51 | section.pop(k, None) |
|
|||
52 | else: |
|
|||
53 | section[k] = v |
|
|||
54 |
|
70 | |||
55 | with io.open(filename, 'w', encoding='utf-8') as f: |
|
71 | with io.open(filename, 'w', encoding='utf-8') as f: | |
56 | json.dump(section, f) |
|
72 | json.dump(section, f) |
@@ -1,5 +1,5 b'' | |||||
1 | # coding: utf-8 |
|
1 | # coding: utf-8 | |
2 |
"""Test the |
|
2 | """Test the config webservice API.""" | |
3 |
|
3 | |||
4 | import json |
|
4 | import json | |
5 |
|
5 | |||
@@ -32,7 +32,7 b' class ConfigAPI(object):' | |||||
32 | return self._req('PATCH', section, json.dumps(values)) |
|
32 | return self._req('PATCH', section, json.dumps(values)) | |
33 |
|
33 | |||
34 | class APITest(NotebookTestBase): |
|
34 | class APITest(NotebookTestBase): | |
35 |
"""Test the |
|
35 | """Test the config web service API""" | |
36 | def setUp(self): |
|
36 | def setUp(self): | |
37 | self.config_api = ConfigAPI(self.base_url()) |
|
37 | self.config_api = ConfigAPI(self.base_url()) | |
38 |
|
38 | |||
@@ -46,18 +46,22 b' class APITest(NotebookTestBase):' | |||||
46 | self.assertEqual(r.json(), sample) |
|
46 | self.assertEqual(r.json(), sample) | |
47 |
|
47 | |||
48 | def test_modify(self): |
|
48 | def test_modify(self): | |
49 |
sample = {'foo': 'bar', 'baz': 73 |
|
49 | sample = {'foo': 'bar', 'baz': 73, | |
|
50 | 'sub': {'a': 6, 'b': 7}, 'sub2': {'c': 8}} | |||
50 | self.config_api.set('example', sample) |
|
51 | self.config_api.set('example', sample) | |
51 |
|
52 | |||
52 | r = self.config_api.modify('example', {'foo': None, # should delete foo |
|
53 | r = self.config_api.modify('example', {'foo': None, # should delete foo | |
53 | 'baz': 75, |
|
54 | 'baz': 75, | |
54 | 'wib': [1,2,3], |
|
55 | 'wib': [1,2,3], | |
|
56 | 'sub': {'a': 8, 'b': None, 'd': 9}, | |||
|
57 | 'sub2': {'c': None} # should delete sub2 | |||
55 | }) |
|
58 | }) | |
56 | self.assertEqual(r.status_code, 204) |
|
59 | self.assertEqual(r.status_code, 204) | |
57 |
|
60 | |||
58 | r = self.config_api.get('example') |
|
61 | r = self.config_api.get('example') | |
59 | self.assertEqual(r.status_code, 200) |
|
62 | self.assertEqual(r.status_code, 200) | |
60 |
self.assertEqual(r.json(), {'baz': 75, 'wib': [1,2,3] |
|
63 | self.assertEqual(r.json(), {'baz': 75, 'wib': [1,2,3], | |
|
64 | 'sub': {'a': 8, 'd': 9}}) | |||
61 |
|
65 | |||
62 | def test_get_unknown(self): |
|
66 | def test_get_unknown(self): | |
63 | # We should get an empty config dictionary instead of a 404 |
|
67 | # We should get an empty config dictionary instead of a 404 |
General Comments 0
You need to be logged in to leave comments.
Login now