Update projects commands to use pluggable formatters

This allows users to specify the formatter they want to use and have the
project-* subcommands honor it.

Change-Id: I4ea6f0d679f6167a1b4865d8d51f5c6ef07c58cd
This commit is contained in:
Ian Cordasco
2017-02-28 13:15:10 -06:00
parent d8e31c39cc
commit 6bb372ea37
3 changed files with 32 additions and 37 deletions

View File

@@ -25,8 +25,7 @@ from cratonclient.v1 import projects
def do_project_show(cc, args): def do_project_show(cc, args):
"""Show detailed information about a project.""" """Show detailed information about a project."""
project = cc.projects.get(args.id) project = cc.projects.get(args.id)
data = {f: getattr(project, f, '') for f in projects.PROJECT_FIELDS} args.formatter.configure(wrap=72).handle(project)
cliutils.print_dict(data, wrap=72)
@cliutils.arg('-n', '--name', @cliutils.arg('-n', '--name',
@@ -88,7 +87,7 @@ def do_project_list(cc, args):
params['autopaginate'] = args.all params['autopaginate'] = args.all
listed_projects = cc.projects.list(**params) listed_projects = cc.projects.list(**params)
cliutils.print_list(listed_projects, list(fields)) args.formatter.configure(fields=list(fields)).handle(listed_projects)
@cliutils.arg('-n', '--name', @cliutils.arg('-n', '--name',
@@ -100,8 +99,7 @@ def do_project_create(cc, args):
fields = {k: v for (k, v) in vars(args).items() fields = {k: v for (k, v) in vars(args).items()
if k in projects.PROJECT_FIELDS and not (v is None)} if k in projects.PROJECT_FIELDS and not (v is None)}
project = cc.projects.create(**fields) project = cc.projects.create(**fields)
data = {f: getattr(project, f, '') for f in projects.PROJECT_FIELDS} args.formatter.configure(wrap=72).handle(project)
cliutils.print_dict(data, wrap=72)
@cliutils.arg('id', @cliutils.arg('id',

View File

@@ -29,14 +29,22 @@ class TestProjectsShell(base.ShellTestCase):
re_options = re.DOTALL | re.MULTILINE re_options = re.DOTALL | re.MULTILINE
project_valid_fields = None project_valid_fields = None
project_invalid_field = None project_invalid_fields = None
def setUp(self): def setUp(self):
"""Setup required test fixtures.""" """Setup required test fixtures."""
super(TestProjectsShell, self).setUp() super(TestProjectsShell, self).setUp()
self.project_valid_fields = Namespace(name='mock_project') self.project_valid_kwargs = {
self.project_invalid_field = Namespace(name='mock_project', 'name': 'mock_project',
invalid_foo='ignored') }
self.project_invalid_kwargs = {
'name': 'mock_project',
'invalid_foo': 'ignored',
}
self.project_valid_fields = Namespace(**self.project_valid_kwargs)
self.project_invalid_fields = Namespace(**self.project_invalid_kwargs)
self.project_valid_fields.formatter = mock.Mock()
self.project_invalid_fields.formatter = mock.Mock()
@mock.patch('cratonclient.v1.projects.ProjectManager.list') @mock.patch('cratonclient.v1.projects.ProjectManager.list')
def test_project_list_success(self, mock_list): def test_project_list_success(self, mock_list):
@@ -92,17 +100,13 @@ class TestProjectsShell(base.ShellTestCase):
) )
@mock.patch('cratonclient.v1.projects.ProjectManager.list') @mock.patch('cratonclient.v1.projects.ProjectManager.list')
@mock.patch('cratonclient.common.cliutils.print_list') def test_project_list_fields_success(self, mock_list):
def test_project_list_fields_success(self, mock_printlist, mock_list):
"""Verify --fields argument successfully passed to Client.""" """Verify --fields argument successfully passed to Client."""
self.shell('project-list --fields id name') self.shell('project-list --fields id name')
mock_list.assert_called_once_with( mock_list.assert_called_once_with(
marker=None, marker=None,
autopaginate=False, autopaginate=False,
) )
mock_printlist.assert_called_once_with(mock.ANY,
list({'id': 'ID',
'name': 'Name'}))
def test_project_create_missing_required_args(self): def test_project_create_missing_required_args(self):
"""Verify that missing required args results in error message.""" """Verify that missing required args results in error message."""
@@ -126,7 +130,7 @@ class TestProjectsShell(base.ShellTestCase):
region_id=mock.ANY, region_id=mock.ANY,
) )
projects_shell.do_project_create(client, self.project_valid_fields) projects_shell.do_project_create(client, self.project_valid_fields)
mock_create.assert_called_once_with(**vars(self.project_valid_fields)) mock_create.assert_called_once_with(**self.project_valid_kwargs)
@mock.patch('cratonclient.v1.projects.ProjectManager.create') @mock.patch('cratonclient.v1.projects.ProjectManager.create')
def test_do_project_create_ignores_unknown_fields(self, mock_create): def test_do_project_create_ignores_unknown_fields(self, mock_create):
@@ -136,8 +140,8 @@ class TestProjectsShell(base.ShellTestCase):
mock.ANY, 'http://127.0.0.1/', mock.ANY, 'http://127.0.0.1/',
region_id=mock.ANY, region_id=mock.ANY,
) )
projects_shell.do_project_create(client, self.project_invalid_field) projects_shell.do_project_create(client, self.project_invalid_fields)
mock_create.assert_called_once_with(**vars(self.project_valid_fields)) mock_create.assert_called_once_with(**self.project_valid_kwargs)
def test_project_show_missing_required_args(self): def test_project_show_missing_required_args(self):
"""Verify that missing required args results in error message.""" """Verify that missing required args results in error message."""
@@ -159,9 +163,9 @@ class TestProjectsShell(base.ShellTestCase):
mock.ANY, 'http://127.0.0.1/', mock.ANY, 'http://127.0.0.1/',
region_id=mock.ANY, region_id=mock.ANY,
) )
test_args = Namespace(id=1) test_args = Namespace(id=1, formatter=mock.Mock())
projects_shell.do_project_show(client, test_args) projects_shell.do_project_show(client, test_args)
mock_get.assert_called_once_with(vars(test_args)['id']) mock_get.assert_called_once_with(1)
def test_project_delete_missing_required_args(self): def test_project_delete_missing_required_args(self):
"""Verify that missing required args results in error message.""" """Verify that missing required args results in error message."""

