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:
		
				
					committed by
					
						
						Mike Perez
					
				
			
			
				
	
			
			
			
						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>`_]
 | 
				
			||||||
		Reference in New Issue
	
	Block a user