diff --git a/Backend-libXML/.gitignore b/Backend-libXML/.gitignore new file mode 100644 index 0000000..9ca9383 --- /dev/null +++ b/Backend-libXML/.gitignore @@ -0,0 +1,3 @@ +.idea +__pycache** +venv diff --git a/Backend-libXML/Dockerfile b/Backend-libXML/Dockerfile new file mode 100644 index 0000000..f3524b4 --- /dev/null +++ b/Backend-libXML/Dockerfile @@ -0,0 +1,8 @@ +FROM tiangolo/meinheld-gunicorn-flask:python3.9 + +COPY ./requirements.txt /app/requirements.txt + +RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt + +COPY ./main.py /app/ +COPY ./Parser.py /app/ \ No newline at end of file diff --git a/Backend-libXML/Parser.py b/Backend-libXML/Parser.py new file mode 100644 index 0000000..3b492b1 --- /dev/null +++ b/Backend-libXML/Parser.py @@ -0,0 +1,67 @@ +from lxml import etree + + +def xpath(source: str, xpath: str) -> str: + """ + Method used to get nodes from XML string using XPath + + :param source: XML string used for selection + :type source: str + :param xpath: XPath query used for selection + :type xpath: str + :return: Nodes selected using XPath + :rtype: str + """ + + + root = etree.XML(source) + nsmap = root.nsmap + + # LXML doesn't accept empty (None) namespace prefix, + # so it need to be deleted if exists + if None in nsmap: + nsmap.pop(None) + + result = root.xpath(xpath, namespaces=nsmap) + result_string = "" + for e in result: + result_string += etree.tostring(e, pretty_print=True).decode() + "\n" + return result_string + + + +def xsd(source: str, xsd: str) -> bool: + """ + Method used to validate XML string against XSD schema + :param source: XML string used for validation + :type source: str + :param xsd: XSD schema to validate XML against + :type xsd: str + :return: If the validation was successful or not + :rtype: bool + """ + xml_schema = etree.XMLSchema(etree.XML(xsd)) + + xml = etree.XML(source) + + return xml_schema.validate(xml) + + +def xslt(source: str, xslt: str) -> str: + """ + Method used to transformate XML string using XSLT + + :param source: XML string to transform + :type source: str + :param xslt: XSLT string used to transformate XML + :type xslt: str + :return: Result of transformation + :rtype: str + """ + xslt_transform = etree.XSLT(etree.XML(xslt)) + + xml = etree.XML(source) + + transformated = xslt_transform(xml) + print(transformated) + return str(transformated) \ No newline at end of file diff --git a/Backend-libXML/main.py b/Backend-libXML/main.py new file mode 100644 index 0000000..78fc6cf --- /dev/null +++ b/Backend-libXML/main.py @@ -0,0 +1,68 @@ +from flask import Flask +from flask import request +from lxml import etree +import json +import time +import Parser + + +app = Flask(__name__) + + +def process_xml(request: request, type: str) -> str: + """Function to process + + :param request: Received request + :type request: request + :param type: Type of needed processing: xsd, xslt or xpath + :type type: str + :raises ValueError: is raised when type is different than those provided above + :return: response JSON converted to string and response code + :rtype: str, int + """ + start = time.time_ns() + code = 200 + response_json = dict() + try: + request_data = json.loads(request.get_data(as_text=True)) + data = request_data['data'] + process = request_data['process'] + if (type == "xsd"): + response_json['result'] = Parser.xsd(data, process) + elif (type == "xslt"): + response_json['result'] = Parser.xslt(data, process) + elif (type == "xpath"): + response_json['result'] = Parser.xpath(data, process) + else: + raise ValueError("Valid operation types are: xsd, xslt, xpath") + + response_json['status'] = "OK" + except KeyError as e: + response_json['result'] = "Missing key: " + str(e) + response_json['status'] = "ERR" + code = 400 + except Exception as e: + response_json['result'] = str(e) + response_json['status'] = "ERR" + code = 400 + finally: + exec_time = (time.time_ns() - start) / 10**6 + response_json['time'] = f"{exec_time:.03f}" + response_json['processor'] = "libxml2 over lxml" + return json.dumps(response_json), code + + +@app.route("/xpathpost", methods=["POST"]) +def xpath(): + return process_xml(request, "xpath") + +@app.route("/xsdpost", methods=["POST"]) +def xsd(): + return process_xml(request, "xsd") + +@app.route("/xsltpost", methods=["POST"]) +def xslt(): + return process_xml(request, "xslt") + +if __name__ == "__main__": + app.run() \ No newline at end of file diff --git a/Backend-libXML/requirements.txt b/Backend-libXML/requirements.txt new file mode 100644 index 0000000..d176ffe --- /dev/null +++ b/Backend-libXML/requirements.txt @@ -0,0 +1,2 @@ +lxml +flask diff --git a/Backend-libXML/sample/xpath/data.json b/Backend-libXML/sample/xpath/data.json new file mode 100644 index 0000000..2de1632 --- /dev/null +++ b/Backend-libXML/sample/xpath/data.json @@ -0,0 +1,6 @@ +{ + "data": "Hamlet2001-05-041falseMacbeth2000-12-131falseHarry Potter and the Sorcerer's Stone2005-04-292trueThe Long Walk2018-07-014trueMisery2018-01-314trueThink and Grow Rich2004-09-106trueThe Law of Success1982-05-096falsePatriot Games1995-10-215falseThe Sum of All Fears1992-09-195falseThe Alchemist2017-02-203falseHamlet1994-06-011falseMeasure for Measure1990-03-231falseHamlet1989-05-051trueHamlet1999-05-301trueThe Law of Success2004-11-266trueRomeo and Juliet1997-02-081trueThe Alchemist2009-08-213true", + "process": "/books/book[name = 'The Law of Success']", + "processor": "saxon", + "version": "2.0" +} diff --git a/Backend-libXML/sample/xpath/dataNS.json b/Backend-libXML/sample/xpath/dataNS.json new file mode 100644 index 0000000..ec5eb94 --- /dev/null +++ b/Backend-libXML/sample/xpath/dataNS.json @@ -0,0 +1,6 @@ +{ + "data": "Hamlet2001-05-041falseMacbeth2000-12-131falseHarry Potter and the Sorcerer's Stone2005-04-292trueThe Long Walk2018-07-014trueMisery2018-01-314trueThink and Grow Rich2004-09-106trueThe Law of Success1982-05-096falsePatriot Games1995-10-215falseThe Sum of All Fears1992-09-195falseThe Alchemist2017-02-203falseHamlet1994-06-011falseMeasure for Measure1990-03-231falseHamlet1989-05-051trueHamlet1999-05-301trueThe Law of Success2004-11-266trueRomeo and Juliet1997-02-081trueThe Alchemist2009-08-213true", + "process": "/b:books/b:book[b:name = 'The Law of Success']", + "processor": "saxon", + "version": "2.0" +} diff --git a/Backend-libXML/sample/xpath/non-ns.curl b/Backend-libXML/sample/xpath/non-ns.curl new file mode 100644 index 0000000..7d4219f --- /dev/null +++ b/Backend-libXML/sample/xpath/non-ns.curl @@ -0,0 +1,4 @@ +#url = "localhost:8081/xpathpost" +url = "localhost:5000/xpath" +request = "POST" +data = "@data.json" diff --git a/Backend-libXML/sample/xpath/ns.curl b/Backend-libXML/sample/xpath/ns.curl new file mode 100644 index 0000000..f439c0e --- /dev/null +++ b/Backend-libXML/sample/xpath/ns.curl @@ -0,0 +1,4 @@ +#url = "localhost:8081/xpathpost" +url = "localhost:5000/xpath" +request = "POST" +data = "@dataNS.json" diff --git a/Backend-libXML/sample/xsd/xsd.curl b/Backend-libXML/sample/xsd/xsd.curl new file mode 100644 index 0000000..2481c02 --- /dev/null +++ b/Backend-libXML/sample/xsd/xsd.curl @@ -0,0 +1,4 @@ +#url = "http://localhost:8082/xsd" +url = "http://localhost:5000/xsd" +data = "@xsd.json" +request = POST diff --git a/Backend-libXML/sample/xsd/xsd.json b/Backend-libXML/sample/xsd/xsd.json new file mode 100644 index 0000000..91dd2f2 --- /dev/null +++ b/Backend-libXML/sample/xsd/xsd.json @@ -0,0 +1,6 @@ +{ + "data": "TestTest3", + "process": " ", + "processor": "saxon", + "version": "1.0" +} diff --git a/Backend-libXML/sample/xslt/xslt.curl b/Backend-libXML/sample/xslt/xslt.curl new file mode 100644 index 0000000..d50edd1 --- /dev/null +++ b/Backend-libXML/sample/xslt/xslt.curl @@ -0,0 +1,3 @@ +url = "http://localhost:5000/xslt" +data = "@xslt.json" +request = POST diff --git a/Backend-libXML/sample/xslt/xslt.json b/Backend-libXML/sample/xslt/xslt.json new file mode 100644 index 0000000..f833a40 --- /dev/null +++ b/Backend-libXML/sample/xslt/xslt.json @@ -0,0 +1,6 @@ +{ + "data": "Hamlet2001-05-041falseMacbeth2000-12-131falseHarry Potter and the Sorcerer's Stone2005-04-292trueThe Long Walk2018-07-014trueMisery2018-01-314trueThink and Grow Rich2004-09-106trueThe Law of Success1982-05-096falsePatriot Games1995-10-215falseThe Sum of All Fears1992-09-195falseThe Alchemist2017-02-203falseHamlet1994-06-011falseMeasure for Measure1990-03-231falseHamlet1989-05-051trueHamlet1999-05-301trueThe Law of Success2004-11-266trueRomeo and Juliet1997-02-081trueThe Alchemist2009-08-213true", + "process": "", + "processor": "saxon", + "version": "1.0" +} diff --git a/Frontend/tools/xpath.html b/Frontend/tools/xpath.html index 3d0a6b3..1d4a84d 100644 --- a/Frontend/tools/xpath.html +++ b/Frontend/tools/xpath.html @@ -24,7 +24,7 @@ - +
diff --git a/Frontend/tools/xslt.html b/Frontend/tools/xslt.html index cc77f34..f1656e9 100644 --- a/Frontend/tools/xslt.html +++ b/Frontend/tools/xslt.html @@ -19,6 +19,7 @@ procInfo

diff --git a/docker-compose.yml b/docker-compose.yml index 559be61..6524db5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -19,6 +19,13 @@ services: ports: - 8081:8081 + xmltools-libxml-backend: + build: ./Backend-libXML + container_name: xmltools-libxml-backend + image: xmltools-libxml-backend + ports: + - 8082:80 + xmltools-mocked-services: build: context: ./Backend/mocked-services diff --git a/readme.md b/readme.md index d5600b6..c1a3e1b 100644 --- a/readme.md +++ b/readme.md @@ -17,6 +17,11 @@ openapi.yml Rest API accepts xml documents, given querry and processor version to call requested xml engine to perform given operation and finally returns outcome in response body. +## Flask Python backend + +This is module providing support for processing XMLs using libxml library. It consumes same JSON as Java backend. + + ## Mocked services MockedServices is a tool that allows developer to create, in easy and simple way, http server mocked endpoints for integration tests