View File

@@ -35,10 +35,8 @@ class TestDoShellShow(base.TestShellCommandUsingPrintDict):
projects_shell.do_project_show(self.craton_client, args) projects_shell.do_project_show(self.craton_client, args)
self.craton_client.projects.get.assert_called_once_with(456) self.craton_client.projects.get.assert_called_once_with(456)
self.print_dict.assert_called_once_with( self.formatter.configure.assert_called_once_with(wrap=72)
{f: mock.ANY for f in projects.PROJECT_FIELDS}, self.assertEqual(1, self.formatter.handle.call_count)
wrap=72,
)
class TestDoProjectList(base.TestShellCommandUsingPrintList): class TestDoProjectList(base.TestShellCommandUsingPrintList):
@@ -47,7 +45,8 @@ class TestDoProjectList(base.TestShellCommandUsingPrintList):
def assertNothingWasCalled(self): def assertNothingWasCalled(self):
"""Assert inventory, list, and print_list were not called.""" """Assert inventory, list, and print_list were not called."""
super(TestDoProjectList, self).assertNothingWasCalled() super(TestDoProjectList, self).assertNothingWasCalled()
self.assertFalse(self.print_list.called) self.assertFalse(self.formatter.configure.called)
self.assertFalse(self.formatter.handle.called)
def args_for(self, **kwargs): def args_for(self, **kwargs):
"""Generate the default argument list for project-list.""" """Generate the default argument list for project-list."""
@@ -69,9 +68,7 @@ class TestDoProjectList(base.TestShellCommandUsingPrintList):
marker=None, marker=None,
autopaginate=False, autopaginate=False,
) )
self.assertTrue(self.print_list.called) self.assertSortedFieldsEqualTo(['id', 'name'])
self.assertEqual(['id', 'name'],
sorted(self.print_list.call_args[0][-1]))
def test_negative_limit(self): def test_negative_limit(self):
"""Ensure we raise an exception for negative limits.""" """Ensure we raise an exception for negative limits."""
@@ -92,9 +89,7 @@ class TestDoProjectList(base.TestShellCommandUsingPrintList):
autopaginate=False, autopaginate=False,
marker=None, marker=None,
) )
self.assertTrue(self.print_list.called) self.assertSortedFieldsEqualTo(['id', 'name'])
self.assertEqual(['id', 'name'],
sorted(self.print_list.call_args[0][-1]))
def test_detail(self): def test_detail(self):
"""Verify the behaviour of specifying --detail.""" """Verify the behaviour of specifying --detail."""
@@ -106,8 +101,7 @@ class TestDoProjectList(base.TestShellCommandUsingPrintList):
marker=None, marker=None,
autopaginate=False, autopaginate=False,
) )
self.assertEqual(sorted(list(projects.PROJECT_FIELDS)), self.assertSortedFieldsEqualTo(sorted(list(projects.PROJECT_FIELDS)))
sorted(self.print_list.call_args[0][-1]))
def test_list_name(self): def test_list_name(self):
"""Verify the behaviour of specifying --detail.""" """Verify the behaviour of specifying --detail."""
@@ -120,8 +114,7 @@ class TestDoProjectList(base.TestShellCommandUsingPrintList):
autopaginate=False, autopaginate=False,
marker=None, marker=None,
) )
self.assertEqual(['id', 'name'], self.assertSortedFieldsEqualTo(['id', 'name'])
sorted(self.print_list.call_args[0][-1]))
def test_raises_exception_with_detail_and_fields(self): def test_raises_exception_with_detail_and_fields(self):
"""Verify that we fail when users specify --detail and --fields.""" """Verify that we fail when users specify --detail and --fields."""
@@ -143,8 +136,7 @@ class TestDoProjectList(base.TestShellCommandUsingPrintList):
autopaginate=False, autopaginate=False,
marker=None, marker=None,
) )
self.assertEqual(['id', 'name'], self.assertSortedFieldsEqualTo(['id', 'name'])
sorted(self.print_list.call_args[0][-1]))
def test_invalid_fields(self): def test_invalid_fields(self):
"""Verify that we error out with invalid fields.""" """Verify that we error out with invalid fields."""
@@ -207,7 +199,8 @@ class TestDoProjectCreate(base.TestShellCommandUsingPrintDict):
self.craton_client.projects.create.assert_called_once_with( self.craton_client.projects.create.assert_called_once_with(
name='New Project' name='New Project'
) )
self.print_dict.assert_called_once_with(mock.ANY, wrap=72) self.formatter.configure.assert_called_once_with(wrap=72)
self.assertEqual(1, self.formatter.handle.call_count)
class TestDoProjectDelete(base.TestShellCommand): class TestDoProjectDelete(base.TestShellCommand):