From 5ff3042a8142bc76255829743234751d23ab3bdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Ara=C3=BAjo?= Date: Wed, 27 Feb 2019 16:14:22 -0300 Subject: [PATCH 1/5] Solving requeriments problems When I tried to build and run the docker image, I got the following error: ImportError: No module named 'hydra_python_core' To solve it, is just change the installation of the hydra_python_core and the hydra_openapi_parser. --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index ea2122fa..bb96a27c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -23,5 +23,5 @@ SQLAlchemy==1.2.10 thespian==3.9.2 Werkzeug==0.14.1 PyYAML==4.2b1 --e git+https://github.com/HTTP-APIs/hydra-python-core@v0.1#egg=hydra_python_core --e git+https://github.com/HTTP-APIs/hydra-openapi-parser@0.1.1#egg=hydra_openapi_parser +git+https://github.com/HTTP-APIs/hydra-python-core@v0.1#egg=hydra_python_core +git+https://github.com/HTTP-APIs/hydra-openapi-parser@0.1.1#egg=hydra_openapi_parser From a3b0e8083c9717cafd71aa62cddb9fc7c0f9bce5 Mon Sep 17 00:00:00 2001 From: Devdutt Shenoi Date: Fri, 8 Mar 2019 20:48:12 +0530 Subject: [PATCH 2/5] Refactor deeply nested if blocks (#341) * Make refactoring changes within resuorces.py * Correct str.format() place holder variables * Apply refactoring to most files --- .gitignore | 3 + cli.py | 2 +- hydrus/auth.py | 10 +- hydrus/data/crud.py | 35 ++++--- hydrus/data/doc_parse.py | 8 +- hydrus/resources.py | 212 +++++++++++++++++++-------------------- hydrus/tests/test_app.py | 12 +-- 7 files changed, 140 insertions(+), 142 deletions(-) diff --git a/.gitignore b/.gitignore index cc30c56f..37a88dd2 100644 --- a/.gitignore +++ b/.gitignore @@ -109,3 +109,6 @@ ENV/ # Keys *.pem + +# Pipenv generated +Pipfile* \ No newline at end of file diff --git a/cli.py b/cli.py index 988d0cc0..fca4fb48 100644 --- a/cli.py +++ b/cli.py @@ -9,7 +9,7 @@ from hydrus.data.db_models import Base from hydrus.data.user import add_user from gevent.pywsgi import WSGIServer -from hydrus.parser.openapi_parser import parse +from hydrus_openapi_parser.openapi_parser import parse from hydrus.samples.hydra_doc_sample import doc as api_document from importlib.machinery import SourceFileLoader from typing import Tuple diff --git a/hydrus/auth.py b/hydrus/auth.py index 4c215b88..915fa7f5 100644 --- a/hydrus/auth.py +++ b/hydrus/auth.py @@ -44,10 +44,9 @@ def verify_user() -> Union[Response, None]: auth = check_authorization(request, get_session()) if auth is False: return failed_authentication(True) - else: - if get_token(): - token = add_token(request, get_session()) - return token_response(token) + elif get_token(): + token = add_token(request, get_session()) + return token_response(token) except Exception as e: status_code, message = e.get_HTTP() # type: ignore return set_response_headers(jsonify(message), status_code=status_code) @@ -70,4 +69,5 @@ def check_authentication_response() -> Union[Response, None]: return failed_authentication(False) else: return verify_user() - return None + else: + return None diff --git a/hydrus/data/crud.py b/hydrus/data/crud.py index bf24e7e2..7b4da539 100644 --- a/hydrus/data/crud.py +++ b/hydrus/data/crud.py @@ -172,11 +172,10 @@ def insert(object_: Dict[str, Any], session: scoped_session, RDFClass.name == object_["@type"]).one() except NoResultFound: raise ClassNotFound(type_=object_["@type"]) - if id_ is not None: - if session.query(exists().where(Instance.id == id_)).scalar(): - raise InstanceExists(type_=rdf_class.name, id_=id_) - else: - instance = Instance(id=id_, type_=rdf_class.id) + if id_ is not None and session.query(exists().where(Instance.id == id_)).scalar(): + raise InstanceExists(type_=rdf_class.name, id_=id_) + elif id_ is not None: + instance = Instance(id=id_, type_=rdf_class.id) else: instance = Instance(type_=rdf_class.id) session.add(instance) @@ -211,20 +210,20 @@ def insert(object_: Dict[str, Any], session: scoped_session, raise NotInstanceProperty(type_=prop_name) # For insertion in IAC + elif session.query(exists().where(RDFClass.name == str(object_[prop_name]))).scalar() \ + and property_.type_ == "PROPERTY" or property_.type_ == "ABSTRACT": + property_.type_ = "ABSTRACT" + session.add(property_) + class_ = session.query(RDFClass).filter( + RDFClass.name == object_[prop_name]).one() + triple = GraphIAC( + subject=instance.id, + predicate=property_.id, + object_=class_.id) + session.add(triple) elif session.query(exists().where(RDFClass.name == str(object_[prop_name]))).scalar(): - if property_.type_ == "PROPERTY" or property_.type_ == "ABSTRACT": - property_.type_ = "ABSTRACT" - session.add(property_) - class_ = session.query(RDFClass).filter( - RDFClass.name == object_[prop_name]).one() - triple = GraphIAC( - subject=instance.id, - predicate=property_.id, - object_=class_.id) - session.add(triple) - else: - session.close() - raise NotAbstractProperty(type_=prop_name) + session.close() + raise NotAbstractProperty(type_=prop_name) # For insertion in IIT else: diff --git a/hydrus/data/doc_parse.py b/hydrus/data/doc_parse.py index bbfb383f..a313af4f 100644 --- a/hydrus/data/doc_parse.py +++ b/hydrus/data/doc_parse.py @@ -51,10 +51,10 @@ def insert_classes(classes: List[Dict[str, Any]], not session.query(exists().where(RDFClass.name == class_["label"] .strip('.'))).scalar()] - class_list = class_list + [RDFClass(name=class_["title"].strip('.')) for class_ in classes - if "title" in class_ and - not session.query(exists().where(RDFClass.name == class_["title"] - .strip('.'))).scalar()] + class_list.extend([RDFClass(name=class_["title"].strip('.')) for class_ in classes + if "title" in class_ and + not session.query(exists().where(RDFClass.name == class_["title"] + .strip('.'))).scalar()]) # print(class_list) session.add_all(class_list) session.commit() diff --git a/hydrus/resources.py b/hydrus/resources.py index ae4dbbe0..e1b44414 100644 --- a/hydrus/resources.py +++ b/hydrus/resources.py @@ -130,8 +130,7 @@ def post(self, id_: str, path: str) -> Response: object_ = json.loads(request.data.decode('utf-8')) obj_type = getType(class_type, "POST") # Load new object and type - if validObject(object_): - if object_["@type"] == obj_type: + if validObject(object_) and object_["@type"] == obj_type: try: # Update the right ID if the object is valid and matches # type of Item @@ -150,10 +149,10 @@ def post(self, id_: str, path: str) -> Response: except (ClassNotFound, InstanceNotFound, InstanceExists, PropertyNotFound) as e: status_code, message = e.get_HTTP() return set_response_headers(jsonify(message), status_code=status_code) - - return set_response_headers(jsonify({400: "Data is not valid"}), status_code=400) - - abort(405) + else: + return set_response_headers(jsonify({400: "Data is not valid"}), status_code=400) + else: + abort(405) def put(self, id_: str, path: str) -> Response: """Add new object_ optional parameter using HTTP PUT. @@ -172,8 +171,7 @@ def put(self, id_: str, path: str) -> Response: object_ = json.loads(request.data.decode('utf-8')) obj_type = getType(class_type, "PUT") # Load new object and type - if validObject(object_): - if object_["@type"] == obj_type: + if validObject(object_) and object_["@type"] == obj_type: try: # Add the object with given ID object_id = crud.insert(object_=object_, id_=id_, session=get_session()) @@ -186,10 +184,10 @@ def put(self, id_: str, path: str) -> Response: except (ClassNotFound, InstanceExists, PropertyNotFound) as e: status_code, message = e.get_HTTP() return set_response_headers(jsonify(message), status_code=status_code) - - return set_response_headers(jsonify({400: "Data is not valid"}), status_code=400) - - abort(405) + else: + return set_response_headers(jsonify({400: "Data is not valid"}), status_code=400) + else: + abort(405) def delete(self, id_: str, path: str) -> Response: """Delete object with id=id_ from database.""" @@ -205,8 +203,7 @@ def delete(self, id_: str, path: str) -> Response: try: # Delete the Item with ID == id_ crud.delete(id_, class_type, session=get_session()) - response = { - "message": "Object with ID {} successfully deleted".format(id_)} + response = {"message": "Object with ID {} successfully deleted".format(id_)} return set_response_headers(jsonify(response)) except (ClassNotFound, InstanceNotFound) as e: @@ -227,38 +224,38 @@ def get(self, path: str) -> Response: if isinstance(auth_response, Response): return auth_response endpoint_ = checkEndpoint("GET", path) - if endpoint_['method']: + if not endpoint_['method']: + # If endpoint and Get method not supported in the API + abort(endpoint_['status']) + elif path in get_doc().collections: # If endpoint and GET method is supported in the API - if path in get_doc().collections: - # If collection name in document's collections - collection = get_doc().collections[path]["collection"] - try: - # Get collection details from the database - response = crud.get_collection( - get_api_name(), collection.class_.title, session=get_session(), path=path) - return set_response_headers(jsonify(hydrafy(response, path=path))) + # and collection name in document's collections + collection = get_doc().collections[path]["collection"] + try: + # Get collection details from the database + response = crud.get_collection( + get_api_name(), collection.class_.title, session=get_session(), path=path) + return set_response_headers(jsonify(hydrafy(response, path=path))) - except ClassNotFound as e: - status_code, message = e.get_HTTP() - return set_response_headers(jsonify(message), status_code=status_code) + except ClassNotFound as e: + status_code, message = e.get_HTTP() + return set_response_headers(jsonify(message), status_code=status_code) - # If class is supported - elif path in get_doc().parsed_classes and "{}Collection".format(path) not in get_doc( - ).collections: - try: - class_type = get_doc().parsed_classes[path]['class'].title - response = crud.get_single( - class_type, - api_name=get_api_name(), - session=get_session(), - path=path) - return set_response_headers(jsonify(hydrafy(response, path=path))) - - except (ClassNotFound, InstanceNotFound) as e: - status_code, message = e.get_HTTP() - return set_response_headers(jsonify(message), status_code=status_code) + # If endpoint and GET method is supported in the API and class is supported + elif path in get_doc().parsed_classes and "{}Collection".format( + path) not in get_doc().collections: + try: + class_type = get_doc().parsed_classes[path]['class'].title + response = crud.get_single( + class_type, + api_name=get_api_name(), + session=get_session(), + path=path) + return set_response_headers(jsonify(hydrafy(response, path=path))) - abort(endpoint_['status']) + except (ClassNotFound, InstanceNotFound) as e: + status_code, message = e.get_HTTP() + return set_response_headers(jsonify(message), status_code=status_code) def put(self, path: str) -> Response: """ @@ -283,45 +280,46 @@ def put(self, path: str) -> Response: # title of HydraClass object corresponding to collection obj_type = collection.class_.title - if validObject(object_): - # If Item in request's JSON is a valid object - # ie. @type is one of the keys in object_ - if object_["@type"] == obj_type: - # If the right Item type is being added to the - # collection - try: - # Insert object and return location in Header - object_id = crud.insert(object_=object_, session=get_session()) - headers_ = [{"Location": "{}{}/{}/{}".format( - get_hydrus_server_url(), get_api_name(), path, object_id)}] - response = { - "message": "Object with ID {} successfully added".format(object_id)} - return set_response_headers( - jsonify(response), headers=headers_, status_code=201) - except (ClassNotFound, InstanceExists, PropertyNotFound) as e: - status_code, message = e.get_HTTP() - return set_response_headers(jsonify(message), status_code=status_code) + if validObject(object_) and object_["@type"] == obj_type: + # If Item in request's JSON is a valid object ie. @type is a key in object_ + # and the right Item type is being added to the collection + try: + # Insert object and return location in Header + object_id = crud.insert(object_=object_, session=get_session()) + headers_ = [ + {"Location": "{}{}/{}/{}".format( + get_hydrus_server_url(), get_api_name(), path, object_id)}] + response = { + "message": "Object with ID {} successfully added".format(object_id)} + return set_response_headers( + jsonify(response), headers=headers_, status_code=201) + except (ClassNotFound, InstanceExists, PropertyNotFound) as e: + status_code, message = e.get_HTTP() + return set_response_headers(jsonify(message), status_code=status_code) - return set_response_headers(jsonify({400: "Data is not valid"}), status_code=400) + else: + return set_response_headers( + jsonify({400: "Data is not valid"}), status_code=400) elif path in get_doc().parsed_classes and "{}Collection".format(path) not in get_doc( ).collections: # If path is in parsed_classes but is not a collection obj_type = getType(path, "PUT") - if object_["@type"] == obj_type: - if validObject(object_): - try: - object_id = crud.insert(object_=object_, session=get_session()) - headers_ = [{"Location": "{}{}/{}/".format( - get_hydrus_server_url(), get_api_name(), path)}] - response = {"message": "Object successfully added"} - return set_response_headers( - jsonify(response), headers=headers_, status_code=201) - except (ClassNotFound, InstanceExists, PropertyNotFound) as e: - status_code, message = e.get_HTTP() - return set_response_headers(jsonify(message), status_code=status_code) + if object_["@type"] == obj_type and validObject(object_): + try: + object_id = crud.insert(object_=object_, session=get_session()) + headers_ = [{"Location": "{}{}/{}/".format( + get_hydrus_server_url(), get_api_name(), path)}] + response = {"message": "Object successfully added"} + return set_response_headers( + jsonify(response), headers=headers_, status_code=201) + except (ClassNotFound, InstanceExists, PropertyNotFound) as e: + status_code, message = e.get_HTTP() + return set_response_headers(jsonify(message), status_code=status_code) - return set_response_headers(jsonify({400: "Data is not valid"}), status_code=400) + else: + return set_response_headers( + jsonify({400: "Data is not valid"}), status_code=400) abort(endpoint_['status']) @@ -339,28 +337,29 @@ def post(self, path: str) -> Response: endpoint_ = checkEndpoint("POST", path) if endpoint_['method']: object_ = json.loads(request.data.decode('utf-8')) - if path in get_doc().parsed_classes and "{}Collection".format( - path) not in get_doc().collections: + if path in get_doc().parsed_classes and "{}Collection".format(path) not in get_doc( + ).collections: obj_type = getType(path, "POST") - if validObject(object_): - if object_["@type"] == obj_type: - try: - crud.update_single( - object_=object_, - session=get_session(), - api_name=get_api_name(), - path=path) - headers_ = [{"Location": "{}{}/{}/".format( - get_hydrus_server_url(), get_api_name(), path)}] - response = { - "message": "Object successfully updated"} - return set_response_headers(jsonify(response), headers=headers_) - except (ClassNotFound, InstanceNotFound, - InstanceExists, PropertyNotFound) as e: - status_code, message = e.get_HTTP() - return set_response_headers(jsonify(message), status_code=status_code) + if validObject(object_) and object_["@type"] == obj_type: + try: + crud.update_single( + object_=object_, + session=get_session(), + api_name=get_api_name(), + path=path) + headers_ = [{"Location": "{}{}/{}/".format( + get_hydrus_server_url(), get_api_name(), path)}] + response = {"message": "Object successfully updated"} + return set_response_headers(jsonify(response), headers=headers_) + except (ClassNotFound, InstanceNotFound, + InstanceExists, PropertyNotFound) as e: + status_code, message = e.get_HTTP() + return set_response_headers( + jsonify(message), status_code=status_code) - return set_response_headers(jsonify({400: "Data is not valid"}), status_code=400) + else: + return set_response_headers( + jsonify({400: "Data is not valid"}), status_code=400) abort(endpoint_['status']) @@ -376,19 +375,20 @@ def delete(self, path: str) -> Response: return auth_response endpoint_ = checkEndpoint("DELETE", path) - if endpoint_['method']: + if not endpoint_['method']: + abort(endpoint_['status']) + elif path in get_doc().parsed_classes and "{}Collection".format( + path) not in get_doc().collections: # No Delete Operation for collections - if path in get_doc().parsed_classes and "{}Collection".format( - path) not in get_doc().collections: - try: - class_type = get_doc().parsed_classes[path]['class'].title - crud.delete_single(class_type, session=get_session()) - response = {"message": "Object successfully deleted"} - return set_response_headers(jsonify(response)) - except (ClassNotFound, InstanceNotFound) as e: - status_code, message = e.get_HTTP() - return set_response_headers(jsonify(message), status_code=status_code) - abort(endpoint_['status']) + try: + class_type = get_doc().parsed_classes[path]['class'].title + crud.delete_single(class_type, session=get_session()) + response = {"message": "Object successfully deleted"} + return set_response_headers(jsonify(response)) + except (ClassNotFound, InstanceNotFound) as e: + status_code, message = e.get_HTTP() + return set_response_headers( + jsonify(message), status_code=status_code) class Items(Resource): diff --git a/hydrus/tests/test_app.py b/hydrus/tests/test_app.py index 6d3be01a..4f422986 100644 --- a/hydrus/tests/test_app.py +++ b/hydrus/tests/test_app.py @@ -257,9 +257,8 @@ def test_object_PUT_at_id(self): if "PUT" in class_methods: dummy_object = gen_dummy_object( collection.class_.title, self.doc) - put_response = self.client.put( - '{}/{}'.format( - endpoints[endpoint], uuid.uuid4()), data=json.dumps(dummy_object)) + put_response = self.client.put('{}/{}'.format( + endpoints[endpoint], uuid.uuid4()), data=json.dumps(dummy_object)) assert put_response.status_code == 201 def test_object_PUT_at_ids(self): @@ -273,9 +272,7 @@ def test_object_PUT_at_ids(self): collection = self.doc.collections[collection_name]["collection"] class_ = self.doc.parsed_classes[collection.class_.title]["class"] class_methods = [x.method for x in class_.supportedOperation] - data_ = { - "data": list() - } + data_ = {"data": list()} objects = list() ids = "" for index in range(3): @@ -286,8 +283,7 @@ def test_object_PUT_at_ids(self): if "PUT" in class_methods: put_response = self.client.put( '{}/add/{}'.format(endpoints[endpoint], ids), - data=json.dumps( - data_)) + data=json.dumps(data_)) assert put_response.status_code == 201 def test_endpointClass_PUT(self): From 14a0ed92998278afe8004df2dd5b2d08409001d1 Mon Sep 17 00:00:00 2001 From: Lorenzo Date: Sun, 17 Mar 2019 16:56:41 +0100 Subject: [PATCH 3/5] Add `pip` and `setuptools` update to Dockerfile (#362) * Amend setup.py * Add pip and setuptools update to Dockerfile --- Dockerfile | 3 ++- setup.py | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index e685ee20..485f0775 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,8 @@ FROM tiangolo/uwsgi-nginx-flask:flask-python3.5-index-upload MAINTAINER Akshay Dahiya COPY ./requirements.txt requirements.txt -RUN pip install -U pip && pip install -r requirements.txt && rm -rf * +RUN pip install -U pip && pip install --upgrade pip setuptools \ + && pip install -r requirements.txt && rm -rf * COPY . /app diff --git a/setup.py b/setup.py index 9e73ed73..ed2ac580 100644 --- a/setup.py +++ b/setup.py @@ -16,10 +16,10 @@ setup(name='hydrus', include_package_data=True, - version='0.1', - description='A space-based application for W3C HYDRA Draft', - author='W3C HYDRA development group', - author_email='public-hydra@w3.org', + version='0.2', + description='Hydra Ecosystem Flagship Server', + author='Hydra Ecosystem', + author_email='hydraecosystem@googlegroups.com', url='https://github.com/HTTP-APIs/hydrus', py_modules=['cli'], python_requires='>=3', From 305862e832d1970f0c7c4770505d43dc359b7712 Mon Sep 17 00:00:00 2001 From: Devdutt Shenoi Date: Sun, 17 Mar 2019 21:28:41 +0530 Subject: [PATCH 4/5] Correct import statement (#364) --- cli.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli.py b/cli.py index fca4fb48..e975acc7 100644 --- a/cli.py +++ b/cli.py @@ -1,7 +1,7 @@ from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker, scoped_session -from hydrus.app import app_factory +from hydrus.app_factory import app_factory from hydrus.utils import (set_session, set_doc, set_hydrus_server_url, set_token, set_api_name, set_authentication) from hydrus.data import doc_parse @@ -9,7 +9,7 @@ from hydrus.data.db_models import Base from hydrus.data.user import add_user from gevent.pywsgi import WSGIServer -from hydrus_openapi_parser.openapi_parser import parse +from hydra_openapi_parser.openapi_parser import parse from hydrus.samples.hydra_doc_sample import doc as api_document from importlib.machinery import SourceFileLoader from typing import Tuple From c6e11f42cf9a80ff90fc8d78a73679b9a7dc7c2a Mon Sep 17 00:00:00 2001 From: Lorenzo Date: Sun, 17 Mar 2019 16:15:07 +0000 Subject: [PATCH 5/5] Bump version to 0.2.1 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index ed2ac580..78a86673 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,7 @@ setup(name='hydrus', include_package_data=True, - version='0.2', + version='0.2.1', description='Hydra Ecosystem Flagship Server', author='Hydra Ecosystem', author_email='hydraecosystem@googlegroups.com',