summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRodrigo Duarte Sousa <rodrigods@lsd.ufcg.edu.br>2015-01-26 12:47:00 -0300
committerRodrigo Duarte Sousa <rodrigods@lsd.ufcg.edu.br>2015-03-13 17:02:39 -0300
commit14ace4a5ded0bd4005928b42a4f337639bd90799 (patch)
tree2b71e008bbd775b160573ddd2713391772fd45de
parent32c18a83e2ffe80f559ca871eeddb2ef8848ff17 (diff)
downloadpython-keystoneclient-14ace4a5ded0bd4005928b42a4f337639bd90799.tar.gz
Implements subtree_as_ids and parents_as_ids
This patch implements the new ways to get the project's hierarchy: 'subtree_as_ids': If True, returns projects IDs down the hierarchy as a structured dictionay. 'parents_as_ids': If True, returns projects IDs up the hierarchy as a structured dictionay. Change-Id: Ia3afe994893dfca059cb8361f7ab1c14e28e1ad5 Implements: blueprint hierarchical-multitenancy-improvements
-rw-r--r--keystoneclient/base.py9
-rw-r--r--keystoneclient/exceptions.py2
-rw-r--r--keystoneclient/tests/unit/v3/test_projects.py86
-rw-r--r--keystoneclient/v3/projects.py49
4 files changed, 140 insertions, 6 deletions
diff --git a/keystoneclient/base.py b/keystoneclient/base.py
index 81d5e26..2af38b9 100644
--- a/keystoneclient/base.py
+++ b/keystoneclient/base.py
@@ -340,6 +340,15 @@ class CrudManager(Manager):
def _build_query(self, params):
return '?%s' % urllib.parse.urlencode(params) if params else ''
+ def build_key_only_query(self, params_list):
+ """Builds a query that does not include values, just keys.
+
+ The Identity API has some calls that define queries without values,
+ this can not be accomplished by using urllib.parse.urlencode(). This
+ method builds a query using only the keys.
+ """
+ return '?%s' % '&'.join(params_list) if params_list else ''
+
@filter_kwargs
def list(self, fallback_to_auth=False, **kwargs):
url = self.build_url(dict_args_in_out=kwargs)
diff --git a/keystoneclient/exceptions.py b/keystoneclient/exceptions.py
index a76aa32..0150bf5 100644
--- a/keystoneclient/exceptions.py
+++ b/keystoneclient/exceptions.py
@@ -21,6 +21,8 @@ Exception definitions.
.. py:exception:: HttpError
+.. py:exception:: ValidationError
+
.. py:exception:: Unauthorized
"""
diff --git a/keystoneclient/tests/unit/v3/test_projects.py b/keystoneclient/tests/unit/v3/test_projects.py
index 5d08bb2..61a5ef1 100644
--- a/keystoneclient/tests/unit/v3/test_projects.py
+++ b/keystoneclient/tests/unit/v3/test_projects.py
@@ -144,6 +144,75 @@ class ProjectTests(utils.TestCase, utils.CrudTests):
return projects
+ def test_get_with_subtree_as_ids(self):
+ projects = self._create_projects_hierarchy()
+ ref = projects[0]
+
+ # We will query for projects[0] subtree, it should include projects[1]
+ # and projects[2] structured like the following:
+ # {
+ # projects[1]: {
+ # projects[2]: None
+ # }
+ # }
+ ref['subtree'] = {
+ projects[1]['id']: {
+ projects[2]['id']: None
+ }
+ }
+
+ self.stub_entity('GET', id=ref['id'], entity=ref)
+
+ returned = self.manager.get(ref['id'], subtree_as_ids=True)
+ self.assertQueryStringIs('subtree_as_ids')
+ self.assertDictEqual(ref['subtree'], returned.subtree)
+
+ def test_get_with_parents_as_ids(self):
+ projects = self._create_projects_hierarchy()
+ ref = projects[2]
+
+ # We will query for projects[2] parents, it should include projects[1]
+ # and projects[0] structured like the following:
+ # {
+ # projects[1]: {
+ # projects[0]: None
+ # }
+ # }
+ ref['parents'] = {
+ projects[1]['id']: {
+ projects[0]['id']: None
+ }
+ }
+
+ self.stub_entity('GET', id=ref['id'], entity=ref)
+
+ returned = self.manager.get(ref['id'], parents_as_ids=True)
+ self.assertQueryStringIs('parents_as_ids')
+ self.assertDictEqual(ref['parents'], returned.parents)
+
+ def test_get_with_parents_as_ids_and_subtree_as_ids(self):
+ ref = self.new_ref()
+ projects = self._create_projects_hierarchy()
+ ref = projects[1]
+
+ # We will query for projects[1] subtree and parents. The subtree should
+ # include projects[2] and the parents should include projects[2].
+ ref['parents'] = {
+ projects[0]['id']: None
+ }
+ ref['subtree'] = {
+ projects[2]['id']: None
+ }
+
+ self.stub_entity('GET', id=ref['id'], entity=ref)
+
+ returned = self.manager.get(ref['id'],
+ parents_as_ids=True,
+ subtree_as_ids=True)
+ self.assertQueryStringIs('subtree_as_ids&parents_as_ids')
+ self.assertDictEqual(ref['parents'], returned.parents)
+ self.assertDictEqual(ref['subtree'], returned.subtree)
+
def test_get_with_subtree_as_list(self):
projects = self._create_projects_hierarchy()
ref = projects[0]
@@ -213,6 +282,23 @@ class ProjectTests(utils.TestCase, utils.CrudTests):
projects[2][attr],
'Expected different %s' % attr)
+ def test_get_with_invalid_parameters_combination(self):
+ # subtree_as_list and subtree_as_ids can not be included at the
+ # same time in the call.
+ self.assertRaises(exceptions.ValidationError,
+ self.manager.get,
+ project=uuid.uuid4().hex,
+ subtree_as_list=True,
+ subtree_as_ids=True)
+
+ # parents_as_list and parents_as_ids can not be included at the
+ # same time in the call.
+ self.assertRaises(exceptions.ValidationError,
+ self.manager.get,
+ project=uuid.uuid4().hex,
+ parents_as_list=True,
+ parents_as_ids=True)
+
def test_update_with_parent_project(self):
ref = self.new_ref()
ref['parent_id'] = uuid.uuid4().hex
diff --git a/keystoneclient/v3/projects.py b/keystoneclient/v3/projects.py
index 0a98991..5daaee5 100644
--- a/keystoneclient/v3/projects.py
+++ b/keystoneclient/v3/projects.py
@@ -15,6 +15,8 @@
# under the License.
from keystoneclient import base
+from keystoneclient import exceptions
+from keystoneclient.i18n import _
from keystoneclient import utils
@@ -103,8 +105,23 @@ class ProjectManager(base.CrudManager):
fallback_to_auth=True,
**kwargs)
+ def _check_not_parents_as_ids_and_parents_as_list(self, parents_as_ids,
+ parents_as_list):
+ if parents_as_ids and parents_as_list:
+ msg = _('Specify either parents_as_ids or parents_as_list '
+ 'parameters, not both')
+ raise exceptions.ValidationError(msg)
+
+ def _check_not_subtree_as_ids_and_subtree_as_list(self, subtree_as_ids,
+ subtree_as_list):
+ if subtree_as_ids and subtree_as_list:
+ msg = _('Specify either subtree_as_ids or subtree_as_list '
+ 'parameters, not both')
+ raise exceptions.ValidationError(msg)
+
@utils.positional()
- def get(self, project, subtree_as_list=False, parents_as_list=False):
+ def get(self, project, subtree_as_list=False, parents_as_list=False,
+ subtree_as_ids=False, parents_as_ids=False):
"""Get a project.
:param project: project to be retrieved.
@@ -115,17 +132,37 @@ class ProjectManager(base.CrudManager):
:param boolean parents_as_list: retrieve projects above this project
in the hierarchy as a flat list.
(optional)
+ :param boolean subtree_as_ids: retrieve the IDs from the projects below
+ this project in the hierarchy as a
+ structured dictionary. (optional)
+ :param boolean parents_as_ids: retrieve the IDs from the projects above
+ this project in the hierarchy as a
+ structured dictionary. (optional)
+
+ :raises keystoneclient.exceptions.ValidationError: if subtree_as_list
+ and subtree_as_ids or parents_as_list and parents_as_ids are
+ included at the same time in the call.
"""
+ self._check_not_parents_as_ids_and_parents_as_list(
+ parents_as_ids, parents_as_list)
+ self._check_not_subtree_as_ids_and_subtree_as_list(
+ subtree_as_ids, subtree_as_list)
+
# According to the API spec, the query params are key only
- query = ''
+ query_params = []
if subtree_as_list:
- query = '?subtree_as_list'
+ query_params.append('subtree_as_list')
+ if subtree_as_ids:
+ query_params.append('subtree_as_ids')
if parents_as_list:
- query = query + '&parents_as_list' if query else '?parents_as_list'
+ query_params.append('parents_as_list')
+ if parents_as_ids:
+ query_params.append('parents_as_ids')
+ query = self.build_key_only_query(query_params)
dict_args = {'project_id': base.getid(project)}
- url = self.build_url(dict_args_in_out=dict_args) + query
- return self._get(url, self.key)
+ url = self.build_url(dict_args_in_out=dict_args)
+ return self._get(url + query, self.key)
@utils.positional(enforcement=utils.positional.WARN)
def update(self, project, name=None, domain=None, description=None,