Skip to content

Commit 64d494f

Browse files
Merge pull request #5 from elsevierlabs-os/dev
New features
2 parents 2562118 + a57216b commit 64d494f

File tree

9 files changed

+487
-197
lines changed

9 files changed

+487
-197
lines changed

media/js/cpldviewer.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"license": "SEE LICENSE IN LICENSE.md",
77
"repository": "https://github.com/elsevierlabs-os/cpld-viewer.git",
88
"icon": "media/img/cpld-icon.png",
9-
"version": "1.0.4",
9+
"version": "1.0.5",
1010
"engines": {
1111
"vscode": "^1.61.0"
1212
},

proxy/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Simple Linked Data Redirect Proxy
2+
3+
The `proxy.py` script in this folder can be used as a simple proxy for redirecting requests from the viewer to Linked Data services on the web. This is particularly useful when:
4+
5+
* The services require authentication
6+
* The services do not dereference from the original Linked Data URI
7+
* The services are not CORS enabled.
8+
9+
## Configuration
10+
11+
* Rename `config.template.yml` to `config.yml`
12+
* Make adjustments in the config file to suit your needs:
13+
* The `rewrites` dictionary should contain entries that match the requested IRI to a regex, with an `id` part.
14+
* If a match is found, this `id` part is then concatenated at the back of the `rewrite_url_prefix` and the request is forwarded to that.
15+
* The `apis` dictionary are also triggered by a regex match.
16+
* Create an object for each api, including an address for retrieving a JSON web token.
17+
* The proxy will always forward only to the first service that matches.

proxy/config.template.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
apis:
2+
API_1_NAME:
3+
regex: "https://data.elsevier.com/health/(core/)?(?P<id>.*)"
4+
token_url: "URL FOR RETRIEVING WEBTOKEN"
5+
key: "SECRET KEY TO OBTAIN TOKEN"
6+
secret: "SECRET TO OBTAIN TOKEN"
7+
api_url: "API URL. The <id> value is concatenated at the end of this URL."
8+
rewrites:
9+
REWRITE_1_NAME:
10+
regex: "https://some.example.iri/namespace/(?P<id>.*)"
11+
rewrite_url_prefix: "https://some.different.example/iri/namespace/to/which/to/redirect/"
12+
REWRITE_2_NAME:
13+
regex: "https://some.other.example.iri/namespace/(?P<id>.*)"
14+
rewrite_url_prefix: "https://some.other.different.example/iri/namespace/to/which/to/redirect/"

proxy/proxy.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import requests
2+
import re
3+
import yaml
4+
import rfc3987
5+
from flask import Flask, request, make_response
6+
from flask_cors import CORS
7+
8+
app = Flask(__name__)
9+
CORS(app)
10+
11+
12+
try:
13+
config = yaml.safe_load(open("config.yml"));
14+
except:
15+
config = {'apis': {}, 'rewrites': {}}
16+
17+
@app.route("/browse", methods=["GET"])
18+
def browse():
19+
iri = request.args.get('uri', None)
20+
21+
22+
is_iri = rfc3987.match(iri)
23+
if is_iri is None:
24+
return make_response({"ERROR": f"{is_iri} is not a valid IRI according to RFC3987"},400)
25+
26+
response = None
27+
if iri is not None:
28+
print(iri)
29+
passed_through_response = do_request(iri, request.headers)
30+
response = make_response(passed_through_response.content)
31+
response.headers['Content-Type'] = passed_through_response.headers['Content-Type']
32+
else:
33+
response = make_response({})
34+
response.headers['Content-Type'] = "application/ld+json"
35+
36+
return response
37+
38+
def do_request(iri, headers):
39+
40+
print(headers.get("Accept"))
41+
proxy_headers = {
42+
"Accept": headers.get("Accept")
43+
}
44+
45+
for api in config['apis'].values():
46+
regex = api['regex']
47+
token_url = api['token_url']
48+
key = api['key']
49+
secret = api['secret']
50+
api_url = api['api_url']
51+
52+
# Test for match against H-Graph API
53+
match = re.match(regex,iri)
54+
55+
if match != None:
56+
# Get token
57+
response = requests.post(token_url, auth=(key,secret))
58+
if response.status_code == 200:
59+
access_token = response.json()['access_token']
60+
print(access_token)
61+
else:
62+
raise Exception("API Token could not be retrieved for API call")
63+
64+
api_call_url = api_url + match.group("id")
65+
66+
proxy_headers["Authorization"] = f"Bearer {access_token}"
67+
68+
print(api_call_url)
69+
try:
70+
return requests.get(api_call_url, headers=proxy_headers)
71+
except Exception as e:
72+
raise e
73+
74+
for rewrite in config['rewrites'].values():
75+
regex = rewrite['regex']
76+
rewrite_url_prefix = rewrite['rewrite_url_prefix']
77+
78+
match = re.match(regex, iri)
79+
if match != None:
80+
rewrite_call_url = rewrite_url_prefix + match.group('id')
81+
print(rewrite_call_url)
82+
return requests.get(rewrite_call_url, headers=proxy_headers)
83+
84+
85+
return requests.get(iri, headers=proxy_headers)

proxy/pyproject.toml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
[tool.poetry]
2+
name = "linked-data-proxy"
3+
version = "0.1.0"
4+
description = ""
5+
authors = ["Rinke Hoekstra <r.hoekstra@elsevier.com>"]
6+
readme = "README.md"
7+
packages = [{include = "linked_data_proxy"}]
8+
9+
[tool.poetry.dependencies]
10+
python = "^3.10"
11+
requests = "^2.31.0"
12+
flask = "^2.3.2"
13+
flask-cors = "^3.0.10"
14+
pyyaml = "^6.0"
15+
rfc3987 = "^1.3.8"
16+
17+
18+
[build-system]
19+
requires = ["poetry-core"]
20+
build-backend = "poetry.core.masonry.api"

src/cpldviewer.css

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ body {
5959
background: #E0E4CC;
6060
}
6161

62+
.object.literal {
63+
background: #e0e4cc7e !important;
64+
font-style: oblique;
65+
cursor:text;
66+
}
67+
6268
.cpld-box {
6369
background: rgba(170, 170, 170, 0.4);
6470
cursor: pointer;
@@ -91,6 +97,23 @@ body {
9197
grid-template-columns: repeat(3, 1fr);
9298
}
9399

100+
.error-container {
101+
min-height: 300px;
102+
min-width: 100%;
103+
display: flex;
104+
align-items: center;
105+
justify-content: center;
106+
}
107+
108+
.error {
109+
border: 1px black;
110+
background-color: #fa690029;
111+
color: black;
112+
border-radius: 5px;
113+
padding: 3em;
114+
text-align: center;
115+
}
116+
94117
.toast {
95118
max-width: 420px;
96119
}

0 commit comments

Comments
 (0)