Repository: HTTP-APIs/hydrus
Pull request #40 (2017-07-16 08:45:10)

Server bug fix
- Server bug fixed, now working with hydra console
- Tests updated
#will finish drone once we finalize the design.


Patch:
diff --git a/docker-compose.yml b/docker-compose.yml
index 84603f1..0f46f22 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -5,8 +5,8 @@ hydrus:
     - "8080:80"
   environment:
     # - HYDRUS_SERVER_URL=http://localhost/ ## For linux local
-    # - HYDRUS_SERVER_URL=http://192.168.99.100:8080/ ## For windows 10
-    - HYDRUS_SERVER_URL=http://54.169.232.177:8080/
+    - HYDRUS_SERVER_URL=http://192.168.99.100:8080/ ## For windows 10
+    # - HYDRUS_SERVER_URL=http://54.169.232.177:8080/
   links:
     - postgres:postgres
 
diff --git a/hydrus/app.py b/hydrus/app.py
index de8dc7e..3b0cc54 100644
--- a/hydrus/app.py
+++ b/hydrus/app.py
@@ -5,6 +5,8 @@ import json
 from flask import Flask, jsonify, request
 from flask_restful import Api, Resource
 from hydrus.metadata.server_doc_gen import server_doc
+# from hydrus.metadata.drone_doc_gen import drone_doc
+
 from hydrus.data import crud
 from flask_cors import CORS
 
@@ -16,6 +18,7 @@ api = Api(app)
 SERVER_URL = os.environ.get("HYDRUS_SERVER_URL", "localhost/")
 API_NAME = "serverapi"
 API_DOC = server_doc(API_NAME, SERVER_URL)
+# API_DOC = drone_doc(API_NAME, SERVER_URL)
 
 
 def validObject(object_):
@@ -50,7 +53,7 @@ class Index(Resource):
         return set_response_headers(jsonify(API_DOC.entrypoint.get()))
 
 
-api.add_resource(Index, "/"+API_NAME, endpoint="api")
+api.add_resource(Index, "/"+API_NAME+"/", endpoint="api")
 
 
 class Item(Resource):
@@ -109,20 +112,29 @@ class ItemCollection(Resource):
         """Retrieve a collection of items from the database."""
         if type_ in API_DOC.collections:
             collection = API_DOC.collections[type_]["collection"]
-            response = crud.get_collection(collection.class_.title)
+            response = crud.get_collection(API_NAME, collection.class_.title)
             if "members" in response:
                 return set_response_headers(jsonify(hydrafy(response)))
             else:
                 status_code = int(list(response.keys())[0])
+                response = crud.get_collection(API_NAME, type_)
+
                 return set_response_headers(jsonify(response), status_code=status_code)
 
     def post(self, type_):
         """Add item to ItemCollection."""
         object_ = json.loads(request.data.decode('utf-8'))
         # print(object_)
+        # Fix @type from hydra console
+        if type_ in API_DOC.collections:
+            collection = API_DOC.collections[type_]["collection"]
+            type_ = collection.class_.title
+            object_["@type"] = type_
+        # print(object_)
 
         if validObject(object_):
             response = crud.insert(object_=object_)
+            # print(response)
             object_id = response[list(response.keys())[0]].split(" ")[3]
             headers_ = [{"Location": SERVER_URL+"api/"+type_+"/"+object_id}]
             status_code = int(list(response.keys())[0])
@@ -172,13 +184,14 @@ class Vocab(Resource):
 
 api.add_resource(Vocab, "/"+API_NAME+"/vocab", endpoint="vocab")
 
-
 class Entrypoint(Resource):
     """Hydra EntryPoint."""
 
     def get(self):
         """Return application main Entrypoint."""
