Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove dependency of class collection #45

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 57 additions & 36 deletions hydra_python_core/doc_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
from urllib.parse import quote, urljoin


class HydraDoc():
class HydraDoc:
"""Class for an API Doc."""

def __init__(self, API: str, title: str, desc: str,
entrypoint: str, base_url: str) -> None:
entrypoint: str, base_url: str, doc_name: str) -> None:
"""Initialize the APIDoc."""
self.API = API
self.entrypoint_endpoint = entrypoint
Expand All @@ -21,6 +21,8 @@ def __init__(self, API: str, title: str, desc: str,
self.possible_status = list()
self.entrypoint = HydraEntryPoint(base_url, entrypoint)
self.desc = desc
self.doc_name = doc_name
doc_url = DocUrl(self.base_url, self.API, self.doc_name)

def add_supported_class(
self, class_: 'HydraClass') -> None:
Expand Down Expand Up @@ -101,14 +103,13 @@ def generate(self) -> Dict[str, Any]:
for key in self.collections]
doc = {
"@context": self.context.generate(),
"@id": "{}/vocab".format(urljoin(self.base_url, self.API)),
"@id": "{}/{}".format(urljoin(self.base_url, self.API), self.doc_name),
"@type": "ApiDocumentation",
"title": self.title,
"description": self.desc,
"entrypoint": urljoin(self.base_url, self.entrypoint_endpoint),
"supportedClass": [
x.generate() for x in parsed_classes +
self.other_classes + collections + [self.entrypoint]],
x.generate() for x in parsed_classes + self.other_classes + collections + [self.entrypoint]],
"possibleStatus": [status.generate() for status in self.possible_status]
}
return doc
Expand All @@ -118,10 +119,10 @@ class HydraClass():
"""Template for a new class."""

def __init__(
self, id_: str, title: str, desc: str, path: str = None,
self, title: str, desc: str, path: str = None,
endpoint: bool = False, sub_classof: None = None) -> None:
"""Initialize the Hydra_Class."""
self.id_ = id_ if "http" in id_ else "vocab:{}".format(id_)
self.id_ = "{}{}".format(DocUrl.doc_url, title)
self.title = title
self.desc = desc
self.path = path if path else title
Expand Down Expand Up @@ -263,14 +264,13 @@ class HydraCollection():

