Skip to content

Commit

Permalink
additional documentation added
Browse files Browse the repository at this point in the history
  • Loading branch information
essepuntato committed Mar 5, 2021
1 parent c00aeb9 commit 8722e4e
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 38 deletions.
21 changes: 13 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ python -m ramose -s <conf_name>.hf -w 127.0.0.1:8080 -css <path/to/file.css>

RAMOSE allows developers to handle several APIs by instantiating the main class `APIManager` and initialising it with a specification file.

The method `exec_op(op_complete_url, method="get", content_type="application/json")` takes in input the url of the call (i.e. the API base URL plus the operation URL), the HTTP method to use for the call, and the content type to return. It executes the operation as indicated in the specification file, by running (in the following order):
The method `get_op(op_complete_url)` takes in input the url of the call (i.e. the API base URL plus the operation URL) and returns an object of type `Operation`. The instance of an `Operation` can be used to run the method `exec(method="get", content_type="application/json")`, which takes in input the url the HTTP method to use for the call and and the content type to return, and executes the operation as indicated in the specification file, by running (in the following order):

1. the methods to preprocess the query (as defined in the specification file at `#{var}` and `#preprocess`);
2. the SPARQL query related to the operation called, by using the parameters indicated in the URL (`#sparql`);
Expand All @@ -306,16 +306,21 @@ The method `exec_op(op_complete_url, method="get", content_type="application/jso
For example:

```
conf = { "api_1": "1_v1.hf", "api_2": "2_v1.hf"}
api_manager = APIManager([ "1_v1.hf", "2_v1.hf" ])
first_api_manager = APIManager(conf["api_1"])
second_api_manager = APIManager(conf["api_2"])
api_base_1 = "..."
api_base_2 = "..."
operation_url_1 = "..."
operation_url_2 = "..."
request = "..."
call_1 = "%s/%s/%s" % (api_base_1, operation_url_1, request)
call_2 = "%s/%s/%s" % (api_base_2, operation_url_2, request)
call_1 = "%s/%s/%s" % (api_base_1, operation_url_1, request )
call_2 = "%s/%s/%s" % (api_base_2, operation_url_2, request )
op1 = api_manager.get_op(call_1)
status1, result1 = op1.exec()
first_api_manager.exec_op(call_1, content_type=%s) % content_type
second_api_manager.exec_op(call_2, content_type=%s) % content_type
op2 = api_manager.get_op(call_2)
status2, result2 = op2.exec()
```

## Other functionalities and examples
Expand Down
26 changes: 14 additions & 12 deletions ramose.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@
from isodate import parse_duration
from argparse import ArgumentParser
from os.path import abspath, dirname, basename
from os import sep , getcwd
from os import path as pt
from os import sep, getcwd


FIELD_TYPE_RE = "([^\(\s]+)\(([^\)]+)\)"
Expand Down Expand Up @@ -105,18 +106,26 @@ def read(self, file_path):

class DocumentationHandler(object):
def __init__(self, api_manager):
"""This class provides the main structure for returning a human-readable documentation of all
the operations described in the configuration files handled by the APIManager specified as input."""
self.conf_doc = api_manager.all_conf

@abstractmethod
def get_documentation(self, *args, **dargs):
"""An abstract method that returns a string defining the human-readable documentation of the operations
available in the input APIManager."""
pass

@abstractmethod
def store_documentation(self, file_path, *args, **dargs):
"""An abstract method that store in the input file path (parameter 'file_path') the human-readable
documentation of the operations available in the input APIManager."""
pass

@abstractmethod
def get_index(self, *args, **dargs):
"""An abstract method that returns a string defining the index of all the various configuration files
handled by the input APIManager."""
pass

class HTMLDocumentationHandler(DocumentationHandler):
Expand Down Expand Up @@ -860,13 +869,6 @@ def __init__(self, op_complete_url, op_key, i, tp, sparql_http_method, addon):
">": gt
}

self.http_method = {
"get": get,
"put": put,
"post": post,
"delete": delete
}

self.dt = DataType()

# START: Ancillary methods
Expand Down Expand Up @@ -974,7 +976,7 @@ def get_item_in_dict(d_or_l, key_list, prev=None):
@staticmethod
def add_item_in_dict(d_or_l, key_list, item, idx):
"""This method takes as input a dictionary or a list of dictionaries, browses it until the value
specified following the chain indicated in 'key_list' is not found, adn then substitute it with 'item'.
specified following the chain indicated in 'key_list' is not found, and then substitutes it with 'item'.
In case the final object retrieved is a list, it selects the object in position 'idx' before the
substitution."""
key_list_len = len(key_list)
Expand Down Expand Up @@ -1508,7 +1510,7 @@ def get_op(self, op_complete_url):
from werkzeug.exceptions import HTTPException

# logs
logs = dh.logger_ramose()
dh.logger_ramose()

# web server
host_name = args.webserver.rsplit(':', 1)[0] if ':' in args.webserver else '127.0.0.1'
Expand Down Expand Up @@ -1563,7 +1565,7 @@ def doc(api_url):
response = make_response(si.getvalue(), status)
response.headers.set("Content-Disposition", "attachment", filename="error.csv")
else:
m_res , m_res["error"] , m_res["message"] = {} , status, res
m_res = {"error": status, "message": res}
mes = dumps(m_res)
response = make_response(mes, status)
response.headers.set('Content-Type', content_type) # overwrite text/plain
Expand All @@ -1580,7 +1582,7 @@ def doc(api_url):

except Exception as e:
exc_type, exc_obj, exc_tb = exc_info()
fname = path.split(exc_tb.tb_frame.f_code.co_filename)[1]
fname = pt.split(exc_tb.tb_frame.f_code.co_filename)[1]
print("[ERROR]", exc_type, fname, exc_tb.tb_lineno)

else:
Expand Down
44 changes: 26 additions & 18 deletions test/ramose.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@
from isodate import parse_duration
from argparse import ArgumentParser
from os.path import abspath, dirname, basename
from os import sep , getcwd
from os import path as pt
from os import sep, getcwd


FIELD_TYPE_RE = "([^\(\s]+)\(([^\)]+)\)"
Expand Down Expand Up @@ -105,6 +106,7 @@ def read(self, file_path):

class DocumentationHandler(object):
def __init__(self, api_manager):
"""TODO"""
self.conf_doc = api_manager.all_conf

@abstractmethod
Expand Down Expand Up @@ -228,7 +230,6 @@ def __operations(self, conf):
", ".join(["%s <em>(%s)</em>" % (f, t) for t, f in
findall(FIELD_TYPE_RE, op["field_type"])]),
conf["website"] + conf["base_url"] + op["call"], op["call"], op["output_json"])
# TODO multiple params: change the listing above
return markdown(result) + ops

def __footer(self):
Expand Down Expand Up @@ -763,7 +764,9 @@ def clean_log(self, l, api_url):

class DataType(object):
def __init__(self):
"""TODO"""
"""This class implements all the possible data types that can be used within
the configuration file of RAMOSE. In particular, it provides methods for converting
a string into the related Python data type representation."""
self.func = {
"str": DataType.str,
"int": DataType.int,
Expand All @@ -773,6 +776,7 @@ def __init__(self):
}

def get_func(self, name_str):
"""This method returns the method for handling a given data type expressed as a string name."""
return self.func.get(name_str)

@staticmethod
Expand Down Expand Up @@ -837,7 +841,13 @@ def float(s):

class Operation(object):
def __init__(self, op_complete_url, op_key, i, tp, sparql_http_method, addon):
"""This class take in input TODO"""
""" This class is responsible for materialising a API operation to be run against a SPARQL endpoint.
It takes in input a full URL referring to a call to an operation (parameter 'op_complete_url'),
the particular shape representing an operation (parameter 'op_key'), the definition (in JSON) of such
operation (parameter 'i'), the URL of the triplestore to contact (parameter 'tp'), the HTTP method
to use for the SPARQL request (paramenter 'sparql_http_method', set to either 'get' or 'post'), and the path
of the Python file which defines additional functions for use in the operation (parameter 'addon')."""
self.url_parsed = urlsplit(op_complete_url)
self.op_url = self.url_parsed.path
self.op = op_key
Expand All @@ -852,18 +862,13 @@ def __init__(self, op_complete_url, op_key, i, tp, sparql_http_method, addon):
">": gt
}

self.http_method = {
"get": get,
"put": put,
"post": post,
"delete": delete
}

self.dt = DataType()

# START: Ancillary methods
@staticmethod
def get_content_type(ct):
"""It returns the mime type of a given textual representation of a format, being it either
'csv' or 'json."""
content_type = ct

if ct == "csv":
Expand Down Expand Up @@ -964,7 +969,7 @@ def get_item_in_dict(d_or_l, key_list, prev=None):
@staticmethod
def add_item_in_dict(d_or_l, key_list, item, idx):
"""This method takes as input a dictionary or a list of dictionaries, browses it until the value
specified following the chain indicated in 'key_list' is not found, adn then substitute it with 'item'.
specified following the chain indicated in 'key_list' is not found, and then substitutes it with 'item'.
In case the final object retrieved is a list, it selects the object in position 'idx' before the
substitution."""
key_list_len = len(key_list)
Expand Down Expand Up @@ -1079,7 +1084,6 @@ def preprocess(self, par_dict, op_item, addon):
if "preprocess" in op_item:

for pre in [sub("\s+", "", i) for i in op_item["preprocess"].split(" --> ")]:
match_url = op_item["url"]
func_name = sub("^([^\(\)]+)\(.+$", "\\1", pre).strip()
params_name = sub("^.+\(([^\(\)]+)\).*", "\\1", pre).split(",")

Expand Down Expand Up @@ -1404,7 +1408,7 @@ def __init__(self, conf_files):

# START: Ancillary methods
@staticmethod
def nor_api_url(i, b=""): # TODO: stay in API manager
def nor_api_url(i, b=""):
"""This method takes an API operation object and an optional base URL (e.g. "/api/v1") as input
and returns the URL composed by the base URL plus the API URL normalised according to specific rules. In
particular, these normalisation rules takes the operation URL (e.g. "#url /citations/{oci}") and the
Expand All @@ -1423,7 +1427,7 @@ def nor_api_url(i, b=""): # TODO: stay in API manager

return "%s%s" % (b, result)

def best_match(self, u): # TODO: stay in API manager
def best_match(self, u):
"""This method takes an URL of an API call in input and find the API operation URL and the related
configuration that best match with the API call, if any."""
#u = u.decode('UTF8') if isinstance(u, (bytes, bytearray)) else u
Expand All @@ -1441,6 +1445,10 @@ def best_match(self, u): # TODO: stay in API manager

# START: Processing methods
def get_op(self, op_complete_url):
"""This method returns a new object of type Operation which represent the operation specified by
the input URL (parameter 'op_complete_url)'. In case no operation can be found according by checking
the configuration files available in the APIManager, a tuple with an HTTP error code and a message
is returned instead."""
url_parsed = urlsplit(op_complete_url)
op_url = url_parsed.path

Expand Down Expand Up @@ -1495,7 +1503,7 @@ def get_op(self, op_complete_url):
from werkzeug.exceptions import HTTPException

# logs
logs = dh.logger_ramose()
dh.logger_ramose()

# web server
host_name = args.webserver.rsplit(':', 1)[0] if ':' in args.webserver else '127.0.0.1'
Expand Down Expand Up @@ -1550,7 +1558,7 @@ def doc(api_url):
response = make_response(si.getvalue(), status)
response.headers.set("Content-Disposition", "attachment", filename="error.csv")
else:
m_res , m_res["error"] , m_res["message"] = {} , status, res
m_res = {"error": status, "message": res}
mes = dumps(m_res)
response = make_response(mes, status)
response.headers.set('Content-Type', content_type) # overwrite text/plain
Expand All @@ -1567,7 +1575,7 @@ def doc(api_url):

except Exception as e:
exc_type, exc_obj, exc_tb = exc_info()
fname = path.split(exc_tb.tb_frame.f_code.co_filename)[1]
fname = pt.split(exc_tb.tb_frame.f_code.co_filename)[1]
print("[ERROR]", exc_type, fname, exc_tb.tb_lineno)

else:
Expand Down

0 comments on commit 8722e4e

Please sign in to comment.