-        return set_response_headers(jsonify(API_DOC.entrypoint.context.generate()))
+        response = {"@context": API_DOC.entrypoint.context.generate()}
+        return set_response_headers(jsonify(response))
+
 
 
 api.add_resource(Entrypoint, "/"+API_NAME+"/contexts/EntryPoint.jsonld",
diff --git a/hydrus/data/crud.py b/hydrus/data/crud.py
index af028f7..bcacc34 100644
--- a/hydrus/data/crud.py
+++ b/hydrus/data/crud.py
@@ -96,7 +96,7 @@ def insert(object_, id_=None, session=session):
     session.commit()
 
     for prop_name in object_:
-        if prop_name != "@type":
+        if prop_name not in ["@type", "@context"]:
             # NOTE: An instance would have to be a JSON object, not string. Otherwise we may have an instance named 23 which will be added
             #  everytime the number is used. Use instance = {"@id": 2 }, where 2 is the ID of the instance.
             try:
@@ -254,10 +254,10 @@ def update(id_, type_, object_, session=session):
         return delete_status
 
 
-def get_collection(type_, session=session):
+def get_collection(API_NAME, type_, session=session):
     """Retrieve a type of collection from the database."""
     collection_template = {
-        "@id": "/api/" + type_ + "/",
+        "@id": "/"+API_NAME+"/"+ type_ + "Collection/",
         "@context": None,
         "@type": type_ + "Collection",
         "members": []
diff --git a/hydrus/data/doc_parse.py b/hydrus/data/doc_parse.py
index 693cf94..f976fac 100644
--- a/hydrus/data/doc_parse.py
+++ b/hydrus/data/doc_parse.py
@@ -65,3 +65,4 @@ if __name__ == "__main__":
 
     # Add all the properties
     insert_properties(properties, session)
+    print("Properties inserted successfully")
diff --git a/hydrus/data/insert_classes.py b/hydrus/data/insert_classes.py
index 5392768..2fc29f1 100644
--- a/hydrus/data/insert_classes.py
+++ b/hydrus/data/insert_classes.py
@@ -1,9 +1,9 @@
 """Definition of all Classes in the SubSystem and Spacecraft vocabulary."""
 
-import db_models as models
+import hydrus.data.db_models as models
 from sqlalchemy.orm import sessionmaker
-from hydrus.metadata.spacecraft_vocab_jsonld import spacecraft_data
-from hydrus.metadata.subsystem_vocab_jsonld import subsystem_data
+from hydrus.metadata.subsystem.spacecraft_vocab_jsonld import spacecraft_data
+from hydrus.metadata.subsystem.subsystem_vocab_jsonld import subsystem_data
 # from keymap import classes_keymap as keymap
 
 
diff --git a/hydrus/hydraspec/doc_writer.py b/hydrus/hydraspec/doc_writer.py
index cbaf805..ee9c0e1 100644
--- a/hydrus/hydraspec/doc_writer.py
+++ b/hydrus/hydraspec/doc_writer.py
@@ -68,9 +68,7 @@ class HydraDoc():
             "@context": self.context.generate(),
             "@id": self.base_url + self.API + "/vocab",
             "@type": "ApiDocumentation",
-            "title": self.title,
             "description": self.desc,
-            "entrypoint": '/'+self.entrypoint.api,
             "supportedClass": [x.generate() for x in parsed_classes + self.other_classes + collections + [self.entrypoint]],
             "possibleStatus": [x.generate() for x in self.status]
         }
@@ -133,8 +131,8 @@ class HydraClassProp():
           "title": self.title,
           "property": self.prop,
           "required": self.required,
-          "readable": self.read,
-          "writeable": self.write
+          "readonly": self.read,
+          "writeonly": self.write
         }
         return prop
 
@@ -184,9 +182,10 @@ class HydraCollection():
                     "@id": "_:%s_create" % (self.class_.title.lower()),
                     "@type": "http://schema.org/AddAction",
                     "method": "POST",
-                    "description": None,
-                    "expects": self.class_.title,
-                    "returns": self.class_.title,
+                    "label": "Create new %s entitity" % (self.class_.title),
+
+                    "expects": self.class_.id_,
+                    "returns": self.class_.id_,
                     "statusCodes": [
                         {
                             "code": 201,
@@ -251,8 +250,8 @@ class HydraEntryPoint():
     def get(self):
         """Create the EntryPoint object to be returnd for the get function."""
         object_ = {
-            "@context": self.url+"contexts/EntryPoint.jsonld",
-            "@id": '/'+self.api,
+            "@context": "/"+self.api+"/"+"contexts/EntryPoint.jsonld",
+            "@id": "/"+self.api,
             "@type": "EntryPoint",
         }
         for item in self.entrypoint.supportedProperty:
@@ -303,7 +302,7 @@ class Context():
                 "supportedClass": "hydra:supportedClass",
                 "supportedProperty": "hydra:supportedProperty",
                 "supportedOperation": "hydra:supportedOperation",
-                "code": "hydra:statusCode",
+                "statusCodes": "hydra:statusCodes",
                 "label": "rdfs:label",
                 "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
                 "vocab": address + "/vocab#",
@@ -377,20 +376,7 @@ class EntryPointCollection():
                 "label": self.name,
                 "description": "The %s collection" % (self.name,),
                 "domain": "vocab:EntryPoint",
-                "range": "vocab:%sCollection" % (self.name,),
-                "supportedOperation": [
-                    {
-                        "@id": "_:%s_collection_retrieve" % (self.name.lower(),),
-                        "@type": "hydra:Operation",
-                        "method": "GET",
-                        "label": "Retrieves all %s entities" % (self.name,),
-                        "description": None,
-                        "expects": None,
-                        "returns": "vocab:%sCollection" % (self.name,),
-                        "statusCodes": [
-                        ]
-                    }
-                ]
+                "range": "vocab:%s" % (self.name,),
             },
             "hydra:title": self.name.lower(),
             "hydra:description": "The %s collection" % (self.name,),
@@ -420,7 +406,7 @@ class EntryPointClass():
                 "label": self.name,
                 "description": self.desc,
                 "domain": "vocab:EntryPoint",
-                "range": "vocab:%sCollection" % (self.name),
+                "range": "vocab:%s" % (self.name),
                 "supportedOperation": []
             },
             "hydra:title": self.name.lower(),