def __init__(
self,
collection_id: str = None,
collection_name: str = None,
collection_path: str = None,
collection_description: str = None,
manages: Union[Dict[str, Any], List] = None,
get: bool = True, post: bool = True) -> None:
get: bool = True, post: bool = True, put: bool = True, delete: bool = True) -> None:
"""Generate Collection for related resources."""
self.collection_id = collection_id
self.collection_id = "{}{}".format(DocUrl.doc_url, quote(collection_name, safe=''))
self.name = collection_name
self.collection_description = collection_description
self.path = collection_path if collection_path else self.name
Expand All @@ -282,21 +282,39 @@ def __init__(
self.manages = manages

if get:
get_op = HydraCollectionOp("{}/{}_retrieve".format(collection_id, self.name),
get_op = HydraCollectionOp("{}{}_retrieve".format(DocUrl.doc_url, self.name),
"http://schema.org/FindAction",
"GET", "Retrieves all the members of {}".format(self.name),
None, self.manages['object'], [], [], [])
self.supportedOperation.append(get_op)

if put:
put_op = HydraCollectionOp("{}{}_create".format(DocUrl.doc_url, self.name), "http://schema.org/AddAction",
"PUT", "Create new member in {}".format(self.name),
self.manages['object'], self.manages['object'], [], [],
[HydraStatus(code=201, desc="A new member in {} created".format(self.name))]
)
self.supportedOperation.append(put_op)
if post:
post_op = HydraCollectionOp("{}/{}_create".format(collection_id, self.name),
"http://schema.org/AddAction",
"PUT", "Create new member in {}".format(self.name),
post_op = HydraCollectionOp("{}{}_update".format(DocUrl.doc_url, self.name),
"http://schema.org/UpdateAction",
"POST", "Update member of {} ".format(self.name),
self.manages['object'], self.manages['object'], [], [],
[HydraStatus(code=201, desc="A new member in {} created".format(self.name))]
[HydraStatus(code=200, desc="If the entity was updated"
"from {}.".format(self.name))]
)
self.supportedOperation.append(post_op)

if delete:
delete_op = HydraCollectionOp("{}{}_delete".format(DocUrl.doc_url, self.name),
"http://schema.org/DeleteAction",
"DELETE", "Delete member of {} ".format(self.name),
self.manages['object'], self.manages['object'], [], [],
[HydraStatus(code=200, desc="If entity was deleted"
"successfully from {}.".format(self.name))]
)
self.supportedOperation.append(delete_op)

def generate(self) -> Dict[str, Any]:
"""Get as a python dict."""

Expand Down Expand Up @@ -364,8 +382,8 @@ def __init__(self, base_url: str, entrypoint: str) -> None:
self.entrypoint = HydraClass(
"EntryPoint", "EntryPoint", "The main entry point or homepage of the API.")
self.entrypoint.add_supported_op(EntryPointOp(
"_:entry_point", "GET", "The APIs main entry point.", None, None,
type_="vocab:EntryPoint"))
"{}entry_point".format(base_url), "GET", "The APIs main entry point.", None, None,
type_="{}EntryPoint".format(DocUrl.doc_url)))
self.context = Context(
"{}{}".format(
base_url,
Expand Down Expand Up @@ -420,7 +438,7 @@ def get(self) -> Dict[str, str]:
object_['collections'] = []
collection_returned = item.generate()
collection_id = uri.replace(
"vocab:EntryPoint", "/{}".format(self.api))
"{}EntryPoint".format(DocUrl.doc_url), "/{}".format(self.api))
collection_to_append = {
"@id": collection_id,
'title': collection_returned['hydra:title'],
Expand All @@ -431,7 +449,7 @@ def get(self) -> Dict[str, str]:
object_['collections'].append(collection_to_append)
else:
object_[item.name] = uri.replace(
"vocab:EntryPoint", "/{}".format(self.api))
"{}EntryPoint".format(DocUrl.doc_url), "/{}".format(self.api))

return object_

Expand All @@ -444,10 +462,9 @@ def __init__(self, collection: HydraCollection) -> None:
self.name = collection.name
self.supportedOperation = collection.supportedOperation
if collection.path:
self.id_ = "vocab:EntryPoint/{}".format(
quote(collection.path, safe=''))
self.id_ = "{}EntryPoint/{}".format(DocUrl.doc_url, quote(collection.path, safe=''))
else:
self.id_ = "vocab:EntryPoint/{}".format(quote(self.name, safe=''))
self.id_ = "{}EntryPoint/{}".format(DocUrl.doc_url, quote(self.name, safe=''))

def generate(self) -> Dict[str, Any]:
"""Get as a python dict."""
Expand All @@ -457,8 +474,8 @@ def generate(self) -> Dict[str, Any]:
"@type": "hydra:Link",
"label": self.name,
"description": "The {} collection".format(self.name, ),
"domain": "vocab:EntryPoint",
"range": "vocab:{}".format(self.name, ),
"domain": "{}EntryPoint".format(DocUrl.doc_url),
"range": "{}:{}".format(DocUrl.doc_url, self.name),
"supportedOperation": [],
},
"hydra:title": self.name.lower(),
Expand Down Expand Up @@ -487,9 +504,9 @@ def __init__(self, class_: HydraClass) -> None:
self.desc = class_.desc
self.supportedOperation = class_.supportedOperation
if class_.path:
self.id_ = "vocab:EntryPoint/{}".format(class_.path)
self.id_ = "{}EntryPoint/{}".format(DocUrl.doc_url, class_.path)
else:
self.id_ = "vocab:EntryPoint/{}".format(self.name)
self.id_ = "{}EntryPoint/{}".format(DocUrl.doc_url, self.name)

def generate(self) -> Dict[str, Any]:
"""Get as Python Dict."""
Expand All @@ -499,8 +516,8 @@ def generate(self) -> Dict[str, Any]:
"@type": "hydra:Link",
"label": self.name,
"description": self.desc,
"domain": "vocab:EntryPoint",
"range": "vocab:{}".format(self.name),
"domain": "{}EntryPoint".format(DocUrl.doc_url),
"range": "{}{}".format(DocUrl.doc_url, self.name),
"supportedOperation": []
},
"hydra:title": self.name.lower(),
Expand Down Expand Up @@ -663,12 +680,11 @@ def generate(self) -> Dict[str, Any]:

class HydraLink():
"""Template for a link property."""

