Browse Source

Rest API review

nico 8 years ago
parent
commit
4a3e68e813

+ 16 - 0
Docs/rest_api.md

@@ -6,6 +6,7 @@ Kalliope provides the REST API to manage the synapses. For configuring the API r
 
 | Method | URL                               | Action                             |
 |--------|-----------------------------------|------------------------------------|
+| GET    | /                                 | Get kaliope version                |
 | GET    | /synapses                         | List synapses                      |
 | GET    | /synapses/<synapse_name>          | Get synapse details by name        |
 | POST   | /synapses/start/id/<synapse_name> | Run a synapse by its name          |
@@ -16,6 +17,21 @@ Kalliope provides the REST API to manage the synapses. For configuring the API r
 
 >**Note:** --user is only needed if `password_protected` is True
 
+### Get Kalliope's version
+
+Normal response codes: 200
+Error response codes: unauthorized(401)
+Curl command:
+```bash
+curl -i --user admin:secret -X GET  http://localhost:5000/
+```
+Output example:
+```JSON
+{
+  "Kalliope version": "0.4.2"
+}
+```
+
 ### List synapses
 
 Normal response codes: 200

+ 19 - 0
Tests/brains/brain_test_api.yml

@@ -0,0 +1,19 @@
+---
+  - name: "test"
+    signals:
+      - order: "test_order"
+    neurons:
+      - say:
+          message:
+            - "test message"
+
+  - name: "test2"
+    signals:
+      - order: "bonjour"
+    neurons:
+      - say:
+          message:
+            - "test message"
+
+  - includes:
+    - included_brain_test.yml

BIN
Tests/files/bonjour.wav


+ 244 - 183
Tests/test_rest_api.py

@@ -1,11 +1,14 @@
+import json
 import os
 import unittest
 
+from werkzeug.datastructures import FileStorage
+
 from flask import Flask
 from flask_testing import LiveServerTestCase
 
 from kalliope.core.Models import Singleton
-
+from kalliope._version import version_str
 from kalliope.core.ConfigurationManager import BrainLoader
 from kalliope.core.ConfigurationManager import SettingLoader
 from kalliope.core.RestAPI.FlaskAPI import FlaskAPI
@@ -20,8 +23,12 @@ class TestRestAPI(LiveServerTestCase):
         # be sure that the singleton haven't been loaded before
         Singleton._instances = {}
         current_path = os.getcwd()
-        full_path_brain_to_test = current_path + os.sep + "Tests/brains/brain_test.yml"
-        print full_path_brain_to_test
+        if "/Tests" in os.getcwd():
+            full_path_brain_to_test = current_path + os.sep + "brains/brain_test_api.yml"
+            self.audio_file = "files/bonjour.wav"
+        else:
+            full_path_brain_to_test = current_path + os.sep + "Tests/brains/brain_test_api.yml"
+            self.audio_file = "Tests/files/bonjour.wav"
 
         # rest api config
         sl = SettingLoader()
@@ -36,196 +43,250 @@ class TestRestAPI(LiveServerTestCase):
         brain = brain_loader.brain
 
         self.app = Flask(__name__)
+        self.app.config['TESTING'] = True
         self.flask_api = FlaskAPI(self.app, port=5000, brain=brain)
-        self.flask_api.app.config['TESTING'] = True
+        self.client = self.app.test_client()
         return self.flask_api.app
 
