Add sort support to project list
Add sort support to project list by sorting items in the client side. By default list will be sorted by name. Change-Id: I00011406846b4003aff075eeeb88ac18fa5e2820 Closes-Bug: #1596818
This commit is contained in:
parent
ed4454c5bb
commit
6c1b03bf73
@ -96,6 +96,7 @@ List projects
|
|||||||
[--domain <domain>]
|
[--domain <domain>]
|
||||||
[--user <user>]
|
[--user <user>]
|
||||||
[--long]
|
[--long]
|
||||||
|
[--sort <key>[:<direction>,<key>:<direction>,..]]
|
||||||
|
|
||||||
.. option:: --domain <domain>
|
.. option:: --domain <domain>
|
||||||
|
|
||||||
@ -113,6 +114,12 @@ List projects
|
|||||||
|
|
||||||
List additional fields in output
|
List additional fields in output
|
||||||
|
|
||||||
|
.. option:: --sort <key>[:<direction>,<key>:<direction>,..]
|
||||||
|
|
||||||
|
Sort output by selected keys and directions (asc or desc) (default: asc),
|
||||||
|
multiple keys and directions can be specified --sort
|
||||||
|
<key>[:<direction>,<key>:<direction>,..]
|
||||||
|
|
||||||
project set
|
project set
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
|
@ -136,6 +136,13 @@ class ListProject(command.Lister):
|
|||||||
default=False,
|
default=False,
|
||||||
help=_('List additional fields in output'),
|
help=_('List additional fields in output'),
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--sort',
|
||||||
|
metavar='<key>[:<direction>]',
|
||||||
|
help=_('Sort output by selected keys and directions (asc or desc) '
|
||||||
|
'(default: asc), repeat this option to specify multiple '
|
||||||
|
'keys and directions.'),
|
||||||
|
)
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
@ -144,6 +151,8 @@ class ListProject(command.Lister):
|
|||||||
else:
|
else:
|
||||||
columns = ('ID', 'Name')
|
columns = ('ID', 'Name')
|
||||||
data = self.app.client_manager.identity.tenants.list()
|
data = self.app.client_manager.identity.tenants.list()
|
||||||
|
if parsed_args.sort:
|
||||||
|
data = utils.sort_items(data, parsed_args.sort)
|
||||||
return (columns,
|
return (columns,
|
||||||
(utils.get_item_properties(
|
(utils.get_item_properties(
|
||||||
s, columns,
|
s, columns,
|
||||||
|
@ -180,6 +180,13 @@ class ListProject(command.Lister):
|
|||||||
default=False,
|
default=False,
|
||||||
help=_('List additional fields in output'),
|
help=_('List additional fields in output'),
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--sort',
|
||||||
|
metavar='<key>[:<direction>]',
|
||||||
|
help=_('Sort output by selected keys and directions (asc or desc) '
|
||||||
|
'(default: asc), repeat this option to specify multiple '
|
||||||
|
'keys and directions.'),
|
||||||
|
)
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
@ -208,6 +215,8 @@ class ListProject(command.Lister):
|
|||||||
kwargs['user'] = user_id
|
kwargs['user'] = user_id
|
||||||
|
|
||||||
data = identity_client.projects.list(**kwargs)
|
data = identity_client.projects.list(**kwargs)
|
||||||
|
if parsed_args.sort:
|
||||||
|
data = utils.sort_items(data, parsed_args.sort)
|
||||||
return (columns,
|
return (columns,
|
||||||
(utils.get_item_properties(
|
(utils.get_item_properties(
|
||||||
s, columns,
|
s, columns,
|
||||||
|
@ -23,6 +23,7 @@ from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes
|
|||||||
class TestProject(identity_fakes.TestIdentityv2):
|
class TestProject(identity_fakes.TestIdentityv2):
|
||||||
|
|
||||||
fake_project = identity_fakes.FakeProject.create_one_project()
|
fake_project = identity_fakes.FakeProject.create_one_project()
|
||||||
|
fake_projects = identity_fakes.FakeProject.create_projects()
|
||||||
|
|
||||||
columns = (
|
columns = (
|
||||||
'description',
|
'description',
|
||||||
@ -36,6 +37,12 @@ class TestProject(identity_fakes.TestIdentityv2):
|
|||||||
fake_project.id,
|
fake_project.id,
|
||||||
fake_project.name,
|
fake_project.name,
|
||||||
)
|
)
|
||||||
|
datalists = (
|
||||||
|
(fake_projects[0].description, True,
|
||||||
|
fake_projects[0].id, fake_projects[0].name,),
|
||||||
|
(fake_projects[1].description, True,
|
||||||
|
fake_projects[1].id, fake_projects[1].name,),
|
||||||
|
)
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestProject, self).setUp()
|
super(TestProject, self).setUp()
|
||||||
@ -357,6 +364,35 @@ class TestProjectList(TestProject):
|
|||||||
), )
|
), )
|
||||||
self.assertEqual(datalist, tuple(data))
|
self.assertEqual(datalist, tuple(data))
|
||||||
|
|
||||||
|
def test_project_list_sort(self):
|
||||||
|
self.projects_mock.list.return_value = self.fake_projects
|
||||||
|
|
||||||
|
arglist = ['--sort', 'name:asc', ]
|
||||||
|
verifylist = []
|
||||||
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
|
# In base command class Lister in cliff, abstract method take_action()
|
||||||
|
# returns a tuple containing the column names and an iterable
|
||||||
|
# containing the data to be listed.
|
||||||
|
(columns, data) = self.cmd.take_action(parsed_args)
|
||||||
|
self.projects_mock.list.assert_called_with()
|
||||||
|
|
||||||
|
collist = ('ID', 'Name')
|
||||||
|
self.assertEqual(collist, columns)
|
||||||
|
|
||||||
|
if self.fake_projects[0].name > self.fake_projects[1].name:
|
||||||
|
datalists = (
|
||||||
|
(self.fake_projects[1].id, self.fake_projects[1].name),
|
||||||
|
(self.fake_projects[0].id, self.fake_projects[0].name),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
datalists = (
|
||||||
|
(self.fake_projects[0].id, self.fake_projects[0].name),
|
||||||
|
(self.fake_projects[1].id, self.fake_projects[1].name),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(datalists, tuple(data))
|
||||||
|
|
||||||
|
|
||||||
class TestProjectSet(TestProject):
|
class TestProjectSet(TestProject):
|
||||||
|
|
||||||
|
@ -622,6 +622,23 @@ class FakeProject(object):
|
|||||||
loaded=True)
|
loaded=True)
|
||||||
return project
|
return project
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_projects(attrs=None, count=2):
|
||||||
|
"""Create multiple fake projects.
|
||||||
|
|
||||||
|
:param Dictionary attrs:
|
||||||
|
A dictionary with all attributes
|
||||||
|
:param int count:
|
||||||
|
The number of projects to fake
|
||||||
|
:return:
|
||||||
|
A list of FakeResource objects faking the projects
|
||||||
|
"""
|
||||||
|
|
||||||
|
projects = []
|
||||||
|
for i in range(0, count):
|
||||||
|
projects.append(FakeProject.create_one_project(attrs))
|
||||||
|
return projects
|
||||||
|
|
||||||
|
|
||||||
class FakeDomain(object):
|
class FakeDomain(object):
|
||||||
"""Fake one or more domain."""
|
"""Fake one or more domain."""
|
||||||
|
@ -451,6 +451,7 @@ class TestProjectList(TestProject):
|
|||||||
domain = identity_fakes.FakeDomain.create_one_domain()
|
domain = identity_fakes.FakeDomain.create_one_domain()
|
||||||
project = identity_fakes.FakeProject.create_one_project(
|
project = identity_fakes.FakeProject.create_one_project(
|
||||||
attrs={'domain_id': domain.id})
|
attrs={'domain_id': domain.id})
|
||||||
|
projects = identity_fakes.FakeProject.create_projects()
|
||||||
|
|
||||||
columns = (
|
columns = (
|
||||||
'ID',
|
'ID',
|
||||||
@ -462,6 +463,12 @@ class TestProjectList(TestProject):
|
|||||||
project.name,
|
project.name,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
datalists = (
|
||||||
|
(projects[0].description, True,
|
||||||
|
projects[0].id, projects[0].name,),
|
||||||
|
(projects[1].description, True,
|
||||||
|
projects[1].id, projects[1].name,),
|
||||||
|
)
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestProjectList, self).setUp()
|
super(TestProjectList, self).setUp()
|
||||||
@ -552,6 +559,36 @@ class TestProjectList(TestProject):
|
|||||||
self.assertEqual(self.columns, columns)
|
self.assertEqual(self.columns, columns)
|
||||||
self.assertEqual(self.datalist, tuple(data))
|
self.assertEqual(self.datalist, tuple(data))
|
||||||
|
|
||||||
|
def test_project_list_sort(self):
|
||||||
|
self.projects_mock.list.return_value = self.projects
|
||||||
|
|
||||||
|
arglist = ['--sort', 'name:asc', ]
|
||||||
|
verifylist = []
|
||||||
|
|
||||||
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
|
# In base command class Lister in cliff, abstract method take_action()
|
||||||
|
# returns a tuple containing the column names and an iterable
|
||||||
|
# containing the data to be listed.
|
||||||
|
(columns, data) = self.cmd.take_action(parsed_args)
|
||||||
|
self.projects_mock.list.assert_called_with()
|
||||||
|
|
||||||
|
collist = ('ID', 'Name')
|
||||||
|
self.assertEqual(collist, columns)
|
||||||
|
|
||||||
|
if self.projects[0].name > self.projects[1].name:
|
||||||
|
datalists = (
|
||||||
|
(self.projects[1].id, self.projects[1].name),
|
||||||
|
(self.projects[0].id, self.projects[0].name),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
datalists = (
|
||||||
|
(self.projects[0].id, self.projects[0].name),
|
||||||
|
(self.projects[1].id, self.projects[1].name),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(datalists, tuple(data))
|
||||||
|
|
||||||
|
|
||||||
class TestProjectSet(TestProject):
|
class TestProjectSet(TestProject):
|
||||||
|
|
||||||
|
6
releasenotes/notes/bug-1596818-d4cd93dd4d38d3d6.yaml
Normal file
6
releasenotes/notes/bug-1596818-d4cd93dd4d38d3d6.yaml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Add ``--sort`` support to ``project list`` by sorting items in client side
|
||||||
|
By default project list will be sorted by name.
|
||||||
|
[Bug `1596818 <https://bugs.launchpad.net/bugs/1596818>`_]
|
Loading…
Reference in New Issue
Block a user