Added ability to remove services

Change-Id: Ieb763013679356c898924444383e4ac65d373736
This commit is contained in:
Serg Melikyan 2013-07-12 17:44:11 +04:00
parent 169bf1316c
commit 944f7c57b4
4 changed files with 124 additions and 49 deletions

View File

@ -39,7 +39,8 @@ def normalize_path(f):
class Controller(object):
@normalize_path
def get(self, request, environment_id, path):
log.debug(_('Services:Get <EnvId: {0}>'.format(environment_id)))
log.debug(_('Services:Get <EnvId: {0}, '
'Path: {1}>'.format(environment_id, path)))
session_id = None
if hasattr(request, 'context') and request.context.session:
@ -54,8 +55,8 @@ class Controller(object):
@utils.verify_session
@normalize_path
def post(self, request, environment_id, path, body):
log.debug(_('Services:Post <EnvId: {0}, '
'Body: {1}>'.format(environment_id, body)))
log.debug(_('Services:Post <EnvId: {0}, , Path: {2}, '
'Body: {1}>'.format(environment_id, body, path)))
post_data = CoreServices.post_data
session_id = request.context.session
@ -68,8 +69,8 @@ class Controller(object):
@utils.verify_session
@normalize_path
def put(self, request, environment_id, path, body):
log.debug(_('Services:Put <EnvId: {0}, '
'Body: {1}>'.format(environment_id, body)))
log.debug(_('Services:Put <EnvId: {0}, , Path: {2}, '
'Body: {1}>'.format(environment_id, body, path)))
put_data = CoreServices.put_data
session_id = request.context.session
@ -80,6 +81,20 @@ class Controller(object):
raise HTTPNotFound
return result
@utils.verify_session
@normalize_path
def delete(self, request, environment_id, path):
log.debug(_('Services:Put <EnvId: {0}, '
'Path: {1}>'.format(environment_id, path)))
delete_data = CoreServices.delete_data
session_id = request.context.session
try:
delete_data(environment_id, session_id, path)
except (KeyError, ValueError):
raise HTTPNotFound
def create_resource():
return wsgi.Resource(Controller())

View File

