From 42d0669111d4095f8d72cadb249ceb60ab950c2d Mon Sep 17 00:00:00 2001
From: Pierre-Yves Chibon <pingou@pingoured.fr>
Date: Sep 26 2017 08:24:06 +0000
Subject: Add JSONP support to greenwave


This will make integrating with greenwave remotely and via JS easier.

Fixes https://pagure.io/greenwave/issue/65

Signed-off-by: Pierre-Yves Chibon <pingou@pingoured.fr>

Signed-off-by: Pierre-Yves Chibon <pingou@pingoured.fr>

---

diff --git a/greenwave/api_v1.py b/greenwave/api_v1.py
index 7f1f0fd..912fd1a 100644
--- a/greenwave/api_v1.py
+++ b/greenwave/api_v1.py
@@ -6,7 +6,7 @@ from werkzeug.exceptions import BadRequest, NotFound, UnsupportedMediaType
 from greenwave import __version__
 from greenwave.policies import summarize_answers
 from greenwave.resources import retrieve_results, retrieve_waivers
-from greenwave.utils import insert_headers
+from greenwave.utils import insert_headers, jsonp
 
 api = (Blueprint('api_v1', __name__))
 
@@ -14,6 +14,7 @@ requests_session = requests.Session()
 
 
 @api.route('/version', methods=['GET'])
+@jsonp
 def version():
     """ Returns the current running version.
 
@@ -40,6 +41,7 @@ def version():
 
 
 @api.route('/policies', methods=['GET'])
+@jsonp
 def get_policies():
     """ Returns all currently loaded policies.
 
@@ -89,6 +91,7 @@ def get_policies():
 
 
 @api.route('/decision', methods=['OPTIONS'])
+@jsonp
 def make_decision_options():
     """ Handles the OPTIONS requests to the /decision endpoint. """
     resp = current_app.make_default_options_response()
@@ -96,6 +99,7 @@ def make_decision_options():
 
 
 @api.route('/decision', methods=['POST'])
+@jsonp
 def make_decision():
     """
     Make a decision after evaluating all applicable policies based on test
diff --git a/greenwave/tests/test_policies.py b/greenwave/tests/test_policies.py
index 06fb395..42efe91 100644
--- a/greenwave/tests/test_policies.py
+++ b/greenwave/tests/test_policies.py
@@ -4,6 +4,7 @@
 import json
 import pytest
 
+from greenwave import __version__
 from greenwave.app_factory import create_app
 from greenwave.policies import summarize_answers, RuleSatisfied, TestResultMissing, TestResultFailed
 from greenwave.utils import load_policies
@@ -187,3 +188,25 @@ rules:
     with pytest.raises(RuntimeError) as excinfo:
         load_policies(tmpdir.strpath)
     assert 'Policies are not configured properly' in str(excinfo.value)
+
+
+def test_version_endpoint():
+    app = create_app('greenwave.config.TestingConfig')
+    test_app = app.test_client()
+    output = test_app.get(
+        '/api/v1.0/version',
+        headers={"content-type": "application/json"}
+    )
+    assert output.status_code == 200
+    assert output.data == '{\n  "version": "%s"\n}\n' % __version__
+
+
+def test_version_endpoint_jsonp():
+    app = create_app('greenwave.config.TestingConfig')
+    test_app = app.test_client()
+    output = test_app.get(
+        '/api/v1.0/version?callback=bac123',
+        headers={"content-type": "application/json"}
+    )
+    assert output.status_code == 200
+    assert output.data == 'bac123({\n  "version": "%s"\n}\n);' % __version__
diff --git a/greenwave/utils.py b/greenwave/utils.py
index 9b1a3ba..25c9623 100644
--- a/greenwave/utils.py
+++ b/greenwave/utils.py
@@ -1,9 +1,10 @@
 # SPDX-License-Identifier: GPL-2.0+
 
+import functools
 import os
 import glob
 import yaml
-from flask import jsonify, current_app
+from flask import jsonify, current_app, request
 from flask.config import Config
 from werkzeug.exceptions import HTTPException
 from greenwave.policies import Policy, Rule
@@ -32,6 +33,24 @@ def json_error(error):
     return response
 
 
+def jsonp(func):
+    """Wraps Jsonified output for JSONP requests."""
+    @functools.wraps(func)
+    def wrapped(*args, **kwargs):
+        callback = request.args.get('callback', False)
+        if callback:
+            resp = func(*args, **kwargs)
+            resp.set_data('{}({});'.format(
+                str(callback),
+                resp.get_data()
+            ))
+            resp.mimetype = 'application/javascript'
+            return resp
+        else:
+            return func(*args, **kwargs)
+    return wrapped
+
+
 def load_config(config_obj=None):
     """
     Load Greenwave configuration. If the config_obj is given, it will load the