-    # TODO all following test passes with 'python -m unittest Tests.TestRestAPI' but not with discover
-    # def test_get_all_synapses(self):
-    #     url = "http://127.0.0.1:5000/synapses"
-    #
-    #     result = requests.get(url=url)
-    #     expected_content = {
-    #         "synapses": [
-    #             {
-    #                 "name": "test",
-    #                 "neurons": [
-    #                     {
-    #                         "say": {
-    #                             "message": [
-    #                                 "test message"
-    #                             ]
-    #                         }
-    #                     }
-    #                 ],
-    #                 "signals": [
-    #                     {
-    #                         "order": "test_order"
-    #                     }
-    #                 ]
-    #             },
-    #             {
-    #                 "name": "test2",
-    #                 "neurons": [
-    #                     {
-    #                         "say": {
-    #                             "message": [
-    #                                 "test message"
-    #                             ]
-    #                         }
-    #                     }
-    #                 ],
-    #                 "signals": [
-    #                     {
-    #                         "order": "test_order_2"
-    #                     }
-    #                 ]
-    #             },
-    #             {
-    #                 "includes": [
-    #                     "included_brain_test.yml"
-    #                 ]
-    #             },
-    #             {
-    #                 "name": "test3",
-    #                 "neurons": [
-    #                     {
-    #                         "say": {
-    #                             "message": [
-    #                                 "test message"
-    #                             ]
-    #                         }
-    #                     }
-    #                 ],
-    #                 "signals": [
-    #                     {
-    #                         "order": "test_order_3"
-    #                     }
-    #                 ]
-    #             }
-    #         ]
-    #     }
-    #
-    #     self.assertEqual(result.status_code, 200)
-    #     self.assertEqual(expected_content, json.loads(result.content))
-    #
-    # def test_get_one_synapse(self):
-    #     url = "http://127.0.0.1:5000/synapses/test"
-    #     result = requests.get(url=url)
-    #
-    #     expected_content = {
-    #         "synapses": {
-    #             "name": "test",
-    #             "neurons": [
-    #                 {
-    #                     "say": {
-    #                         "message": [
-    #                             "test message"
-    #                         ]
-    #                     }
-    #                 }
-    #             ],
-    #             "signals": [
-    #                 {
-    #                     "order": "test_order"
-    #                 }
-    #             ]
-    #         }
-    #     }
-    #
-    #     self.assertEqual(expected_content, json.loads(result.content))
-    #
-    # def test_get_synapse_not_found(self):
-    #     url = "http://127.0.0.1:5000/synapses/test-none"
-    #     result = requests.get(url=url)
-    #
-    #     expected_content = {
-    #         "error": {
-    #             "synapse name not found": "test-none"
+    def test_server_is_up_and_running(self):
+        # response = urllib2.urlopen(self.get_server_url())
+        response = self.client.get(self.get_server_url())
+        self.assertEqual(response.status_code, 200)
+
+    def test_get_main_page(self):
+        url = self.get_server_url() + "/"
+        response = self.client.get(url)
+        expected_content = {
+            "Kalliope version": "%s" % version_str
+        }
+        self.assertEqual(json.dumps(expected_content), json.dumps(json.loads(response.get_data())))
+
+    def test_get_all_synapses(self):
+        url = self.get_server_url()+"/synapses"
+
+        response = self.client.get(url)
+        expected_content = {
+            "synapses": [
+                {
+                    "name": "test",
+                    "neurons": [
+                        {
+                            "name": "say",
+                            "parameters": {
+                                "message": [
+                                    "test message"
+                                ]
+                            }
+                        }
+                    ],
+                    "signals": [
+                        {
+                            "order": "test_order"
+                        }
+                    ]
+                },
+                {
+                    "name": "test2",
+                    "neurons": [
+                        {
+                            "name": "say",
+                            "parameters": {
+                                "message": [
+                                    "test message"
+                                ]
+                            }
+                        }
+                    ],
+                    "signals": [
+                        {
+                            "order": "bonjour"
+                        }
+                    ]
+                },
+                {
+                    "name": "test3",
+                    "neurons": [
+                        {
+                            "name": "say",
+                            "parameters": {
+                                "message": [
+                                    "test message"
+                                ]
+                            }
+                        }
+                    ],
+                    "signals": [
+                        {
+                            "order": "test_order_3"
+                        }
+                    ]
+                }
+            ]
+        }
+        # a lot of char ti process
+        self.maxDiff = None
+        self.assertEqual(response.status_code, 200)
+        # print response.get_data()
+        # print json.dumps(expected_content)
+        self.assertEqual(json.dumps(expected_content), json.dumps(json.loads(response.get_data())))
+
+    def test_get_one_synapse(self):
+        url = self.get_server_url() + "/synapses/test"
+        response = self.client.get(url)
+
+        expected_content = {
+            "synapses": {
+                "name": "test",
+                "neurons": [
+                    {
+                        "name": "say",
+                        "parameters": {
+                            "message": [
+                                "test message"
+                            ]
+                        }
+                    }
+                ],
+                "signals": [
+                    {
+                        "order": "test_order"
+                    }
+                ]
+            }
+        }
+        self.assertEqual(json.dumps(expected_content), json.dumps(json.loads(response.get_data())))
+
+    def test_get_synapse_not_found(self):
+        url = self.get_server_url() + "/synapses/test-none"
+        result = self.client.get(url)
+
+        expected_content = {
+            "error": {
+                "synapse name not found": "test-none"
+            }
+        }
+
+        self.assertEqual(expected_content, json.loads(result.get_data()))
+        self.assertEqual(result.status_code, 404)
+
+    def test_run_synapse_by_name(self):
+        url = self.get_server_url() + "/synapses/start/id/test"
+        result = self.client.post(url)
+
+        expected_content = {
+            "synapses": {
+                "name": "test",
+                "neurons": [
+                    {
+                        "name": "say",
+                        "parameters": {
+                            "message": [
+                                "test message"
+                            ]
+                        }
+                    }
+                ],
+                "signals": [
+                    {
+                        "order": "test_order"
+                    }
+                ]
+            }
+        }
+        self.assertEqual(json.dumps(expected_content), json.dumps(json.loads(result.get_data())))
+        self.assertEqual(result.status_code, 201)
+
+    def test_post_synapse_not_found(self):
+        url = self.get_server_url() + "/synapses/start/id/test-none"
+        result = self.client.post(url)
+
+        expected_content = {
+            "error": {
+                "synapse name not found": "test-none"
+            }
+        }
+
+        self.assertEqual(json.dumps(expected_content), json.dumps(json.loads(result.get_data())))
+        self.assertEqual(result.status_code, 404)
+
+    def test_run_synapse_with_order(self):
+        url = self.get_server_url() + "/synapses/start/order"
+        headers = {"Content-Type": "application/json"}
+        data = {"order": "test_order"}
+        result = self.client.post(url, headers=headers, data=json.dumps(data))
+
+        expected_content = {
+            "synapses": [
+                {
+                    "name": "test",
+                    "neurons": [
+                        {
+                            "name": "say",
+                            "parameters": {
+                                "message": [
+                                    "test message"
+                                ]
+                            }
+                        }
+                    ],
+                    "signals": [
+                        {
+                            "order": "test_order"
+                        }
+                    ]
+                }
+            ]
+        }
+        print result.get_data()
+        self.assertEqual(json.dumps(expected_content), json.dumps(json.loads(result.get_data())))
+        self.assertEqual(result.status_code, 201)
+
+    def test_post_synapse_by_order_not_found(self):
+        url = self.get_server_url() + "/synapses/start/order"
+        data = {"order": "non existing order"}
+        headers = {"Content-Type": "application/json"}
+        result = self.client.post(url, headers=headers, data=json.dumps(data))
+
+        expected_content = {'error': {'error': "The given order doesn't match any synapses"}}
+
+        self.assertEqual(json.dumps(expected_content), json.dumps(json.loads(result.get_data())))
+        self.assertEqual(result.status_code, 400)
+
+    # TODO this doesn't work on travis but works locally with python -m unittest discover
+    # def test_post_synapse_by_audio(self):
+    #     url = self.get_server_url() + "/synapses/start/audio"
+    #     with open(os.path.join(self.audio_file), 'rb') as fp:
+    #         file = FileStorage(fp)
+    #         data = {
+    #             'file': file
     #         }