@ -20,16 +20,21 @@ from collections import deque
from functools import wraps
from muranoapi.openstack.common import log as logging
log = logging.getLogger(__name__)
class TraverseHelper(object):
value_type = (types.StringTypes, types.IntType, types.FloatType,
types.BooleanType)
@staticmethod
def get(path, source):
"""
Provides the ability to traverse a data source made up of any
combination of lists and dicts. Has simple rules for selecting item of
combination of lists and dicts. Has simple rules for selecting item of
the list:
* each item should have id property
* to select item from the list, specify id value
@ -49,24 +54,24 @@ class TraverseHelper(object):
:return: object
:raise: ValueError if object is malformed
"""
if path.startswith('/'):
path = path[1:]
queue = deque(path.split('/'))
obj = source
queue = deque(filter(lambda x: x, path.split('/')))
while len(queue):
path = queue.popleft()
if isinstance(obj, types.ListType):
filtered = filter(lambda i: 'id' in i and i['id'] == path, obj)
obj = filtered[0] if filtered else None
elif isinstance(obj, types.DictionaryType):
obj = obj[path] if path else obj
if isinstance(source, types.ListType):
idx_source = source
source = next((i for i in source if i['id'] == path), None)
if source is None and path.isdigit():
source = idx_source[int(path)]
elif isinstance(source, types.DictionaryType):
source = source[path]
elif isinstance(source, TraverseHelper.value_type):
break
else:
raise ValueError('Object or path is malformed')
raise ValueError('Source object or path is malformed')
return obj
return source
@staticmethod
def update(path, value, source):
@ -91,11 +96,34 @@ class TraverseHelper(object):
:param path: string with path to desired value
:param value: value
:param source: python object (list or dict)
:param source: List
"""
node = TraverseHelper.get(path, source)
node.append(value)
@staticmethod
def remove(path, source):
"""
Removes selected item from source.
:param path: string with path to desired value
:param source: python object (list or dict)
"""
parent_path = '/'.join(path.split('/')[:-1])
node = TraverseHelper.get(parent_path, source)
key = path[1:].split('/')[-1]
if isinstance(node, types.ListType):
item = next((i for i in node if i['id'] == key), None)
if item is None and key.isdigit():
del node[int(key)]
else:
node.remove(item)
elif isinstance(node, types.DictionaryType):
del node[key]
else:
raise ValueError('Source object or path is malformed')
def auto_id(value):
if isinstance(value, types.DictionaryType):

View File

@ -67,3 +67,13 @@ class CoreServices(object):
save_description(session_id, env_description)
return data
@staticmethod
def delete_data(environment_id, session_id, path):
get_description = EnvironmentServices.get_environment_description
save_description = EnvironmentServices.save_environment_description
env_description = get_description(environment_id, session_id)
TraverseHelper.remove(path, env_description)
save_description(session_id, env_description)

View File

@ -11,27 +11,38 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import unittest2 as unittest
from muranoapi.common.utils import TraverseHelper
class TraverseHelperTests(unittest.TestCase):
def test_simple_root_get(self):
source = {"attr": True}
def test_root_get_with_dict(self):
source = {'attr': True}
value = TraverseHelper.get('/', source)
self.assertDictEqual(value, {"attr": True})
self.assertDictEqual(value, source)
def test_simple_attribute_get(self):
source = {"attr": True}
def test_root_get_with_list(self):
source = [{'attr': True}]
value = TraverseHelper.get('/', source)
self.assertListEqual(value, source)
def test_root_get_with_value_type(self):
source = 'source'
value = TraverseHelper.get('/', source)
self.assertEqual(value, source)
def test_attribute_get(self):
source = {'attr': True}
value = TraverseHelper.get('/attr', source)
self.assertEqual(value, True)
def test_attribute_get(self):
def test_nested_attribute_get(self):
source = {'obj': {'attr': True}}
value = TraverseHelper.get('/obj/attr', source)
self.assertEqual(value, True)
def test_list_item_attribute_get_(self):
def test_list_item_attribute_get(self):
source = {'obj': [
{'id': '1', 'value': 1},
{'id': '2s', 'value': 2},
@ -39,53 +50,64 @@ class TraverseHelperTests(unittest.TestCase):
value = TraverseHelper.get('/obj/2s/value', source)
self.assertEqual(value, 2)
def test_simple_attribute_set(self):
source = {"attr": True}
def test_list_item_attribute_get_by_index(self):
source = {'obj': [
{'id': 'guid1', 'value': 1},
{'id': 'guid2', 'value': 2},
]}
value = TraverseHelper.get('/obj/1/value', source)
self.assertEqual(value, 2)
def test_attribute_set(self):
source = {'attr': True}
TraverseHelper.update('/newAttr', False, source)
value = TraverseHelper.get('/newAttr', source)
self.assertEqual(value, False)
def test_simple_attribute_update(self):
source = {"attr": True}
def test_attribute_update(self):
source = {'attr': True}
TraverseHelper.update('/attr', False, source)
value = TraverseHelper.get('/attr', source)
self.assertEqual(value, False)
def test_attribute_update(self):
source = {"obj": {"attr": True}}
def test_nested_attribute_update(self):
source = {'obj': {'attr': True}}
TraverseHelper.update('/obj/attr', False, source)
value = TraverseHelper.get('/obj/attr', source)
self.assertEqual(value, False)
def test_simple_adding_item_to_list(self):
source = {"attr": [1, 2, 3]}
def test_adding_item_to_list(self):
source = {'attr': [1, 2, 3]}
TraverseHelper.insert('/attr', 4, source)
value = TraverseHelper.get('/attr', source)
self.assertListEqual(value, [1, 2, 3, 4])
def test_adding_item_to_list(self):
source = {"obj": {"attr": [1, 2, 3]}}
def test_nested_adding_item_to_list(self):
source = {'obj': {'attr': [1, 2, 3]}}
TraverseHelper.insert('/obj/attr', 4, source)
value = TraverseHelper.get('/obj/attr', source)
self.assertListEqual(value, [1, 2, 3, 4])
@unittest.skip
def test_simple_attribute_remove(self):
source = {"attr1": False, "attr2": True}
def test_attribute_remove_from_dict(self):
source = {'attr1': False, 'attr2': True}
TraverseHelper.remove('/attr1', source)
value = TraverseHelper.get('/', source)
self.assertEqual(value, {"attr2": True})
self.assertDictEqual(value, {'attr2': True})
@unittest.skip
def test_nested_attribute_remove_from_object(self):
source = {"obj": {"attr1": False, "attr2": True}}
def test_nested_attribute_remove_from_dict(self):
source = {'obj': {'attr1': False, 'attr2': True}}
TraverseHelper.remove('/obj/attr1', source)
value = TraverseHelper.get('/obj', source)
self.assertDictEqual(value, {"attr2": True})
self.assertDictEqual(value, {'attr2': True})
@unittest.skip
def test_nested_attribute_remove_from_list(self):
source = {"obj": [{"id": 'id1'}, {"id": 'id2'}]}
def test_nested_attribute_remove_from_list_by_id(self):
source = {'obj': [{'id': 'id1'}, {'id': 'id2'}]}
TraverseHelper.remove('/obj/id1', source)
value = TraverseHelper.get('/', source)
self.assertListEqual(value, [{"id": 'id2'}])
value = TraverseHelper.get('/obj', source)
self.assertListEqual(value, [{'id': 'id2'}])
def test_nested_attribute_remove_from_list_by_index(self):
source = {'obj': [{'id': 'id1'}, {'id': 'id2'}]}
TraverseHelper.remove('/obj/0', source)
value = TraverseHelper.get('/obj', source)
self.assertListEqual(value, [{'id': 'id2'}])