diff --git a/README.md b/README.md index d2ccc71..3f03ad3 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,13 @@ According to [World History Encyclopedia](https://www.worldhistory.org/article/7 In the same way the Domus TDD API does not just offer a standards-conformant interface as specified in the link above, but also allows for flexible and scalable deployment, and has the possibility for extensions to carry out a bit more than just store & retrieve operations. +## Install from pypi + +``` +pip install domus-tdd-api +domus-tdd-api run +``` + ## Configuration The TDD API can be configured using two methods. @@ -26,7 +33,7 @@ The TDD API can be configured using two methods. export TDD__CHECK_SCHEMA=True ``` -2. Editing the `config.toml` file using the direct name of the variable +2. Create and edit a `config.toml` file at the root of the folder where you run the API using the direct name of the variable ``` SPARQLENDPOINT_URL="http://my-new-sparql.endpoint/address" @@ -55,9 +62,6 @@ The `config.toml` file can also be used to define FLask server configuration (c. | [TDD__]MANDATE_TTL | False | Boolean value, if set to True, it will only upload TDs having a time-to-live (ttl) value. The server will send a 400 HTTP code if the TD does not contain one. | | [TDD__]LIMIT_BATCH_TDS | 25 | Default limit of returned TDs by batch (used for pagination) | | [TDD__]ENDPOINT_TYPE | None | Special configuration to workaround SPARQL endpoints which do not follow the SPARQL standard. Possible values: `GRAPHDB` or `VIRTUOSO` | -| [TDD__]TD_JSONSCHEMA | ./tdd/data/td-json-schema-validation.json | The path to the file containing JSON-Schema to validate the TDs | -| [TDD__]TD_ONTOLOGY | ./tdd/data/td.ttl | The path to the file containing the TD OWL Ontology (only used for SHACL validation) | -| [TDD__]TD_SHACL_VALIDATOR | ./tdd/data/td-validation.ttl | The path to the file containing the SHACL shapes (only used for SHACL validation) | | [TDD__]PERIOD_CLEAR_EXPIRE_TD | 3600 | The number of seconds between each clearing of expired TDs (0 to disable clearing expired TD) | | [TDD__]OVERWRITE_DISCOVERY | False | Use custom discovery context (for offline purposes) | diff --git a/setup.py b/setup.py index d7a783d..bfc3581 100644 --- a/setup.py +++ b/setup.py @@ -66,4 +66,7 @@ "requests", ], }, + entry_points={ + "console_scripts": ["domus-tdd-api = tdd:cli"], + }, ) diff --git a/tdd/__init__.py b/tdd/__init__.py index d670e82..a13acbf 100644 --- a/tdd/__init__.py +++ b/tdd/__init__.py @@ -13,15 +13,19 @@ * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 ********************************************************************************""" +from importlib import resources import sys import time from threading import Thread +import click from flask import Flask, request, Response, stream_with_context import json +from flask.cli import FlaskGroup import json_merge_patch import httpx import toml from importlib_metadata import entry_points +from pathlib import Path from tdd.errors import ( @@ -95,7 +99,8 @@ def thread_clear_expire_td(): def create_app(): app = Flask(__name__) - app.config.from_file("../config.toml", load=toml.load) + if Path("config.toml").is_file(): + app.config.from_file("../config.toml", load=toml.load) wait_for_sparqlendpoint() register_error_handler(app) register_routes(app) @@ -127,6 +132,11 @@ def create_app(): return app +@click.group(cls=FlaskGroup, create_app=create_app) +def cli(): + """Management script for the API application.""" + + def register_error_handler(app): @app.errorhandler(AppException) def error_response(e): @@ -155,7 +165,7 @@ def add_cors_headers(response): @app.route("/", methods=["GET"]) def directory_description(): - with open("tdd/data/tdd-description.json", "r") as f: + with resources.open_text("tdd.data", "tdd-description.json") as f: tdd_description = json.loads(f.read()) tdd_description["base"] = CONFIG["TD_REPO_URL"] return Response( diff --git a/tdd/common.py b/tdd/common.py index 45bd70f..cd967fe 100644 --- a/tdd/common.py +++ b/tdd/common.py @@ -13,6 +13,7 @@ * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 ********************************************************************************""" +from importlib import resources import subprocess import json import re @@ -29,7 +30,6 @@ from tdd.metadata import insert_metadata, delete_metadata from tdd.errors import IDNotFound from tdd.config import CONFIG -from tdd.paths import LIB_PATH def get_check_schema_from_url_params(request): @@ -53,11 +53,16 @@ def delete_id(uri): def json_ld_to_ntriples(ld_content): - p = subprocess.Popen( - ["node", LIB_PATH / "transform-to-nt.js", json.dumps(ld_content)], - stdout=subprocess.PIPE, - ) - nt_content = p.stdout.read() + with resources.path("tdd.lib", "transform-to-nt.js") as transform_lib_path: + p = subprocess.Popen( + [ + "node", + transform_lib_path, + json.dumps(ld_content), + ], + stdout=subprocess.PIPE, + ) + nt_content = p.stdout.read() return nt_content.decode("utf-8") @@ -96,11 +101,17 @@ def put_rdf_in_sparql(g, uri, context, delete_if_exists, ontology, forced_type=N def frame_nt_content(id, nt_content, frame): - p = subprocess.Popen( - ["node", LIB_PATH / "frame-jsonld.js", nt_content, json.dumps(frame)], - stdout=subprocess.PIPE, - ) - json_ld_compacted = p.stdout.read() + with resources.path("tdd.lib", "frame-jsonld.js") as frame_lib_path: + p = subprocess.Popen( + [ + "node", + frame_lib_path, + nt_content, + json.dumps(frame), + ], + stdout=subprocess.PIPE, + ) + json_ld_compacted = p.stdout.read() return json_ld_compacted diff --git a/tdd/config.py b/tdd/config.py index a613d77..31895a8 100644 --- a/tdd/config.py +++ b/tdd/config.py @@ -15,15 +15,12 @@ from config import config_from_env, config_from_toml, config_from_dict from config.configuration_set import ConfigurationSet +from pathlib import Path -from tdd.paths import DATA_PATH _default_config = { "TD_REPO_URL": "http://localhost:5000", "SPARQLENDPOINT_URL": "http://127.0.0.1:3030/things", - "TD_JSONSCHEMA": DATA_PATH / "td-json-schema-validation.json", - "TD_ONTOLOGY": DATA_PATH / "td.ttl", - "TD_SHACL_VALIDATOR": DATA_PATH / "td-validation.ttl", "ENDPOINT_TYPE": None, "LIMIT_BATCH_TDS": 25, "CHECK_SCHEMA": False, @@ -33,11 +30,18 @@ "OVERWRITE_DISCOVERY": False, } -CONFIG = ConfigurationSet( - config_from_env(prefix="TDD", interpolate=True), - config_from_toml("config.toml", read_from_file=True), - config_from_dict(_default_config), -) + +if Path("config.toml").is_file(): + CONFIG = ConfigurationSet( + config_from_env(prefix="TDD", interpolate=True), + config_from_toml("config.toml", read_from_file=True), + config_from_dict(_default_config), + ) +else: + CONFIG = ConfigurationSet( + config_from_env(prefix="TDD", interpolate=True), + config_from_dict(_default_config), + ) # Remove trailing / if CONFIG["SPARQLENDPOINT_URL"][-1] == "/": diff --git a/tdd/context.py b/tdd/context.py index f23e6fc..6c16ba2 100644 --- a/tdd/context.py +++ b/tdd/context.py @@ -17,7 +17,6 @@ import json -from tdd.paths import DATA_PATH from tdd.config import CONFIG from tdd.utils import DEFAULT_THING_CONTEXT_URI, DEFAULT_DISCOVERY_CONTEXT_URI from tdd.sparql import ( @@ -26,6 +25,7 @@ GET_ALL_CONTEXTS, query, ) +from importlib import resources def convert_context_to_array(ld_content): @@ -45,7 +45,7 @@ def overwrite_thing_context(ld_content): return if type(ld_content["@context"]) not in (tuple, list): return - with open(DATA_PATH / "fixed-ctx.json") as fp: + with resources.open_text("tdd.data", "fixed-ctx.json") as fp: fixed_ctx = fp.read() try: index_wot_ctx = ld_content["@context"].index(DEFAULT_THING_CONTEXT_URI) @@ -61,7 +61,7 @@ def overwrite_discovery_context(ld_content): return if type(ld_content["@context"]) not in (tuple, list): return - with open(DATA_PATH / "fixed-discovery-ctx.json") as fp: + with resources.open_text("tdd.data", "fixed-discovery-ctx.json") as fp: fixed_discovery_ctx = fp.read() try: index_discovery_ctx = ld_content["@context"].index( diff --git a/tdd/paths.py b/tdd/paths.py index 596c9d5..e69de29 100644 --- a/tdd/paths.py +++ b/tdd/paths.py @@ -1,4 +0,0 @@ -from pathlib import Path - -DATA_PATH = Path(__file__).parent / "data" -LIB_PATH = Path(__file__).parent / "lib" diff --git a/tdd/td.py b/tdd/td.py index b01907a..7c7ee6f 100644 --- a/tdd/td.py +++ b/tdd/td.py @@ -16,6 +16,7 @@ import concurrent.futures from copy import copy from datetime import datetime +from importlib import resources import json from jsonschema import Draft7Validator import uuid @@ -71,7 +72,7 @@ ) -with open(CONFIG["TD_JSONSCHEMA"]) as fp: +with resources.open_text("tdd.data", "td-json-schema-validation.json") as fp: schema = json.load(fp) validator = Draft7Validator(schema=schema) @@ -183,10 +184,11 @@ def put_td_rdf_in_sparql( if check_schema: ontology_graph = create_binded_graph() - ontology_graph.parse(location=CONFIG["TD_ONTOLOGY"], format="turtle") - - shacl_shapes_graph = create_binded_graph() - shacl_shapes_graph.parse(location=CONFIG["TD_SHACL_VALIDATOR"], format="turtle") + with resources.path("tdd.data", "td.ttl") as onto_path: + ontology_graph.parse(location=onto_path, format="turtle") + with resources.path("tdd.data", "td-validation.ttl") as shacl_path: + shacl_shapes_graph = create_binded_graph() + shacl_shapes_graph.parse(location=shacl_path, format="turtle") conforms, graph_reports, text_reports = pyshacl.validate( g,