-    #     }
+    #         result = self.client.post(url, data=data, content_type='multipart/form-data')
     #
-    #     self.assertEqual(expected_content, json.loads(result.content))
-    #     self.assertEqual(result.status_code, 404)
-    #
-    # def test_run_synapse_by_name(self):
-    #     url = "http://127.0.0.1:5000/synapses/test"
-    #     result = requests.post(url=url)
-    #
-    #     expected_content = {
-    #         "synapses": {
-    #             "name": "test",
-    #             "neurons": [
-    #                 {
-    #                     "say": {
-    #                         "message": [
-    #                             "test message"
-    #                         ]
-    #                     }
-    #                 }
-    #             ],
-    #             "signals": [
+    #         expected_content = {
+    #             "synapses": [
     #                 {
-    #                     "order": "test_order"
+    #                     "name": "test2",
+    #                     "neurons": [
+    #                         {
+    #                             "name": "say",
+    #                             "parameters": {
+    #                                 "message": [
+    #                                     "test message"
+    #                                 ]
+    #                             }
+    #                         }
+    #                     ],
+    #                     "signals": [
+    #                         {
+    #                             "order": "bonjour"
+    #                         }
+    #                     ]
     #                 }
     #             ]
     #         }
-    #     }
-    #
-    #     self.assertEqual(expected_content, json.loads(result.content))
-    #     self.assertEqual(result.status_code, 201)
-    #
-    # def test_post_synapse_not_found(self):
-    #     url = "http://127.0.0.1:5000/synapses/test-none"
-    #     result = requests.post(url=url)
-    #
-    #     expected_content = {
-    #         "error": {
-    #             "synapse name not found": "test-none"
-    #         }
-    #     }
-    #
-    #     self.assertEqual(expected_content, json.loads(result.content))
-    #     self.assertEqual(result.status_code, 404)
-    #
-    # def test_run_synapse_with_order(self):
-    #     url = "http://127.0.0.1:5000/order/"
-    #     headers = {"Content-Type": "application/json"}
-    #     data = {"order": "test_order"}
-    #     result = requests.post(url=url, headers=headers, json=data)
-    #
-    #     expected_content = {
-    #         "synapses": [
-    #             {
-    #                 "name": "test",
-    #                 "neurons": [
-    #                     {
-    #                         "name": "say",
-    #                         "parameters": "{'message': ['test message']}"
-    #                     }
-    #                 ],
-    #                 "signals": [
-    #                     {
-    #                         "order": "test_order"
-    #                     }
-    #                 ]
-    #             }
-    #         ]
-    #     }
-    #
-    #     self.assertEqual(expected_content, json.loads(result.content))
-    #     self.assertEqual(result.status_code, 201)
-    #
-    # def test_post_synapse_by_order_not_found(self):
-    #     url = "http://127.0.0.1:5000/order/"
-    #     data = {"order": "non existing order"}
-    #     headers = {"Content-Type": "application/json"}
-    #     result = requests.post(url=url, headers=headers, json=data)
-    #
-    #     expected_content = {'error': {'error': "The given order doesn't match any synapses"}}
     #
