Skip to content

Commit 49f64a2

Browse files
committed
Add several fixes
1 parent 015f738 commit 49f64a2

40 files changed

+9418
-2
lines changed

Diff for: README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ It can also translate a Thing Description into an AID.
1111
To install the plugin, create a Python 3 [virtualenv](https://virtualenv.pypa.io/en/latest/user_guide.html).
1212

1313
```bash
14-
virtualenv env_name
14+
virtualenv domus
1515
```
1616

1717
Then activate the venv
1818

1919
```bash
20-
source env_name/bin/activate
20+
source domus/bin/activate
2121
```
2222

2323
Then install the [domus-tdd-api](https://github.com/eclipse-thingweb/domus-tdd-api).

Diff for: build/lib/domus_tdd_api_plugin_aid/__init__.py

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
from flask import Blueprint, request, Response
2+
import json
3+
4+
from tdd.common import (
5+
delete_id,
6+
)
7+
from tdd.utils import (
8+
POSSIBLE_MIMETYPES,
9+
negociate_mime_type,
10+
update_collection_etag,
11+
)
12+
from tdd.errors import WrongMimeType
13+
14+
15+
from domus_tdd_api_plugin_aid.aas import (
16+
get_aas_description,
17+
put_aas_json_in_sparql,
18+
put_aas_rdf_in_sparql,
19+
validate_aas,
20+
)
21+
from domus_tdd_api_plugin_aid.aml import translate_aml_to_aas
22+
23+
blueprint = Blueprint("domus_tdd_api_plugin_aid", __name__, url_prefix="/aas")
24+
25+
26+
@blueprint.route("/<id>", methods=["DELETE"])
27+
def delete_route_aas(id):
28+
response = delete_id(id)
29+
if response.status_code in [200, 204]:
30+
update_collection_etag()
31+
return response
32+
33+
34+
@blueprint.route("/<id>", methods=["GET"])
35+
def describe_aas(id):
36+
mime_type_negociated = negociate_mime_type(
37+
request, default_mimetype="application/aas+json"
38+
)
39+
description = get_aas_description(id, content_type=mime_type_negociated)
40+
if mime_type_negociated == "application/aas+json":
41+
description = json.dumps(description)
42+
return Response(description, content_type=mime_type_negociated)
43+
44+
45+
@blueprint.route("/<id>", methods=["PUT"])
46+
def create_aas(id):
47+
mimetype = request.content_type
48+
if mimetype == "application/json":
49+
mimetype = "application/aas+json"
50+
if mimetype == "application/aas+json":
51+
json_ld_content = request.get_data()
52+
content = validate_aas(json_ld_content, uri=id)
53+
updated, uri = put_aas_json_in_sparql(content, uri=id)
54+
elif mimetype == "application/aml+xml":
55+
aml_data = request.get_data()
56+
uri, rdf_aas_data = translate_aml_to_aas(aml_data, uri=id)
57+
updated, uri = put_aas_rdf_in_sparql(
58+
rdf_aas_data, "application/n-triples", uri=uri
59+
)
60+
elif mimetype in POSSIBLE_MIMETYPES:
61+
rdf_content = request.get_data()
62+
updated, uri = put_aas_rdf_in_sparql(rdf_content, mimetype, uri=id)
63+
else:
64+
raise WrongMimeType(mimetype)
65+
66+
update_collection_etag()
67+
return Response(status=201 if not updated else 204, headers={"Location": uri})
68+
69+
70+
@blueprint.route("", methods=["POST"])
71+
def create_anonymous_aas():
72+
mimetype = request.content_type
73+
if mimetype == "application/json":
74+
mimetype = "application/aas+json"
75+
if mimetype == "application/aas+json":
76+
json_ld_content = request.get_data()
77+
content = validate_aas(json_ld_content)
78+
updated, uri = put_aas_json_in_sparql(content, delete_if_exists=False)
79+
elif mimetype == "application/aml+xml":
80+
aml_data = request.get_data()
81+
uri, rdf_aas_data = translate_aml_to_aas(aml_data)
82+
updated, uri = put_aas_rdf_in_sparql(
83+
rdf_aas_data, "application/n-triples", uri=uri
84+
)
85+
elif mimetype in POSSIBLE_MIMETYPES:
86+
content = request.get_data()
87+
updated, uri = put_aas_rdf_in_sparql(content, mimetype, delete_if_exists=False)
88+
else:
89+
raise WrongMimeType(mimetype)
90+
update_collection_etag()
91+
return Response(status=201 if not updated else 204, headers={"Location": uri})

Diff for: build/lib/domus_tdd_api_plugin_aid/aas.py

+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# -*- coding: utf-8 -*-
2+
from copy import copy
3+
import json
4+
from pathlib import Path
5+
import uuid
6+
7+
8+
from rdflib import Graph
9+
from rdflib.exceptions import ParserError
10+
11+
from tdd.common import (
12+
frame_nt_content,
13+
get_id_description,
14+
put_json_in_sparql,
15+
put_rdf_in_sparql,
16+
)
17+
from tdd.context import convert_context_to_array, get_context
18+
from tdd.errors import IDMismatchError, JSONDecodeError, RDFValidationError
19+
from tdd.utils import uri_to_base
20+
21+
ONTOLOGY = {"prefix": "aas", "base": "https://admin-shell.io/aas/3/0/"}
22+
23+
DATA_DIR = Path(__file__).parent / "data"
24+
25+
with open(DATA_DIR / "context.v3.json") as fp:
26+
CONTEXT = json.load(fp)
27+
28+
29+
def validate_aas(str_content, uri=None):
30+
try:
31+
json_content = json.loads(str_content)
32+
except json.decoder.JSONDecodeError as exc:
33+
raise JSONDecodeError(exc)
34+
35+
if uri is not None:
36+
if "id" in json_content:
37+
if uri != json_content["id"]:
38+
raise IDMismatchError(json_content["id"], uri)
39+
else:
40+
json_content["id"] = uri
41+
return json_content
42+
43+
44+
def frame_aas_nt_content(uri, nt_content, original_context):
45+
context = copy(original_context)
46+
context.append({"@base": uri_to_base(uri)})
47+
context.append(CONTEXT)
48+
49+
frame = {
50+
"@context": context,
51+
"id": uri,
52+
}
53+
54+
str_json_framed = frame_nt_content(uri, nt_content, frame).decode("utf-8")
55+
56+
result = json.loads(str_json_framed)
57+
try:
58+
del result["@context"]
59+
del result["@id"]
60+
except KeyError:
61+
pass
62+
return result
63+
64+
65+
def get_aas_description(uri, content_type="application/aas+json"):
66+
if not content_type.endswith("json"):
67+
return get_id_description(uri, content_type, ONTOLOGY)
68+
content = get_id_description(uri, "application/n-triples", ONTOLOGY)
69+
original_context = get_context(uri, ONTOLOGY)
70+
jsonld_response = frame_aas_nt_content(uri, content, original_context)
71+
return jsonld_response
72+
73+
74+
def put_aas_rdf_in_sparql(rdf_content, mimetype, uri=None, delete_if_exists=True):
75+
g = Graph()
76+
try:
77+
g.parse(data=rdf_content, format=mimetype)
78+
except (SyntaxError, ParserError):
79+
raise RDFValidationError(f"The RDF triples are not well formatted ({mimetype})")
80+
81+
put_rdf_in_sparql(
82+
g,
83+
uri,
84+
[CONTEXT],
85+
delete_if_exists,
86+
ONTOLOGY,
87+
)
88+
89+
return (False, uri) # For now, updated = False
90+
91+
92+
def put_aas_json_in_sparql(content, uri=None, delete_if_exists=True):
93+
"""
94+
returns:
95+
- boolean True if the TD inserted already existed
96+
- uri of the TD
97+
"""
98+
content = copy(content)
99+
convert_context_to_array(content)
100+
if "id" not in content and "identification" not in content:
101+
content["id"] = f"urn:uuid:{uuid.uuid4()}"
102+
103+
original_context = copy(content["@context"])
104+
105+
uri = uri if uri is not None else content["id"]
106+
107+
content["@context"].append(CONTEXT)
108+
content["@context"].append({"@base": uri_to_base(uri)})
109+
put_json_in_sparql(content, uri, original_context, delete_if_exists, ONTOLOGY)
110+
111+
return (False, uri) # For now, updated = False

0 commit comments

Comments
 (0)