Repository: fedora-infra/bodhi

Issue:
BROs are double plus unfun when users try to create them and they already exist
@AdamWill reported today a frustrating user experience: if you try to create a buildroot override for an nvr, and an override already exists for that nvr, it will fail to create the BRO but will not redirect you or give you a link to the existing one.

We could have it just force the existing BRO to be to the new parameters, but that might be bad since the existing notes might be lost. A couple ideas:

1. It could redirect you to the edit form of the existing BRO. This way you can see and edit the existing notes field without blindly replacing it.
2. It could append your notes to the existing notes, perhaps with some autoformatting so it's clear which user wrote what. It could also pick the latter of the two dates if the BRO dates differ.

I think the second option wouldn't be too difficult. If we do it that way, the same fix might also work for the CLI.


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
@@ -840,16 +840,7 @@ def save_buildroot_overrides(user, password, url, staging, **kwargs):
         staging (bool): Whether to use the staging server or not.
         kwargs (dict): Other keyword arguments passed to us by click.
     """
-
-    try:
-        _save_override(url=url, user=user, password=password, staging=staging, **kwargs)
-    except bindings.BodhiClientException as e:
-        if str(e) == "Buildroot override for %s already exists" % (kwargs['nvr']):
-            click.echo(str(e))
-            click.echo("The `overrides save` command is used for creating a new override.")
-            click.echo("Use `overrides edit` to edit an existing override.")
-        else:
-            raise
+    _save_override(url=url, user=user, password=password, staging=staging, **kwargs)
 
 
 @overrides.command('edit')
diff --git a/bodhi/server/models.py b/bodhi/server/models.py
--- a/bodhi/server/models.py
+++ b/bodhi/server/models.py
@@ -4438,6 +4438,8 @@ def edit(cls, request, **data):
         override.submitter = data['submitter']
         override.notes = data['notes']
         override.expiration_date = data['expiration_date']
+        if 'submission_date' in data:
+            override.submission_date = data['submission_date']
 
         now = datetime.utcnow()
 
diff --git a/bodhi/server/services/overrides.py b/bodhi/server/services/overrides.py
--- a/bodhi/server/services/overrides.py
+++ b/bodhi/server/services/overrides.py
@@ -19,6 +19,7 @@
 """Define API endpoints for managing and searching buildroot overrides."""
 
 import math
+from datetime import datetime
 
 from cornice import Service
 from cornice.validators import colander_body_validator, colander_querystring_validator
@@ -257,10 +258,29 @@ def save_override(request):
                 })
             for build in builds:
                 log.info("Creating a new buildroot override: %s" % build.nvr)
-                if BuildrootOverride.get(build.id):
-                    request.errors.add('body', 'builds',
-                                       'Buildroot override for %s already exists' % build.nvr)
-                    return
+                existing_override = BuildrootOverride.get(build.id)
+                if existing_override:
+                    if not existing_override.expired_date:
+                        data['expiration_date'] = max(existing_override.expiration_date,
+                                                      data['expiration_date'])
+
+                    data['notes'] = """{0}
+
+_@{1} ({2})_
+
+_____________
+{3}""".format(existing_override.notes, existing_override.submitter.name,
+                        existing_override.submission_date.strftime("%b %d, %Y"), data['notes'])
+
+                    overrides.append(BuildrootOverride.edit(
+                        request,
+                        edited=build,
+                        submitter=submitter,
+                        submission_date=datetime.now(),
+                        notes=data['notes'],
+                        expiration_date=data['expiration_date'],
+                        expired=None,
+                    ))
                 else:
                     overrides.append(BuildrootOverride.new(
                         request,


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
@@ -1132,27 +1132,6 @@ def _send_request(*args, **kwargs):
                 params={'ids': [15]}))
         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.send_request', autospec=True)
-    def test_existing_override_error_message(self, send_request):
-        """
-        Assert that the error message is provided if we try to save an existing override
-        """
-        exception_message = "Buildroot override for js-tag-it-2.0-1.fc25 already exists"
-        send_request.side_effect = bindings.BodhiClientException(exception_message)
-        runner = testing.CliRunner()
-
-        result = runner.invoke(
-            client.save_buildroot_overrides,
-            ['--user', 'bowlofeggs', '--password', 's3kr3t', 'js-tag-it-2.0-1.fc25'])
-
-        self.assertEqual(result.exit_code, 0)
-        self.assertIn("The `overrides save` command is used for creating a new override",
-                      result.output)
-        self.assertIn("Use `overrides edit` to edit an existing override",
-                      result.output)
-
 
 class TestWarnIfUrlAndStagingSet(unittest.TestCase):
     """
diff --git a/bodhi/tests/server/services/test_overrides.py b/bodhi/tests/server/services/test_overrides.py
--- a/bodhi/tests/server/services/test_overrides.py
+++ b/bodhi/tests/server/services/test_overrides.py
@@ -301,9 +301,16 @@ def test_create_duplicate_override(self, publish):
         self.assertEqual(o['expired_date'], None)
 
         # Submit it again
-        res = self.app.post('/overrides/', data, status=400)
-        self.assertEqual(res.json_body['errors'][0]['description'],
-                         'Buildroot override for %s already exists' % build.nvr)
+        data['notes'] = 'new blah blah'
+        res = self.app.post('/overrides/', data)
+        o = res.json_body
+        new_notes = """blah blah blah
+
+_@guest ({})_
+
+_____________
+new blah blah""".format(datetime.utcnow().strftime("%b %d, %Y"))
+        self.assertEquals(o['notes'], new_notes)
 
     @mock.patch('bodhi.server.notifications.publish')
     def test_create_override_multiple_nvr(self, publish):