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

View File

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

View File

@ -67,3 +67,13 @@ class CoreServices(object):
save_description(session_id, env_description) save_description(session_id, env_description)
return data 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 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import unittest2 as unittest import unittest2 as unittest
from muranoapi.common.utils import TraverseHelper from muranoapi.common.utils import TraverseHelper
class TraverseHelperTests(unittest.TestCase): class TraverseHelperTests(unittest.TestCase):
def test_simple_root_get(self): def test_root_get_with_dict(self):
source = {"attr": True} source = {'attr': True}
value = TraverseHelper.get('/', source) value = TraverseHelper.get('/', source)
self.assertDictEqual(value, {"attr": True}) self.assertDictEqual(value, source)
def test_simple_attribute_get(self): def test_root_get_with_list(self):
source = {"attr": True} 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) value = TraverseHelper.get('/attr', source)
self.assertEqual(value, True) self.assertEqual(value, True)
def test_attribute_get(self): def test_nested_attribute_get(self):
source = {'obj': {'attr': True}} source = {'obj': {'attr': True}}
value = TraverseHelper.get('/obj/attr', source) value = TraverseHelper.get('/obj/attr', source)
self.assertEqual(value, True) self.assertEqual(value, True)
def test_list_item_attribute_get_(self): def test_list_item_attribute_get(self):
source = {'obj': [ source = {'obj': [
{'id': '1', 'value': 1}, {'id': '1', 'value': 1},
{'id': '2s', 'value': 2}, {'id': '2s', 'value': 2},
@ -39,53 +50,64 @@ class TraverseHelperTests(unittest.TestCase):
value = TraverseHelper.get('/obj/2s/value', source) value = TraverseHelper.get('/obj/2s/value', source)
self.assertEqual(value, 2) self.assertEqual(value, 2)
def test_simple_attribute_set(self): def test_list_item_attribute_get_by_index(self):
source = {"attr": True} 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) TraverseHelper.update('/newAttr', False, source)
value = TraverseHelper.get('/newAttr', source) value = TraverseHelper.get('/newAttr', source)
self.assertEqual(value, False) self.assertEqual(value, False)
def test_simple_attribute_update(self): def test_attribute_update(self):
source = {"attr": True} source = {'attr': True}
TraverseHelper.update('/attr', False, source) TraverseHelper.update('/attr', False, source)
value = TraverseHelper.get('/attr', source) value = TraverseHelper.get('/attr', source)
self.assertEqual(value, False) self.assertEqual(value, False)
def test_attribute_update(self): def test_nested_attribute_update(self):
source = {"obj": {"attr": True}} source = {'obj': {'attr': True}}
TraverseHelper.update('/obj/attr', False, source) TraverseHelper.update('/obj/attr', False, source)
value = TraverseHelper.get('/obj/attr', source) value = TraverseHelper.get('/obj/attr', source)
self.assertEqual(value, False) self.assertEqual(value, False)
def test_simple_adding_item_to_list(self): def test_adding_item_to_list(self):
source = {"attr": [1, 2, 3]} source = {'attr': [1, 2, 3]}
TraverseHelper.insert('/attr', 4, source) TraverseHelper.insert('/attr', 4, source)
value = TraverseHelper.get('/attr', source) value = TraverseHelper.get('/attr', source)
self.assertListEqual(value, [1, 2, 3, 4]) self.assertListEqual(value, [1, 2, 3, 4])
def test_adding_item_to_list(self): def test_nested_adding_item_to_list(self):
source = {"obj": {"attr": [1, 2, 3]}} source = {'obj': {'attr': [1, 2, 3]}}
TraverseHelper.insert('/obj/attr', 4, source) TraverseHelper.insert('/obj/attr', 4, source)
value = TraverseHelper.get('/obj/attr', source) value = TraverseHelper.get('/obj/attr', source)
self.assertListEqual(value, [1, 2, 3, 4]) self.assertListEqual(value, [1, 2, 3, 4])
@unittest.skip def test_attribute_remove_from_dict(self):
def test_simple_attribute_remove(self): source = {'attr1': False, 'attr2': True}
source = {"attr1": False, "attr2": True}
TraverseHelper.remove('/attr1', source) TraverseHelper.remove('/attr1', source)
value = TraverseHelper.get('/', source) value = TraverseHelper.get('/', source)
self.assertEqual(value, {"attr2": True}) self.assertDictEqual(value, {'attr2': True})
@unittest.skip def test_nested_attribute_remove_from_dict(self):
def test_nested_attribute_remove_from_object(self): source = {'obj': {'attr1': False, 'attr2': True}}
source = {"obj": {"attr1": False, "attr2": True}}
TraverseHelper.remove('/obj/attr1', source) TraverseHelper.remove('/obj/attr1', source)
value = TraverseHelper.get('/obj', 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_by_id(self):
def test_nested_attribute_remove_from_list(self): source = {'obj': [{'id': 'id1'}, {'id': 'id2'}]}
source = {"obj": [{"id": 'id1'}, {"id": 'id2'}]}
TraverseHelper.remove('/obj/id1', source) TraverseHelper.remove('/obj/id1', source)
value = TraverseHelper.get('/', source) value = TraverseHelper.get('/obj', source)
self.assertListEqual(value, [{"id": 'id2'}]) 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'}])