From f10ef98f36dc5c5c490d609c2cdf247a3a5da18d Mon Sep 17 00:00:00 2001 From: Jonathan Yu Date: Wed, 21 Nov 2018 15:27:56 +1100 Subject: [PATCH 1/6] initail commit --- Dockerfile | 4 ++++ app.py | 47 +++++++++++++++++++++++++++++++++++++++ docker-compose.yml | 13 +++++++++++ requirements.txt | 2 ++ templates/.view.html.swp | Bin 0 -> 12288 bytes templates/index.html | 3 +++ templates/view.html | 31 ++++++++++++++++++++++++++ 7 files changed, 100 insertions(+) create mode 100644 Dockerfile create mode 100644 app.py create mode 100644 docker-compose.yml create mode 100644 requirements.txt create mode 100644 templates/.view.html.swp create mode 100644 templates/index.html create mode 100644 templates/view.html diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..51668a2 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,4 @@ +FROM python:3.6 +ADD . /todo +WORKDIR /todo +RUN pip install -r requirements.txt diff --git a/app.py b/app.py new file mode 100644 index 0000000..1feea40 --- /dev/null +++ b/app.py @@ -0,0 +1,47 @@ +import os +from flask import Flask, redirect, url_for, request, render_template, jsonify +from pymongo import MongoClient +from bson import json_util +from bson.json_util import dumps + +import json + + +app = Flask(__name__) + +client = MongoClient( + os.environ['DB_PORT_27017_TCP_ADDR'], + 27017) +db = client.datasets + +@app.route('/') +def list(): + _items = db.datasets.find() + items = [item for item in _items] + return render_template('index.html', items=items) + +@app.route('/view') +def view(): + app.logger.debug(request.args) + id = request.args.get('id') + item = db.datasets.find_one( { 'id': id }) + item.pop('_id') + jsonldStr = dumps(item,default=json_util.default, indent=4) + app.logger.debug(jsonldStr) + + if request.args.get('format') is not None and request.args.get('format') == 'jsonld': + return jsonify(item) + + return render_template('view.html', dataset=item, jsonld=jsonldStr) + +@app.route('/new', methods=['POST']) +def new(): + app.logger.debug("JSON received...") + app.logger.debug(request.json) + content = request.json + db.datasets.insert_one(content) + + return jsonify({ 'result' : 'success' }) + +if __name__ == "__main__": + app.run(host='0.0.0.0', debug=True) diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..ce569e4 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,13 @@ +web: + build: . + command: python -u app.py + ports: + - "4000:5000" + volumes: + - .:/todo + links: + - db +db: + image: mongo:latest + ports: + - "27017:27017" diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..a98715e --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +flask +pymongo diff --git a/templates/.view.html.swp b/templates/.view.html.swp new file mode 100644 index 0000000000000000000000000000000000000000..6382684600b45e3d445f6bdcaa85bad8d5a41c6e GIT binary patch literal 12288 zcmeI2KX21O7>BP&Fd!8WtVkR|p&&@Ez|!I3N*$08>V%pFfjH;RwHwE0*_TqwwHWBc zpP7v>z|O`ufRT}fnFYkc#?N+w3R1*ecr88lo$tN(-g}}9QO;i--r3}9{Z)qR3}Yug zeZMbXpJb12Gqz)s`t4sY#Pxn$&P9Ii z?AD@adQS$(02v?yWPl8i0Wv@a$iP2nz@`VOfe-G1P0$7B!Ex{f`9Fh?;5~Q_o`C}pfQR4#XzDnE9;sw9KnBPF86X2>fDDiU zGC&5%!2f7KN>QKG_861Gx4Q6{R6NW~Fjx^Q-i8{DxwAY5&{ zBRwcft(?k@>qok(E(bQ7C^y*2Z4&E_H^h-}UUzVGyv-w=@?+z%vdc}P5zf06 {{ item.id}}

