Skip to content

Instantly share code, notes, and snippets.

@pebbie
Created September 15, 2014 16:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pebbie/3a6b2b10c71ba796dca2 to your computer and use it in GitHub Desktop.
Save pebbie/3a6b2b10c71ba796dca2 to your computer and use it in GitHub Desktop.
an attempt to write a console for hydra
import sys
from rdflib import Graph, Namespace
from rdflib.namespace import RDF, RDFS
import requests
HYDRA = Namespace("http://www.w3.org/ns/hydra/core#")
class HydraOperation:
def __init__(self, subj, graph):
self.type = graph.value(subj, RDF.type)
self.subj = subj
self.method = graph.value(subj, HYDRA.method)
self.expects = graph.value(subj, HYDRA.expects)
self.returns = graph.value(subj, HYDRA.returns)
self.statusCodes = []
def dump(self):
print "Operation : ", self.subj
print "method : ", self.method
print "expects : ", self.expects
print "returns : ", self.returns
class HydraProperty:
def __init__(self, subj, graph):
self.type = graph.value(subj, RDF.type)
self.subj = subj
self.title = graph.value(subj, HYDRA.title)
self.description = graph.value(subj, HYDRA.description)
self.property = graph.value(subj, HYDRA.property)
self.required = graph.value(subj, HYDRA.required)
self.readonly = graph.value(subj, HYDRA.readonly)
self.writeonly = graph.value(subj, HYDRA.writeonly)
def dump(self):
if self.title is not None:
print "Property : ", self.title,
else:
print "Property : ", self.subj,
if self.required:
print " required",
if self.readonly:
print " readonly",
if self.writeonly:
print " writeonly",
print
class HydraClass:
def __init__(self, subj, graph):
self.subj = subj
self.properties = []
self.operations = []
for prop in graph.objects(subj, HYDRA.supportedProperty):
self.properties.append(HydraProperty(prop, graph))
for op in graph.objects(subj, HYDRA.supportedOperation):
ntriples = [triple for triple in graph.triples((op, None, None))]
if len(ntriples)==0:
g = Graph().parse(location=op, format="json-ld")
graph += g
self.operations.append(HydraOperation(op, graph))
def dump(self):
print "Class : ", self.subj
print self.subj, " properties :"
for prop in self.properties :
prop.dump()
print self.subj, " operations :"
for op in self.operations:
op.dump()
class ApiDoc:
def __init__(self, location):
self.graph = Graph().parse(location=location, format="json-ld")
self.classes = {}
self.operations = []
self.apidoc = self.graph.value(predicate=RDF.type, object=HYDRA.ApiDocumentation)
if self.apidoc is None:
raise Exception("no API Documentation found")
for cls in self.graph.objects(self.apidoc, HYDRA.supportedClass):
ntriples = [triple for triple in self.graph.triples((cls, None, None))]
if len(ntriples)==0:
g = Graph().parse(location=cls, format="json-ld")
self.graph += g
self.classes[cls] = HydraClass(cls, self.graph)
for ops in self.graph.objects(self.apidoc, HYDRA.supportedOperation):
self.operations.append(HydraOperation(ops, self.graph))
def dump(self):
print self.apidoc, " classes : "
for cls, clsobj in self.classes.items():
clsobj.dump()
def parse_link(lvalue):
"""parse Link HTTP header"""
tmp = lvalue.split("; ")
rel = None
link = tmp[0]
if link[0] =="<":
link = link[1:-1]
k, v = tmp[1].split('=')
if k=="rel":
rel = v
if rel is not None:
if rel[0]=='"':
rel = rel[1:-1]
return link, rel
class HypermediaControl:
def __init__(self, url):
self.url = url
class Hypermedia:
def __init__(self, doc=None):
self.doc = doc
self.is_collection = False
self.controls = []
self.members = []
def check_documentation(self, location):
"""return true if the location is ApiDocumentation, false if the documentation is elsewhere (pointed by Link header)
"""
use_entry = True
hresp = requests.head(location)
if 'link' in hresp.headers:
link, rel = parse_link(hresp.headers["link"])
#print link, rel
if rel is not None:
self.doc = ApiDoc(link)
use_entry = False
else:
self.doc = ApiDoc(location)
else:
self.doc = ApiDoc(location)
return use_entry
def open(self, location):
""" open current location, find for api doc if not yet set
"""
is_apidoc = False
if self.doc is None:
is_apidoc = self.check_documentation(location)
if is_apidoc:
return False
g = Graph().parse(location=location, format="json-ld")
print g.serialize(format="n3")
for subj, obj in g.subject_objects(RDF.type):
#print subj, obj, obj in self.doc.classes
if obj in [HYDRA.Collection, HYDRA.PagedCollection]:
for member in g.objects(subj, HYDRA.member):
self.members.append(member)
elif obj in self.doc.classes:
for p, o in g.predicate_objects(subj):
if p == RDF.type: continue
if (p, RDF.type, None) in self.doc.graph:
ptype = self.doc.graph.value(p, RDF.type)
if ptype == HYDRA.Link:
pref = self.doc.graph.value(p, RDFS.comment)
if pref is None:
pref = "Link"
link = {}
link["operations"] = []
link["url"] = o
for op in self.doc.graph.objects(p, HYDRA.supportedOperation):
link["operations"].append(HydraOperation(op, self.doc.graph))
self.controls.append(link)
print pref, o, [str(l.method) for l in link["operations"]]
else:
print o, " is ", ptype
return True
if __name__ == "__main__":
hm = Hypermedia()
if hm.open(sys.argv[1]):
print len(hm.members), len(hm.controls)
@mpetyx
Copy link

mpetyx commented Nov 2, 2014

Hello,
I am playing with your script here and seems cool!
I am not that sure if it is updated to the latest hydra spec, so I just wanted to check if you parse correctly the http://www.markus-lanthaler.com/hydra/api-demo/vocab# example.
I just give that as an input, but even though it parses it, it returns 0 0 as the result according your main function..
Is this the desired approach for this document?
Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment