Skip to content

Commit 6c1b03b

Browse files
tomjosekalThingee
authored andcommitted
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
1 parent ed4454c commit 6c1b03b

7 files changed

Lines changed: 121 additions & 0 deletions

File tree

doc/source/command-objects/project.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ List projects
9696
[--domain <domain>]
9797
[--user <user>]
9898
[--long]
99+
[--sort <key>[:<direction>,<key>:<direction>,..]]
99100
100101
.. option:: --domain <domain>
101102
@@ -113,6 +114,12 @@ List projects
113114
114115
List additional fields in output
115116
117+
.. option:: --sort <key>[:<direction>,<key>:<direction>,..]
118+
119+
Sort output by selected keys and directions (asc or desc) (default: asc),
120+
multiple keys and directions can be specified --sort
121+
<key>[:<direction>,<key>:<direction>,..]
122+
116123
project set
117124
-----------
118125

openstackclient/identity/v2_0/project.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,13 @@ def get_parser(self, prog_name):
136136
default=False,
137137
help=_('List additional fields in output'),
138138
)
139+
parser.add_argument(
140+
'--sort',
141+
metavar='<key>[:<direction>]',
142+
help=_('Sort output by selected keys and directions (asc or desc) '
143+
'(default: asc), repeat this option to specify multiple '
144+
'keys and directions.'),
145+
)
139146
return parser
140147