diff --git a/hydrus/metadata/drone_doc_gen.py b/hydrus/metadata/drone_doc_gen.py
index 461a9b6..47a36d2 100644
--- a/hydrus/metadata/drone_doc_gen.py
+++ b/hydrus/metadata/drone_doc_gen.py
@@ -1,6 +1,6 @@
 """API Doc generator for the drone side API."""
 
-from hyrus.hydraspec.doc_writer import HydraDoc, HydraClass, HydraClassProp, HydraClassOp
+from hydrus.hydraspec.doc_writer import HydraDoc, HydraClass, HydraClassProp, HydraClassOp
 import json
 
 
@@ -14,7 +14,7 @@ def drone_doc(API, BASE_URL):
                        BASE_URL)
 
     # Status Class
-    status = HydraClass("Status", "Status", "Class for drone status objects")
+    status = HydraClass("http://hydrus.com/Status", "Status", "Class for drone status objects")
     # Properties
     status.add_supported_prop(HydraClassProp("http://auto.schema.org/speed", "Speed", True, False, False))
     status.add_supported_prop(HydraClassProp("http://schema.org/geo", "Position", True, False, False))
@@ -22,7 +22,7 @@ def drone_doc(API, BASE_URL):
     status.add_supported_prop(HydraClassProp("https://schema.org/status", "SensorStatus", True, False, False))
 
     # Drone Class
-    drone = HydraClass("Drone", "Drone", "Class for a drone", endpoint=True)
+    drone = HydraClass("http://hydrus.com/Drone", "Drone", "Class for a drone", endpoint=True)
     # Properties
     drone.add_supported_prop(HydraClassProp("vocab:Status", "DroneStatus", True, False, False))
     drone.add_supported_prop(HydraClassProp("http://schema.org/name", "name", True, False, False))
@@ -42,7 +42,7 @@ def drone_doc(API, BASE_URL):
                                         None,
                                         [{"statusCode": 200, "description": "Command issued"}]))
 
-    data = HydraClass("Data", "Data", "Class for a data entry", endpoint=True)
+    data = HydraClass("http://hydrus.com/Data", "Data", "Class for a data entry", endpoint=True)
     data.add_supported_prop(HydraClassProp("http://schema.org/QuantitativeValue", "Temperature", True, False, False))
     data.add_supported_prop(HydraClassProp("http://schema.org/identifier", "DroneID", True, False, False))
     data.add_supported_prop(HydraClassProp("http://schema.org/geo", "Position", True, False, False))
diff --git a/hydrus/metadata/server_doc_gen.py b/hydrus/metadata/server_doc_gen.py
index d9b73df..f056d19 100644
--- a/hydrus/metadata/server_doc_gen.py
+++ b/hydrus/metadata/server_doc_gen.py
@@ -14,7 +14,7 @@ def server_doc(API, BASE_URL):
                        BASE_URL)
 
     # Status Class
-    status = HydraClass("Status", "Status", "Class for drone status objects")
+    status = HydraClass("http://hydrus.com/Status", "Status", "Class for drone status objects")
     # Properties
     status.add_supported_prop(HydraClassProp("http://auto.schema.org/speed", "Speed", True, False, False))
     status.add_supported_prop(HydraClassProp("http://schema.org/geo", "Position", True, False, False))
@@ -22,7 +22,7 @@ def server_doc(API, BASE_URL):
     status.add_supported_prop(HydraClassProp("https://schema.org/status", "SensorStatus", True, False, False))
 
     # Drone Class
-    drone = HydraClass("Drone", "Drone", "Class for a drone")
+    drone = HydraClass("http://hydrus.com/Drone", "Drone", "Class for a drone")
     # Properties
     drone.add_supported_prop(HydraClassProp("vocab:Status", "DroneStatus", True, False, False))
     drone.add_supported_prop(HydraClassProp("http://schema.org/name", "name", True, False, False))
