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
This commit is contained in:
@@ -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)
|
||||
|
@@ -21,6 +21,8 @@ Exception definitions.
|
||||
|
||||
.. py:exception:: HttpError
|
||||
|
||||
.. py:exception:: ValidationError
|
||||
|
||||
.. py:exception:: Unauthorized
|
||||
|
||||
"""
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
"""
|
||||
# According to the API spec, the query params are key only
|
||||
query = ''
|
||||
if subtree_as_list:
|
||||
query = '?subtree_as_list'
|
||||
if parents_as_list:
|
||||
query = query + '&parents_as_list' if query else '?parents_as_list'
|
||||
: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_params = []
|
||||
if subtree_as_list:
|
||||
query_params.append('subtree_as_list')
|
||||
if subtree_as_ids:
|
||||
query_params.append('subtree_as_ids')
|
||||
if 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,
|
||||
|
Reference in New Issue
Block a user