141148
def take_action(self, parsed_args):
@@ -144,6 +151,8 @@ def take_action(self, parsed_args):
144151
else:
145152
columns = ('ID', 'Name')
146153
data = self.app.client_manager.identity.tenants.list()
154+
if parsed_args.sort:
155+
data = utils.sort_items(data, parsed_args.sort)
147156
return (columns,
148157
(utils.get_item_properties(
149158
s, columns,

openstackclient/identity/v3/project.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,13 @@ def get_parser(self, prog_name):
180180
default=False,
181181
help=_('List additional fields in output'),
182182
)
183+
parser.add_argument(
184+
'--sort',
185+
metavar='<key>[:<direction>]',
186+
help=_('Sort output by selected keys and directions (asc or desc) '
187+
'(default: asc), repeat this option to specify multiple '
188+
'keys and directions.'),
189+
)
183190
return parser
184191

185192
def take_action(self, parsed_args):
@@ -208,6 +215,8 @@ def take_action(self, parsed_args):
208215
kwargs['user'] = user_id
209216

210217
data = identity_client.projects.list(**kwargs)
218+
if parsed_args.sort:
219+
data = utils.sort_items(data, parsed_args.sort)
211220
return (columns,
212221
(utils.get_item_properties(
213222
s, columns,

openstackclient/tests/unit/identity/v2_0/test_project.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
class TestProject(identity_fakes.TestIdentityv2):
2424

2525
fake_project = identity_fakes.FakeProject.create_one_project()
26+
fake_projects = identity_fakes.FakeProject.create_projects()
2627

2728
columns = (
2829
'description',
@@ -36,6 +37,12 @@ class TestProject(identity_fakes.TestIdentityv2):
3637
fake_project.id,
3738
fake_project.name,
3839
)
40+
datalists = (
41+
(fake_projects[0].description, True,
42+
fake_projects[0].id, fake_projects[0].name,),
43+
(fake_projects[1].description, True,
44+
fake_projects[1].id, fake_projects[1].name,),
45+
)
3946

4047
def setUp(self):
4148
super(TestProject, self).setUp()
@@ -357,6 +364,35 @@ def test_project_list_long(self):
357364
), )
358365
self.assertEqual(datalist, tuple(data))
359366

367+
def test_project_list_sort(self):
368+
self.projects_mock.list.return_value = self.fake_projects
369+
370+
arglist = ['--sort', 'name:asc', ]
371+
verifylist = []
372+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
373+
374+
# In base command class Lister in cliff, abstract method take_action()
375+
# returns a tuple containing the column names and an iterable
376+
# containing the data to be listed.
377+
(columns, data) = self.cmd.take_action(parsed_args)
378+
self.projects_mock.list.assert_called_with()
379+
380+
collist = ('ID', 'Name')
381+
self.assertEqual(collist, columns)
382+
383+
if self.fake_projects[0].name > self.fake_projects[1].name:
384+
datalists = (
385+
(self.fake_projects[1].id, self.fake_projects[1].name),
386+
(self.fake_projects[0].id, self.fake_projects[0].name),
387+
)
388+
else:
389+
datalists = (
390+
(self.fake_projects[0].id, self.fake_projects[0].name),
391+
(self.fake_projects[1].id, self.fake_projects[1].name),
392+
)
393+
394+
self.assertEqual(datalists, tuple(data))
395+
360396

361397
class TestProjectSet(TestProject):
362398

openstackclient/tests/unit/identity/v3/fakes.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,23 @@ def create_one_project(attrs=None):
622622
loaded=True)
623623
return project
624624

625+
@staticmethod
626+
def create_projects(attrs=None, count=2):
627+
"""Create multiple fake projects.
628+
629+
:param Dictionary attrs:
630+
A dictionary with all attributes
631+
:param int count:
632+
The number of projects to fake
633+
:return:
634+
A list of FakeResource objects faking the projects
635+
"""
636+
637+
projects = []
638+
for i in range(0, count):
639+
projects.append(FakeProject.create_one_project(attrs))
640+
return projects
641+
625642

626643
class FakeDomain(object):
627644
"""Fake one or more domain."""

openstackclient/tests/unit/identity/v3/test_project.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,7 @@ class TestProjectList(TestProject):
451451
domain = identity_fakes.FakeDomain.create_one_domain()
452452
project = identity_fakes.FakeProject.create_one_project(
453453
attrs={'domain_id': domain.id})
454+
projects = identity_fakes.FakeProject.create_projects()
454455

455456
columns = (
456457
'ID',
@@ -462,6 +463,12 @@ class TestProjectList(TestProject):
462463
project.name,
463464
),
464465
)
466+
datalists = (
467+
(projects[0].description, True,
468+
projects[0].id, projects[0].name,),
469+
(projects[1].description, True,
470+
projects[1].id, projects[1].name,),
471+
)
465472

466473
def setUp(self):
467474
super(TestProjectList, self).setUp()
@@ -552,6 +559,36 @@ def test_project_list_domain_no_perms(self):
552559
self.assertEqual(self.columns, columns)
553560
self.assertEqual(self.datalist, tuple(data))
554561

562+
def test_project_list_sort(self):
563+
self.projects_mock.list.return_value = self.projects
564+
565+
arglist = ['--sort', 'name:asc', ]
566+
verifylist = []
567+
568+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
569+
570+
# In base command class Lister in cliff, abstract method take_action()
571+
# returns a tuple containing the column names and an iterable
572+
# containing the data to be listed.
573+
(columns, data) = self.cmd.take_action(parsed_args)
574+
self.projects_mock.list.assert_called_with()
575+
576+
collist = ('ID', 'Name')
577+
self.assertEqual(collist, columns)
578+
579+
if self.projects[0].name > self.projects[1].name:
580+
datalists = (
581+
(self.projects[1].id, self.projects[1].name),
582+
(self.projects[0].id, self.projects[0].name),
583+
)
584+
else:
585+
datalists = (
586+
(self.projects[0].id, self.projects[0].name),
587+
(self.projects[1].id, self.projects[1].name),
588+
)
589+
590+
self.assertEqual(datalists, tuple(data))
591+
555592

556593
class TestProjectSet(TestProject):
557594

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
features:
3+
- |
4+
Add ``--sort`` support to ``project list`` by sorting items in client side
5+
By default project list will be sorted by name.
6+
[Bug `1596818 <https://bugs.launchpad.net/bugs/1596818>`_]

0 commit comments

Comments
 (0)