-    #     self.assertEqual(expected_content, json.loads(result.content))
-    #     self.assertEqual(result.status_code, 400)
+    #         self.assertEqual(json.dumps(expected_content), json.dumps(json.loads(result.get_data())))
+    #         self.assertEqual(result.status_code, 201)
 
 if __name__ == '__main__':
     unittest.main()

+ 1 - 1
install/files/python_requirements.txt

@@ -14,7 +14,7 @@ flask_cors==3.0.2
 requests==2.12.4
 httpretty==0.8.14
 mock==2.0.0
-Flask-Testing==0.6.1
+Flask-Testing>=0.6.1
 apscheduler==3.3.0
 GitPython==2.1.1
 packaging>=16.8

+ 8 - 0
kalliope/core/RestAPI/FlaskAPI.py

@@ -18,6 +18,7 @@ from flask_cors import CORS, cross_origin
 from kalliope.core import OrderAnalyser
 from kalliope.core.RestAPI.utils import requires_auth
 from kalliope.core.SynapseLauncher import SynapseLauncher
+from kalliope._version import version_str
 
 logging.basicConfig()
 logger = logging.getLogger("kalliope")
@@ -63,6 +64,7 @@ class FlaskAPI(threading.Thread):
             cors = CORS(app, resources={r"/*": {"origins": allowed_cors_origin}}, supports_credentials=True)
 
         # Add routing rules
+        self.app.add_url_rule('/', view_func=self.get_main_page, methods=['GET'])
         self.app.add_url_rule('/synapses', view_func=self.get_synapses, methods=['GET'])
         self.app.add_url_rule('/synapses/<synapse_name>', view_func=self.get_synapse, methods=['GET'])
         self.app.add_url_rule('/synapses/start/id/<synapse_name>', view_func=self.run_synapse_by_name, methods=['POST'])
@@ -73,6 +75,12 @@ class FlaskAPI(threading.Thread):
     def run(self):
         self.app.run(host='0.0.0.0', port="%s" % int(self.port), debug=True, threaded=True, use_reloader=False)
 
+    def get_main_page(self):
+        data = {
+            "Kalliope version": "%s" % version_str
+        }
+        return jsonify(data), 200
+
     @staticmethod
     def allowed_file(filename):
         return '.' in filename and \

+ 2 - 2
setup.py

@@ -73,11 +73,11 @@ setup(
         'requests==2.12.4',
         'httpretty==0.8.14',
         'mock==2.0.0',
-        'Flask-Testing==0.6.1',
+        'Flask-Testing>=0.6.1',
         'apscheduler==3.3.0',
         'GitPython==2.1.1',
         'packaging>=16.8',
-        'transitions>=0.4.3'
+        'transitions>=0.4.3',
     ],