From 27ba71d97cc02bcd1cc1a6db8c24d1042e52908c Mon Sep 17 00:00:00 2001
From: Lukas Holecek <lholecek@redhat.com>
Date: May 07 2018 08:12:57 +0000
Subject: Allow creating multiple waivers


To create multiple waivers, POST list to "waivers/" instead of single
waiver (`[{...}, ...]` instead of `{...}`).

Fixes #98

---

diff --git a/tests/test_api_v10.py b/tests/test_api_v10.py
index 9ab0541..7291682 100644
--- a/tests/test_api_v10.py
+++ b/tests/test_api_v10.py
@@ -9,6 +9,7 @@ from mock import patch, Mock
 
 from .utils import create_waiver
 from waiverdb import __version__
+from waiverdb.models import Waiver
 
 
 @patch('waiverdb.auth.get_user', return_value=('foo', {}))
@@ -567,3 +568,59 @@ def test_no_cors_waivers(client, session):
     res_data = json.loads(r.get_data(as_text=True))
     assert r.status_code == 200
     assert len(res_data['data']) == 3
+
+
+@patch('waiverdb.auth.get_user', return_value=('foo', {}))
+def test_create_multiple_waivers(mocked_get_user, client, session):
+    item1 = {
+        'subject': {'subject.test': 'subject1'},
+        'testcase': 'testcase1',
+        'product_version': 'fool-1',
+        'waived': True,
+        'comment': 'it broke',
+    }
+    item2 = {
+        'subject': {'subject.test': 'subject2'},
+        'testcase': 'testcase2',
+        'product_version': 'fool-2',
+        'waived': False,
+        'comment': 'fixed',
+    }
+    data = [item1, item2]
+
+    r = client.post('/api/v1.0/waivers/', data=json.dumps(data),
+                    content_type='application/json')
+
+    res_data = json.loads(r.get_data(as_text=True))
+    assert r.status_code == 201
+    assert isinstance(res_data, list)
+    assert len(res_data) == 2
+
+    actual_item1 = {k: v for k, v in res_data[0].items() if k in item1}
+    actual_item2 = {k: v for k, v in res_data[1].items() if k in item2}
+    assert actual_item1 == item1
+    assert actual_item2 == item2
+
+    # Transaction was not rolled back.
+    assert session.query(Waiver).count() == 2
+
+
+@patch('waiverdb.auth.get_user', return_value=('foo', {}))
+def test_create_multiple_waivers_rollback_on_error(mocked_get_user, client, session):
+    item1 = {
+        'subject': {'subject.test': 'subject1'},
+        'testcase': 'testcase1',
+        'product_version': 'fool-1',
+        'waived': True,
+        'comment': 'it broke',
+    }
+    item2 = {}
+    data = [item1, item2]
+
+    r = client.post('/api/v1.0/waivers/', data=json.dumps(data),
+                    content_type='application/json')
+
+    assert r.status_code == 400
+
+    # Transaction was rolled back.
+    assert session.query(Waiver).count() == 0
diff --git a/waiverdb/api_v1.py b/waiverdb/api_v1.py
index 8090f13..a62ab3a 100644
--- a/waiverdb/api_v1.py
+++ b/waiverdb/api_v1.py
@@ -111,6 +111,15 @@ RP['get_waivers'].add_argument('limit', default=10, type=int, location='args')
 RP['get_waivers'].add_argument('proxied_by', location='args')
 
 
+class DummyJsonRequest(object):
+    """
+    Can be passed to reqparse.RequestParser.parse_args() instead of current
+    request.
+    """
+    def __init__(self, data):
+        self.json = data
+
+
 class WaiversResource(Resource):
     @jsonp
     def get(self):
@@ -194,7 +203,10 @@ class WaiversResource(Resource):
     @marshal_with(waiver_fields)
     def post(self):
         """
-        Create a new waiver.
+        Create a new waiver or multiple waivers.
+
+        To create multiple waivers, pass list of dict instead. Response also
+        contains list on success.
 
         **Sample request**:
 
@@ -251,7 +263,26 @@ class WaiversResource(Resource):
         """
 
         user, headers = waiverdb.auth.get_user(request)
-        args = RP['create_waiver'].parse_args()
+        data = request.get_json(force=True)
+
+        if isinstance(data, list):
+            result = []
+            for sub_data in data:
+                sub_request = DummyJsonRequest(sub_data)
+                args = RP['create_waiver'].parse_args(sub_request)
+                one_result = self._create_waiver(args, user)
+                result.append(one_result)
+            db.session.add_all(result)
+        else:
+            args = RP['create_waiver'].parse_args()
+            result = self._create_waiver(args, user)
+            db.session.add(result)
+
+        db.session.commit()
+
+        return result, 201, headers
+
+    def _create_waiver(self, args, user):
         proxied_by = None
         if args.get('username'):
             if user not in current_app.config['SUPERUSERS']:
@@ -295,11 +326,14 @@ class WaiversResource(Resource):
         if not args['comment']:
             raise BadRequest('Comment is a required argument.')
 
-        waiver = Waiver(args['subject'], args['testcase'], user,
-                        args['product_version'], args['waived'], args['comment'], proxied_by)
-        db.session.add(waiver)
-        db.session.commit()
-        return waiver, 201, headers
+        return Waiver(
+            args['subject'],
+            args['testcase'],
+            user,
+            args['product_version'],
+            args['waived'],
+            args['comment'],
+            proxied_by)
 
 
 class WaiverResource(Resource):