@@ -41,11 +41,11 @@ def server_doc(API, BASE_URL):
                                         "vocab:Drone",
                                         [{"statusCode": 200, "description": "Drone Returned"}]))
 
-    command = HydraClass("Command", "Command", "Class for drone commands")
+    command = HydraClass("http://hydrus.com/Command", "Command", "Class for drone commands")
     command.add_supported_prop(HydraClassProp("http://schema.org/UpdateAction", "Update", False, True, False))
     command.add_supported_prop(HydraClassProp("vocab:Status", "Status", False, False, False))
 
-    log = HydraClass("LogEntry", "LogEntry", "Class for a log entry")
+    log = HydraClass("http://hydrus.com/LogEntry", "LogEntry", "Class for a log entry")
     # Subject
     log.add_supported_prop(HydraClassProp("http://schema.org/identifier", "DroneID", True, True, False))
     # Predicate
@@ -63,7 +63,7 @@ def server_doc(API, BASE_URL):
                                       [{"statusCode": 404, "description": "Log entry not found"},
                                        {"statusCode": 200, "description": "Log entry returned"}]))
 
-    data = HydraClass("Data", "Data", "Class for a data entry")
+    data = HydraClass("http://hydrus.com/Data", "Data", "Class for a data entry")
     data.add_supported_prop(HydraClassProp("http://schema.org/QuantitativeValue", "Temperature", True, False, False))
     data.add_supported_prop(HydraClassProp("http://schema.org/identifier", "DroneID", True, False, False))
     data.add_supported_prop(HydraClassProp("http://schema.org/geo", "Position", True, False, False))
@@ -79,7 +79,7 @@ def server_doc(API, BASE_URL):
                                        None,
                                        [{"statusCode": 201, "description": "Data added"}]))
 
-    area = HydraClass("Area", "Area", "Class for Area of Interest of the server", endpoint=True)
+    area = HydraClass("http://hydrus.com/Area", "Area", "Class for Area of Interest of the server", endpoint=True)
     # Using two positions to have a bounding box
     area.add_supported_prop(HydraClassProp("http://schema.org/geo", "TopLeft", True, True, True))
     area.add_supported_prop(HydraClassProp("http://schema.org/geo", "BottomRight", True, True, True))
@@ -96,11 +96,11 @@ def server_doc(API, BASE_URL):
                                        [{"statusCode": 404, "description": "Area of not found"},
                                         {"statusCode": 200, "description": "Area of returned"}]))
 
-    message = HydraClass("Message", "Message", "Class for messages received by the GUI interface")
+    message = HydraClass("http://hydrus.com/Message", "Message", "Class for messages received by the GUI interface")
     message.add_supported_prop(HydraClassProp("http://schema.org/Text", "MessageString", True, True, False))
 
     api_doc.add_supported_class(drone, collection=True)
-    api_doc.add_supported_class(status, collection=False)
+    api_doc.add_supported_class(status, collection=True)
     api_doc.add_supported_class(data, collection=True)
     api_doc.add_supported_class(log, collection=True)
     api_doc.add_supported_class(area, collection=False)


Test patch:
diff --git a/hydrus/metadata/subsystem/__init__.py b/hydrus/metadata/subsystem/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/hydrus/tests/test_app.py b/hydrus/tests/test_app.py
index d6d8c14..16089c6 100644
--- a/hydrus/tests/test_app.py
+++ b/hydrus/tests/test_app.py
@@ -6,7 +6,7 @@ import tempfile
 import os
 import hydrus.app as views
 import json
-from hydrus.app import SERVER_URL, SEMANTIC_REF_URL, SEMANTIC_REF_NAME, PARSED_CLASSES
+from hydrus.app import API_NAME, SERVER_URL
 
 
 
@@ -26,12 +26,12 @@ class ViewsTestCase(unittest.TestCase):
 
     def test_Index(self):
         """Test for the index."""
-        response_get = self.app.get("/api")
+        response_get = self.app.get("/"+API_NAME)
         self.endpoints = json.loads(response_get.data.decode('utf-8'))
-        response_post = self.app.post("/api", data={})
-        response_delete = self.app.delete("/api")
+        response_post = self.app.post("/"+API_NAME, data={})
+        response_delete = self.app.delete("/"+API_NAME)
         assert "@context" in self.endpoints
-        assert self.endpoints["@id"] == "/api"
+        assert self.endpoints["@id"] == "/"+API_NAME
         assert self.endpoints["@type"] == "EntryPoint"
         assert response_get.status_code == 200
         assert response_post.status_code == 405
@@ -39,10 +39,10 @@ class ViewsTestCase(unittest.TestCase):
 
     def test_EntryPoint_context(self):
         """Test for the EntryPoint context."""
-        response_get = self.app.get("/api/contexts/EntryPoint.jsonld")
+        response_get = self.app.get("/"+API_NAME +"/contexts/EntryPoint.jsonld")
         response_get_data = json.loads(response_get.data.decode('utf-8'))
-        response_post = self.app.post("/api/contexts/EntryPoint.jsonld", data={})
-        response_delete = self.app.delete("/api/contexts/EntryPoint.jsonld")
+        response_post = self.app.post("/"+API_NAME +"/contexts/EntryPoint.jsonld", data={})
+        response_delete = self.app.delete("/"+API_NAME +"/contexts/EntryPoint.jsonld")
         assert response_get.status_code == 200
         assert "@context" in response_get_data
         assert response_post.status_code == 405
@@ -50,31 +50,32 @@ class ViewsTestCase(unittest.TestCase):
 
     def test_Vocab(self):
         """Test the vocab."""
-        response_get = self.app.get("/api/vocab#")
+        response_get = self.app.get("/"+API_NAME +"/vocab#")
         response_get_data = json.loads(response_get.data.decode('utf-8'))
         assert "@context" in response_get_data
+        # print(response_get_data["@id"], SERVER_URL+API_NAME +"/vocab#")
         assert response_get_data["@type"] == "ApiDocumentation"
-        assert response_get_data["@id"] == SERVER_URL+"api/vocab"
+        assert response_get_data["@id"] == SERVER_URL+API_NAME +"/vocab"
         assert response_get.status_code == 200
 
-        response_delete = self.app.delete("/api/vocab#")
+        response_delete = self.app.delete("/"+API_NAME +"/vocab#")
         assert response_delete.status_code == 405
 
-        response_put = self.app.put("/api/vocab#", data=json.dumps(dict(foo = 'bar')))
-        assert response_post.status_code == 405
+        response_put = self.app.put("/"+API_NAME +"/vocab#", data=json.dumps(dict(foo = 'bar')))
+        assert response_put.status_code == 405
 
     def test_Endpoints_Collections(self):
         """Test all endpoints to get the collection."""
-        index = self.app.get("/api")
+        index = self.app.get("/"+API_NAME)
         assert index.status_code == 200
         endpoints = json.loads(index.data.decode('utf-8'))
         for endpoint in endpoints:
-            if endpoint not in ["@id", "@context", "@type"]:
+            if endpoint not in ["@id", "@context", "@type"] and "Collection" in endpoints[endpoint]:
                 response_get = self.app.get(endpoints[endpoint])
                 response_post = self.app.post(endpoints[endpoint], data=json.dumps(dict(foo = 'bar')))
                 response_delete = self.app.delete(endpoints[endpoint])
                 assert response_get.status_code == 200
-                assert response_post.status_code == 400
+                assert response_post.status_code == 201
                 assert response_delete.status_code == 405
                 response_get_data = json.loads(response_get.data.decode('utf-8'))
                 assert "@context" in response_get_data
@@ -84,13 +85,13 @@ class ViewsTestCase(unittest.TestCase):
 
     def test_Endpoints_Contexts(self):
         """Test all endpoints contexts are generated properly."""
-        index = self.app.get("/api")
+        index = self.app.get("/"+API_NAME)
         assert index.status_code == 200
         endpoints = json.loads(index.data.decode('utf-8'))
         for endpoint in endpoints:
-            if endpoint not in ["@id", "@context", "@type"]:
+            if endpoint not in ["@id", "@context", "@type"] and "Collection" in endpoints[endpoint]:
                 response_get = self.app.get(endpoints[endpoint])
-                print(response_get.data.decode('utf-8'))
+                # print(response_get.data.decode('utf-8'))
                 context = json.loads(response_get.data.decode('utf-8'))["@context"]
                 response_context = self.app.get(context)
                 response_context_data = json.loads(response_context.data.decode('utf-8'))
diff --git a/hydrus/tests/test_crud.py b/hydrus/tests/test_crud.py
index b9eb66b..f4bb643 100644
--- a/hydrus/tests/test_crud.py
+++ b/hydrus/tests/test_crud.py
@@ -6,7 +6,6 @@ from sqlalchemy import create_engine
 from sqlalchemy.orm import sessionmaker
 import hydrus.data.crud as crud
 from hydrus.data.db_models import Base
-from hydrus.hydraspec.vocab_generator import gen_vocab
 from hydrus.data.insert_classes import gen_classes
 
 def object_1():