Skip to content

Commit 9184e19

Browse files
Jenkinsopenstack-gerrit
authored andcommitted
Merge "Add sort support to project list"
2 parents 6624656 + 6c1b03b commit 9184e19

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
@@ -150,6 +150,13 @@ def get_parser(self, prog_name):
150150
default=False,
151151
help=_('List additional fields in output'),
152152
)
153+
parser.add_argument(
154+
'--sort',
155+
metavar='<key>[:<direction>]',
156+
help=_('Sort output by selected keys and directions (asc or desc) '
157+
'(default: asc), repeat this option to specify multiple '
158+
'keys and directions.'),
159+
)
153160
return parser
154161

155162
def take_action(self, parsed_args):
@@ -158,6 +165,8 @@ def take_action(self, parsed_args):
158165
else:
159166
columns = ('ID', 'Name')
160167
data = self.app.client_manager.identity.tenants.list()
168+
if parsed_args.sort:
169+
data = utils.sort_items(data, parsed_args.sort)
161170
return (columns,
162171
(utils.get_item_properties(
163172
s, columns,

openstackclient/identity/v3/project.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,13 @@ def get_parser(self, prog_name):
194194
default=False,
195195
help=_('List additional fields in output'),
196196
)
197+
parser.add_argument(
198+
'--sort',
199+
metavar='<key>[:<direction>]',
200+
help=_('Sort output by selected keys and directions (asc or desc) '
201+
'(default: asc), repeat this option to specify multiple '
202+
'keys and directions.'),
203+
)
197204
return parser
198205

199206
def take_action(self, parsed_args):
@@ -222,6 +229,8 @@ def take_action(self, parsed_args):
222229
kwargs['user'] = user_id
223230

224231
data = identity_client.projects.list(**kwargs)
232+
if parsed_args.sort:
233+
data = utils.sort_items(data, parsed_args.sort)
225234
return (columns,
226235
(utils.get_item_properties(
227236
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
@@ -26,6 +26,7 @@
2626
class TestProject(identity_fakes.TestIdentityv2):
2727

2828
fake_project = identity_fakes.FakeProject.create_one_project()
29+
fake_projects = identity_fakes.FakeProject.create_projects()
2930

3031
columns = (
3132
'description',
@@ -39,6 +40,12 @@ class TestProject(identity_fakes.TestIdentityv2):
3940
fake_project.id,
4041
fake_project.name,
4142
)
43+
datalists = (
44+
(fake_projects[0].description, True,
45+
fake_projects[0].id, fake_projects[0].name,),
46+
(fake_projects[1].description, True,
47+
fake_projects[1].id, fake_projects[1].name,),
48+
)
4249

4350
def setUp(self):
4451
super(TestProject, self).setUp()
@@ -386,6 +393,35 @@ def test_project_list_long(self):
386393
), )
387394
self.assertEqual(datalist, tuple(data))
388395

396+
def test_project_list_sort(self):
397+
self.projects_mock.list.return_value = self.fake_projects
398+
399+
arglist = ['--sort', 'name:asc', ]
400+
verifylist = []
401+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
402+
403+
# In base command class Lister in cliff, abstract method take_action()
404+
# returns a tuple containing the column names and an iterable
405+
# containing the data to be listed.
406+
(columns, data) = self.cmd.take_action(parsed_args)
407+
self.projects_mock.list.assert_called_with()
408+
409+
collist = ('ID', 'Name')
410+
self.assertEqual(collist, columns)
411+
412+
if self.fake_projects[0].name > self.fake_projects[1].name:
413+
datalists = (
414+
(self.fake_projects[1].id, self.fake_projects[1].name),
415+
(self.fake_projects[0].id, self.fake_projects[0].name),
416+
)
417+
else:
418+
datalists = (
419+
(self.fake_projects[0].id, self.fake_projects[0].name),
420+
(self.fake_projects[1].id, self.fake_projects[1].name),
421+
)
422+
423+
self.assertEqual(datalists, tuple(data))
424+
389425

390426
class TestProjectSet(TestProject):
391427

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
@@ -479,6 +479,7 @@ class TestProjectList(TestProject):
479479
domain = identity_fakes.FakeDomain.create_one_domain()
480480
project = identity_fakes.FakeProject.create_one_project(
481481
attrs={'domain_id': domain.id})
482+
projects = identity_fakes.FakeProject.create_projects()
482483

483484
columns = (
484485
'ID',
@@ -490,6 +491,12 @@ class TestProjectList(TestProject):
490491
project.name,
491492
),
492493
)
494+
datalists = (
495+
(projects[0].description, True,
496+
projects[0].id, projects[0].name,),
497+
(projects[1].description, True,
498+
projects[1].id, projects[1].name,),
499+
)
493500

494501
def setUp(self):
495502
super(TestProjectList, self).setUp()
@@ -580,6 +587,36 @@ def test_project_list_domain_no_perms(self):
580587
self.assertEqual(self.columns, columns)
581588
self.assertEqual(self.datalist, tuple(data))
582589

590+
def test_project_list_sort(self):
591+
self.projects_mock.list.return_value = self.projects
592+
593+
arglist = ['--sort', 'name:asc', ]
594+
verifylist = []
595+
596+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
597+
598+
# In base command class Lister in cliff, abstract method take_action()
599+
# returns a tuple containing the column names and an iterable
600+
# containing the data to be listed.
601+
(columns, data) = self.cmd.take_action(parsed_args)
602+
self.projects_mock.list.assert_called_with()
603+
604+
collist = ('ID', 'Name')
605+
self.assertEqual(collist, columns)
606+
607+
if self.projects[0].name > self.projects[1].name:
608+
datalists = (
609+
(self.projects[1].id, self.projects[1].name),
610+
(self.projects[0].id, self.projects[0].name),
611+
)
612+
else:
613+
datalists = (
614+
(self.projects[0].id, self.projects[0].name),
615+
(self.projects[1].id, self.projects[1].name),
616+
)
617+
618+
self.assertEqual(datalists, tuple(data))
619+
583620

584621
class TestProjectSet(TestProject):
585622

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)