Repository: fedora-infra/bodhi

Issue:
The --notes-file parameter should be shared between the new and edit CLI commands
Right now the CLI's new update command has a ```--notes-file``` parameter that can be used to pass a file with notes to the CLI. The edit command has ```--notes``` but would also benefit from ```--notes-file```.


Discussion:

Resolving patch:
diff --git a/bodhi/client/__init__.py b/bodhi/client/__init__.py
--- a/bodhi/client/__init__.py
+++ b/bodhi/client/__init__.py
@@ -28,6 +28,7 @@
     click.option('--type', default='bugfix', help='Update type', required=True,
                  type=click.Choice(['security', 'bugfix', 'enhancement', 'newpackage'])),
     click.option('--notes', help='Update description'),
+    click.option('--notes-file', help='Update description from a file'),
     click.option('--bugs', help='Comma-seperated list of bug numbers', default=''),
     click.option('--close-bugs', default=True, is_flag=True, help='Automatically close bugs'),
     click.option('--request', help='Requested repository',
@@ -102,7 +103,6 @@ def updates():
 @updates.command()
 @add_options(new_edit_options)
 @click.argument('builds')
-@click.option('--notes-file', help='Update description from a file')
 @click.option('--file', help='A text file containing all the update details')
 @url_option
 def new(user, password, url, **kwargs):
@@ -125,14 +125,7 @@ def new(user, password, url, **kwargs):
     else:
         updates = client.parse_file(os.path.abspath(kwargs['file']))
 
-    if kwargs['notes_file'] is not None:
-        if kwargs['notes'] is None:
-            with open(kwargs['notes_file'], 'r') as fin:
-                kwargs['notes'] = fin.read()
-
-        else:
-            click.echo("ERROR: Cannot specify --notes and --notes-file")
-            sys.exit(1)
+    kwargs['notes'] = _get_notes(**kwargs)
 
     for update in updates:
         try:
@@ -162,6 +155,8 @@ def edit(user, password, url, **kwargs):
     client = bindings.BodhiClient(base_url=url, username=user, password=password,
                                   staging=kwargs['staging'])
 
+    kwargs['notes'] = _get_notes(**kwargs)
+
     try:
         query_param = {'updateid': kwargs['updateid']}
         del(kwargs['updateid'])
@@ -337,6 +332,29 @@ def download(url, **kwargs):
                         click.echo("WARNING: download of {0} failed!".format(build['nvr']))
 
 
+def _get_notes(**kwargs):
+    """
+    If the user provides a --notes-file, _get_notes processes the contents of the notes-file.
+    If the user does not provide a --notes-file, _get_notes() returns the notes from the kwargs.
+    One cannot specify both --notes and --notesfile. Doing so will result in an error.
+
+    Args:
+        kwargs (dict): Keyword arguments passed to us by click.
+
+    :returns: the contents of the notes file or the notes from kwargs
+    :rtype: string
+    """
+    if kwargs['notes_file'] is not None:
+        if kwargs['notes'] is None:
+            with open(kwargs['notes_file'], 'r') as fin:
+                return fin.read()
+        else:
+            click.echo("ERROR: Cannot specify --notes and --notes-file")
+            sys.exit(1)
+    else:
+        return kwargs['notes']
+
+
 @cli.group()
 def overrides():
     """


Test patch:
diff --git a/bodhi/tests/client/test___init__.py b/bodhi/tests/client/test___init__.py
--- a/bodhi/tests/client/test___init__.py
+++ b/bodhi/tests/client/test___init__.py
@@ -392,6 +392,61 @@ def test_url_flag(self, send_request, query):
                 'close_bugs': True, 'stable_karma': None, 'csrf_token': 'a_csrf_token',
                 'staging': False, 'builds': u'nodejs-grunt-wrap-0.3.0-2.fc25', 'autokarma': True,
                 'edited': u'nodejs-grunt-wrap-0.3.0-2.fc25', 'suggest': None,
-                'notes': u'this is an edited note', 'request': None, 'bugs': u'',
-                'unstable_karma': None, 'type': 'bugfix'})
+                'notes': u'this is an edited note', 'notes_file': None,
+                'request': None, 'bugs': u'', 'unstable_karma': None, 'type': 'bugfix'})
         self.assertEqual(bindings_client.base_url, 'http://localhost:6543/')
+
+    @mock.patch('bodhi.client.bindings.BodhiClient.csrf',
+                mock.MagicMock(return_value='a_csrf_token'))
+    @mock.patch('bodhi.client.bindings.BodhiClient.query',
+                return_value=client_test_data.EXAMPLE_QUERY_MUNCH, autospec=True)
+    @mock.patch('bodhi.client.bindings.BodhiClient.send_request',
+                return_value=client_test_data.EXAMPLE_UPDATE_MUNCH, autospec=True)
+    def test_notes_file(self, send_request, query):
+        """
+        Assert that a valid notes-file is properly handled in a successful updates
+        edit request.
+        """
+        runner = testing.CliRunner()
+        with runner.isolated_filesystem():
+            with open('notefile.txt', 'w') as f:
+                f.write('This is a --notes-file note!')
+
+            result = runner.invoke(
+                client.edit, ['FEDORA-2017-cc8582d738', '--user', 'bowlofeggs',
+                              '--password', 's3kr3t', '--notes-file', 'notefile.txt',
+                              '--url', 'http://localhost:6543'])
+
+            self.assertEqual(result.exit_code, 0)
+            bindings_client = query.mock_calls[0][1][0]
+            query.assert_called_with(
+                bindings_client, updateid=u'FEDORA-2017-cc8582d738')
+            bindings_client = send_request.mock_calls[0][1][0]
+            send_request.assert_called_with(
+                bindings_client, 'updates/', auth=True, verb='POST',
+                data={
+                    'close_bugs': True, 'stable_karma': None, 'csrf_token': 'a_csrf_token',
+                    'staging': False, 'builds': u'nodejs-grunt-wrap-0.3.0-2.fc25',
+                    'autokarma': True, 'edited': u'nodejs-grunt-wrap-0.3.0-2.fc25',
+                    'suggest': None, 'notes': 'This is a --notes-file note!',
+                    'notes_file': 'notefile.txt', 'request': None, 'bugs': u'',
+                    'unstable_karma': None, 'type': 'bugfix'})
+            self.assertEqual(bindings_client.base_url, 'http://localhost:6543/')
+
+    def test_notes_and_notes_file(self):
+        """
+        Assert providing both --notes-file and --notes parameters to an otherwise successful
+        updates edit request results in an error.
+        """
+        runner = testing.CliRunner()
+        with runner.isolated_filesystem():
+            with open('notefile.txt', 'w') as f:
+                f.write('This is a --notes-file note!')
+
+            result = runner.invoke(
+                client.edit, ['FEDORA-2017-cc8582d738', '--user', 'bowlofeggs',
+                              '--password', 's3kr3t', '--notes', 'this is a notey note',
+                              '--notes-file', 'notefile.txt', '--url', 'http://localhost:6543'])
+
+            self.assertEqual(result.exit_code, 1)
+            self.assertEqual(result.output, u'ERROR: Cannot specify --notes and --notes-file\n')