+{% endfor %} diff --git a/templates/view.html b/templates/view.html new file mode 100644 index 0000000..5665836 --- /dev/null +++ b/templates/view.html @@ -0,0 +1,31 @@ + + + + + + + + + + + + + +{% for key, value in dataset.items() %} + + + + +{% endfor %} +
KeyValue
{{key}}{{value}}
+View json-ld + +
+Back to index + + + From 4a14be2ac82cfc37f36f5ccfa7d03cbefea058ab Mon Sep 17 00:00:00 2001 From: Jonathan Yu Date: Wed, 21 Nov 2018 15:28:11 +1100 Subject: [PATCH 2/6] remove swp file; --- templates/.view.html.swp | Bin 12288 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 templates/.view.html.swp diff --git a/templates/.view.html.swp b/templates/.view.html.swp deleted file mode 100644 index 6382684600b45e3d445f6bdcaa85bad8d5a41c6e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeI2KX21O7>BP&Fd!8WtVkR|p&&@Ez|!I3N*$08>V%pFfjH;RwHwE0*_TqwwHWBc zpP7v>z|O`ufRT}fnFYkc#?N+w3R1*ecr88lo$tN(-g}}9QO;i--r3}9{Z)qR3}Yug zeZMbXpJb12Gqz)s`t4sY#Pxn$&P9Ii z?AD@adQS$(02v?yWPl8i0Wv@a$iP2nz@`VOfe-G1P0$7B!Ex{f`9Fh?;5~Q_o`C}pfQR4#XzDnE9;sw9KnBPF86X2>fDDiU zGC&5%!2f7KN>QKG_861Gx4Q6{R6NW~Fjx^Q-i8{DxwAY5&{ zBRwcft(?k@>qok(E(bQ7C^y*2Z4&E_H^h-}UUzVGyv-w=@?+z%vdc}P5zf06 Date: Wed, 21 Nov 2018 15:45:27 +1100 Subject: [PATCH 3/6] adding readme content --- README.md | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3096154..56eb928 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,31 @@ # bald-server -Web server for BALD graphs +This repo provides landing pages and web APIs for BALD graphs - exposed as RDF triples or JSON-LD (schema.org profile). + +## Current features +* REST API +* HTML Templated content with schema.org embedded in + +## To do +* RDF Triple store backend serving BALD graphs + + +## Pre-requisites +* Docker 1.6+ +* docker-compose 1.3+ + +## Quickstart + +Spin up the python flask app and mongo via docker-compose +``` +$ docker-compose up -d +``` + +Your application should be now running on http://localhost:4000 + +## Usage + +### Routes + +* GET '/' - list of datasets +* GET '/view?id=param1&format=param2' - view the dataset. param1 is the ID of the dataset, param2 (optional) currently allows 'jsonld' +* POST '/new' - add a new dataset description in JSON format (schema.org) From a0d19b7d74a111c0aafb34ea92ae078d45169a3b Mon Sep 17 00:00:00 2001 From: Jonathan Yu Date: Wed, 21 Nov 2018 15:48:07 +1100 Subject: [PATCH 4/6] adding more readme content --- README.md | 7 +++++++ example-uploader.sh | 7 +++++++ 2 files changed, 14 insertions(+) create mode 100755 example-uploader.sh diff --git a/README.md b/README.md index 56eb928..3746c4f 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,13 @@ Your application should be now running on http://localhost:4000 ## Usage +## Loading schema.org descriptions + +Assuming you have a directory of schema.org descriptions as json-ld, you can use the `example-uploader.sh` (as-is or customised), +to upload content to via APIs. This will then be listed in the application running on http://localhost:4000 + +A possible way to get a list of these descriptions is to use the threddsnc2rdf.py tool in the `bald` library. + ### Routes * GET '/' - list of datasets diff --git a/example-uploader.sh b/example-uploader.sh new file mode 100755 index 0000000..fcb7af0 --- /dev/null +++ b/example-uploader.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +DIR=$1 +for f in $(find $1 -name '*.json'); +do + curl -vX POST http://localhost:4000/new -d @${f} --header "Content-Type: application/json" +done From 7ce28b1b07403f3d451eb5b747e5b82943719158 Mon Sep 17 00:00:00 2001 From: Jonathan Yu Date: Wed, 21 Nov 2018 17:02:18 +1100 Subject: [PATCH 5/6] adding auth for POST and css --- app.py | 57 +++++++++++++++++++++++++++++++++++++++----- example-uploader.sh | 6 ++++- static/styles.css | 23 ++++++++++++++++++ templates/index.html | 14 +++++++++++ templates/view.html | 23 ++++++++++++++++-- 5 files changed, 114 insertions(+), 9 deletions(-) create mode 100644 static/styles.css diff --git a/app.py b/app.py index 1feea40..39bc967 100644 --- a/app.py +++ b/app.py @@ -1,11 +1,12 @@ import os -from flask import Flask, redirect, url_for, request, render_template, jsonify +from functools import wraps +from flask import Flask, redirect, url_for, request, render_template, jsonify, abort from pymongo import MongoClient from bson import json_util from bson.json_util import dumps - import json - +import uuid +from flask.logging import default_handler app = Flask(__name__) @@ -14,6 +15,43 @@ 27017) db = client.datasets +uuid.uuid4().hex + +if 'SECRET_KEY' in os.environ and os.environ['SECRET_KEY'] is not None: + authtoken = os.environ['SECRET_KEY'] +# app.logger.debug("Auth token is " + str(authtoken)) +else: + #generate a unique auth token + authtoken = uuid.uuid4().hex +# app.logger.debug("Auth token is " + str(authtoken)) + app.logger.debug("Use this for posting content") +authtoken = "SECRET" +print("Auth token is " + str(authtoken)) + +def authorize(f): + @wraps(f) + def decorated_function(*args, **kws): + print(request) + if not 'Authorization' in request.headers: + abort(401) + + user = None + data = request.headers['Authorization'].encode('ascii','ignore') + token = str.replace(str(data), 'Bearer ','') + #try: + # user = jwt.decode(token, JWT_SECRET, algorithms=['HS256'])['sub'] + #except: + # abort(401) + if token != authtoken: + abort(401) + return f(user, *args, **kws) + return decorated_function + +def isAuthorised(token): + if token == authtoken: + return True + return False + @app.route('/') def list(): _items = db.datasets.find() @@ -37,11 +75,18 @@ def view(): @app.route('/new', methods=['POST']) def new(): app.logger.debug("JSON received...") - app.logger.debug(request.json) + if not 'Authorization' in request.headers: + abort(401) + print(request.headers['Authorization']) + data = request.headers['Authorization'] + token = str.replace(str(data), 'Bearer ', '') + if token != authtoken: + return jsonify({ 'result' : 'not authorised' }),401 content = request.json - db.datasets.insert_one(content) + #db.datasets.insert_one(content, check_keys=False) + db.datasets.insert(content, check_keys=False) - return jsonify({ 'result' : 'success' }) + return jsonify({ 'result' : 'success' }),200 if __name__ == "__main__": app.run(host='0.0.0.0', debug=True) diff --git a/example-uploader.sh b/example-uploader.sh index fcb7af0..e65e9d3 100755 --- a/example-uploader.sh +++ b/example-uploader.sh @@ -1,7 +1,11 @@ #!/bin/bash DIR=$1 +AUTHTOKEN=$2 +declare -a curlArgs=('-H' "Content-Type: application/json" '-H' "Authorization: Bearer ${AUTHTOKEN}") + for f in $(find $1 -name '*.json'); do - curl -vX POST http://localhost:4000/new -d @${f} --header "Content-Type: application/json" + echo "curl -vX POST http://localhost:4000/new?token=${AUTHTOKEN} -d @${f} ${curlArgs[@]}\"" + curl -vX POST http://localhost:4000/new -d @${f} "${curlArgs[@]}" done diff --git a/static/styles.css b/static/styles.css new file mode 100644 index 0000000..8dd0728 --- /dev/null +++ b/static/styles.css @@ -0,0 +1,23 @@ +@import url('https://fonts.googleapis.com/css?family=Open+Sans'); + +body { +font-family: 'Open Sans', sans-serif; +} + +table { + border-collapse: collapse; +} + +table, th, td { + border: 1px solid black; +} + +td { + padding: 2px 2px 2px 2px; + vertical-align: top; +} + +.pullright { + float: right; + clear: both; +} diff --git a/templates/index.html b/templates/index.html index 6240401..420d6df 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,3 +1,17 @@ + + + + + + + + +

List of datasets

+ {% for item in items %}

{{ item.id}}

{% endfor %} + + + + diff --git a/templates/view.html b/templates/view.html index 5665836..475addd 100644 --- a/templates/view.html +++ b/templates/view.html @@ -1,6 +1,8 @@ + +