def __init__(
self, id_: str, title: str = "",
desc: str = "", domain: str = "", range_: str = "") -> None:
"""Initialize the Hydra_Link."""
self.id_ = id_ if "http" in id_ else "vocab:{}".format(id_)
self.id_ = id_ if "http" in id_ else "{}{}".format(DocUrl.doc_url, id_)
self.range = range_
self.title = title
self.desc = desc
Expand Down Expand Up @@ -714,21 +730,20 @@ def __init__(self,
# NOTE: adders is a dictionary containing additional
# context elements to the base Hydra context
if class_ is not None:
self.context = {"vocab": "{}/vocab#".format(address), "hydra": "http://www.w3.org/ns/hydra/core#",
self.context = {"hydra": "http://www.w3.org/ns/hydra/core#",
"members": "http://www.w3.org/ns/hydra/core#member", "object": "http://schema.org/object",
class_.title: class_.id_} # type: Dict[str, Any]
for prop in class_.supportedProperty:
self.context[prop.title] = prop.prop

elif collection is not None:
self.context = {"vocab": "{}/vocab#".format(address), "hydra": "http://www.w3.org/ns/hydra/core#",
self.context = {"hydra": "http://www.w3.org/ns/hydra/core#",
"members": "http://www.w3.org/ns/hydra/core#member",
collection.name: collection.collection_id}

elif entrypoint is not None:
self.context = {
"EntryPoint": "vocab:EntryPoint",
"vocab": "{}/vocab#".format(address)
"EntryPoint": "{}:EntryPoint".format(DocUrl.doc_url),
}

else:
Expand All @@ -743,7 +758,6 @@ def __init__(self,
"supportedOperation": "hydra:supportedOperation",
"label": "rdfs:label",
"rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
"vocab": "{}/vocab#".format(address),
"domain": {
"@type": "@id",
"@id": "rdfs:domain"
Expand Down Expand Up @@ -799,7 +813,7 @@ def createContext(self, object_: Dict[str, Any]) -> None:
for prop in object_.supportedProperty:
self.add(prop.title, self.prop)
if isinstance(object_, HydraCollection):
self.add(object_.name, "vocab:{}".format(object_.name))
self.add(object_.name, "{}:{}".format(DocUrl.doc_url, object_.name))
self.add(object_.class_.title, object_.class_.id)

def generate(self) -> Dict[str, Any]:
Expand All @@ -809,3 +823,10 @@ def generate(self) -> Dict[str, Any]:
def add(self, key: str, value: Union[Dict[str, str], str]) -> None:
"""Add entry to context."""
self.context[key] = value


class DocUrl:
doc_url = ''

def __init__(self, base_url: str, api_name: str, doc_name: str) -> None:
DocUrl.doc_url = "{}/{}#".format(urljoin(base_url, api_name), doc_name)
46 changes: 21 additions & 25 deletions samples/doc_writer_sample.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,62 +16,58 @@
"Title for the API Documentation",
"Description for the API Documentation",
API_NAME,
BASE_URL)
BASE_URL,
"vocab")


# Creating classes for the API
class_uri = urljoin(BASE_URL, API_NAME + "/dummyClass") # URI of class for the HydraClass
class_title = "dummyClass" # Title of the Class
class_description = "A dummyClass for demo" # Description of the class
class_ = HydraClass(class_uri, class_title, class_description, endpoint=False)
class_ = HydraClass(class_title, class_description, endpoint=False)


# Class with single instance
class_2_uri = urljoin(BASE_URL, API_NAME + "/singleClass")

class_2_title = "singleClass"
class_2_description = "A non collection class"
class_2 = HydraClass(class_2_uri, class_2_title,
class_2 = HydraClass(class_2_title,
class_2_description, endpoint=True)

# Another class with single instance, will be used as nested class

class_1_uri = urljoin(BASE_URL, API_NAME + "/anotherSingleClass")
class_1_title = "anotherSingleClass"
class_1_description = "An another non collection class"
class_1 = HydraClass(class_1_uri, class_1_title,
class_1 = HydraClass(class_1_title,
class_1_description, endpoint=True)

# Class not having any methods except put and get
class_3_uri = urljoin(BASE_URL, API_NAME + "/extraClass")
class_3_title = "extraClass"
class_3_description = "Class without any explicit methods"
class_3 = HydraClass(class_3_uri, class_3_title,
class_3 = HydraClass(class_3_title,
class_3_description, endpoint=False)

collection_uri = urljoin(BASE_URL, API_NAME + "/collection_1")
collection_name = "Extraclasses"
collection_title = "ExtraClass collection"
collection_description = "This collection comprises of instances of ExtraClass"
# add explicit statements about members of the collection
# Following manages block means every member of this collection is of type class_3
collection_managed_by = {
"property": "rdf:type",
"object": class_3_uri,
"object": class_3.id_,
}
collection_1 = HydraCollection(collection_id=collection_uri, collection_name=collection_name,
collection_1 = HydraCollection(collection_name=collection_name,
collection_description=collection_description, manages=collection_managed_by, get=True,
post=True, collection_path="EcTest")

collection2_uri = urljoin(BASE_URL, API_NAME + "/collection_2")
collection2_title = "dummyClass collection"
collection2_name = "dummyclasses"
collection2_description = "This collection comprises of instances of dummyClass"
collection2_managed_by = {
"property": "rdf:type",
"object": class_uri,
"object": class_.id_,
}

collection_2 = HydraCollection(collection_id=collection2_uri, collection_name=collection2_name,
collection_2 = HydraCollection(collection_name=collection2_name,
collection_description=collection2_description, manages=collection2_managed_by, get=True,
post=True, collection_path="DcTest")

Expand Down Expand Up @@ -104,7 +100,7 @@
op_name = "UpdateClass" # The name of the operation
op_method = "POST" # The method of the Operation [GET, POST, PUT, DELETE]
# URI of the object that is expected for the operation
op_expects = class_uri
op_expects = class_.id_
op_returns = None # URI of the object that is returned by the operation
op_returns_header = ["Content-Type", "Content-Length"]
op_expects_header = []
Expand All @@ -123,38 +119,38 @@
op2_status = [HydraStatus(code=200, desc="dummyClass deleted.")]
op2 = HydraClassOp("DeleteClass", "DELETE", None, None, [], [], op2_status)
op3_status = [HydraStatus(code=201, desc="dummyClass successfully added.")]
op3 = HydraClassOp("AddClass", "PUT", class_uri, None, [], [], op3_status)
op3 = HydraClassOp("AddClass", "PUT", class_.id_, None, [], [], op3_status)
op4_status = [HydraStatus(code=200, desc="dummyClass returned.")]
op4 = HydraClassOp("GetClass", "GET", None, class_uri, [], [], op4_status)
op4 = HydraClassOp("GetClass", "GET", None, class_.id_, [], [], op4_status)

# Operations for non collection class
class_2_op1_status = [HydraStatus(code=200, desc="singleClass changed.")]
class_2_op1 = HydraClassOp("UpdateClass", "POST",
class_2_uri, None, [], [], class_2_op1_status)
class_2.id_, None, [], [], class_2_op1_status)
class_2_op2_status = [HydraStatus(code=200, desc="singleClass deleted.")]
class_2_op2 = HydraClassOp("DeleteClass", "DELETE",
None, None, [], [], class_2_op2_status)
class_2_op3_status = [HydraStatus(code=201, desc="singleClass successfully added.")]
class_2_op3 = HydraClassOp(
"AddClass", "PUT", class_2_uri, None, [], [], class_2_op3_status)
"AddClass", "PUT", class_2.id_, None, [], [], class_2_op3_status)
class_2_op4_status = [HydraStatus(code=200, desc="singleClass returned.")]
class_2_op4 = HydraClassOp("GetClass", "GET", None,
class_2_uri, [], [], class_2_op4_status)
class_2.id_, [], [], class_2_op4_status)

class_1_op1_status = [HydraStatus(code=200, desc="anotherSingleClass returned.")]
class_1_op1 = HydraClassOp("GetClass", "GET", None,
class_1_uri, [], [], class_1_op1_status)
class_1.id_, [], [], class_1_op1_status)
# Add the properties to the classes
class_.add_supported_prop(dummyProp1)
class_.add_supported_prop(dummyProp2)
class_2.add_supported_prop(dummyProp1)
class_2.add_supported_prop(dummyProp2)
dummy_prop_link = HydraLink("singleClass/dummyProp", "dummyProp", domain=class_2_uri,
range_=class_uri)
dummy_prop_link = HydraLink("singleClass/dummyProp", "dummyProp", domain=class_2.id_,
range_=class_.id_)
class_2.add_supported_prop(HydraClassProp(
dummy_prop_link, "dummyProp", required=False, read=False, write=True))
class_2.add_supported_prop(HydraClassProp(
class_1_uri, "singleClassProp", required=False, read=False, write=True))
class_1.id_, "singleClassProp", required=False, read=False, write=True))
class_1.add_supported_prop(dummyProp1)
# Add the operations to the classes
class_.add_supported_op(op1)
Expand Down
Loading