Skip to content
Snippets Groups Projects
Commit 527c51a7 authored by Stefan Hynek's avatar Stefan Hynek :drooling_face:
Browse files

Merge branch '36-make-host-port-dsn-and-textgrid-host-configurable' into 'main'

Resolve "make host, port, dsn and textgrid host configurable"

Closes #36

See merge request !24
parents fd08709a 804d5130
No related branches found
No related tags found
1 merge request!24Resolve "make host, port, dsn and textgrid host configurable"
Pipeline #342960 passed
...@@ -11,14 +11,11 @@ services: ...@@ -11,14 +11,11 @@ services:
build: build:
context: . context: .
ports: ports:
- ${port:-8080}:${port:-8080} - ${REPDAV_PORT:-8080}:${REPDAV_PORT:-8080}
environment: environment:
host: ${host:-0.0.0.0} REPDAV_HOST: ${REPDAV_HOST:-0.0.0.0}
port: ${port:-8080} REPDAV_PORT: ${REPDAV_PORT:-8080}
sentry_dsn: ${sentry_dsn} SENTRY_DSN: ${SENTRY_DSN}
tg_auth_wsdl: "https://textgridlab.org/1.0/tgauth/wsdl/tgextra.wsdl" TEXTGRID_HOST: ${TEXTGRID_HOST}
tg_auth_address: "https://textgridlab.org/1.0/tgauth/tgextra.php"
tg_nav_address: "https://textgridlab.org/1.0/tgsearch/navigation/"
tg_host: "https://textgridlab.org/"
volumes: volumes:
- $PWD/src:/app - $PWD/src:/app
...@@ -4,8 +4,6 @@ ...@@ -4,8 +4,6 @@
"""Configure and start the WSGI DAV Server.""" """Configure and start the WSGI DAV Server."""
import logging import logging
import os
import sentry_sdk import sentry_sdk
from cheroot import wsgi from cheroot import wsgi
from wsgidav.wsgidav_app import WsgiDAVApp from wsgidav.wsgidav_app import WsgiDAVApp
...@@ -14,13 +12,11 @@ from repdav.config import AppConfig ...@@ -14,13 +12,11 @@ from repdav.config import AppConfig
app_config = AppConfig() app_config = AppConfig()
if app_config.dsn: if app_config.dsn:
sentry_sdk.init( sentry_sdk.init(app_config.dsn, traces_sample_rate=1.0)
app_config.dsn,
traces_sample_rate=1.0
)
logging.basicConfig(level=logging.DEBUG, logging.basicConfig(
format='%(name)s %(levelname)s %(asctime)s %(message)s') level=logging.DEBUG, format="%(name)s %(levelname)s %(asctime)s %(message)s"
)
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
_logger.propagate = True _logger.propagate = True
...@@ -31,12 +27,13 @@ tmp_logger.setLevel(logging.DEBUG) ...@@ -31,12 +27,13 @@ tmp_logger.setLevel(logging.DEBUG)
# Configuration of the WsgiDAVApp. # Configuration of the WsgiDAVApp.
# https://wsgidav.readthedocs.io/en/latest/user_guide_configure.html # https://wsgidav.readthedocs.io/en/latest/user_guide_configure.html
# TODO: move to config.py
config = { config = {
"host": os.getenv("host") or "localhost", "host": app_config.host,
"port": int(os.getenv("port") or "8080"), "port": app_config.port,
"provider_mapping": { "provider_mapping": {
"/": {"class": "repdav.textgrid_named_dav_provider.TextgridNamedResourceProvider"}, "/": {
"class": "repdav.textgrid_named_dav_provider.TextgridNamedResourceProvider"
},
}, },
"verbose": 4, "verbose": 4,
"http_authenticator": { "http_authenticator": {
...@@ -47,7 +44,8 @@ config = { ...@@ -47,7 +44,8 @@ config = {
"default_to_digest": False, "default_to_digest": False,
# Name of a header field that will be accepted as authorized user # Name of a header field that will be accepted as authorized user
"trusted_auth_header": None, "trusted_auth_header": None,
} },
"tg_host": app_config.tg_host,
} }
app = WsgiDAVApp(config) app = WsgiDAVApp(config)
......
...@@ -3,63 +3,51 @@ ...@@ -3,63 +3,51 @@
# SPDX-License-Identifier: CC0-1.0 # SPDX-License-Identifier: CC0-1.0
import os import os
from typing import Optional
from urllib.parse import urlparse
from .errors import EnvNotSetError
# TODO: remove "tg_auth_wsdl", "tg_auth_address", "tg_nav_address" def check_url(url):
"""Check if `url` can be parsed."""
if url:
result = urlparse(url)
if result.scheme and result.netloc:
return url
raise ValueError(f"{url} is not a valid URL.")
def lookup_env_name(internal_name: str) -> str: def check_port(port):
mapping = { """Check if port is set correctly to a non-privileged value."""
"_auth_wsdl": "tg_auth_wsdl", if 1000 <= port <= 65535:
"_auth_address": "tg_auth_address", return port
"_dsn": "sentry_dsn", raise ValueError(f"{port} is not a valid Port.")
"_host": "tg_host",
"_nav_address": "tg_nav_address",
}
return mapping[internal_name]
# TODO check for trailing "/", add if missing!
# TODO check URLs for validity
class AppConfig: class AppConfig:
# TODO: configure the app according to the set environment (i.e. prod or dev) """Settings that can be be configured from the environment."""
def __init__(self) -> None:
self._dsn = os.getenv(lookup_env_name("_dsn"))
@property
def dsn(self) -> str:
return self._dsn
class TextgridConfig:
def __init__(self) -> None: def __init__(self) -> None:
self._auth_wsdl = os.getenv(lookup_env_name("_auth_wsdl")) self._dsn = check_url(os.getenv("SENTRY_DSN"))
self._auth_address = os.getenv(lookup_env_name("_auth_address")) self._host = os.getenv("REPDAV_HOST") or "localhost"
self._nav_address = os.getenv(lookup_env_name("_nav_address")) self._port = check_port(int(os.getenv("REPDAV_PORT") or 8080))
self._host = os.getenv(lookup_env_name("_host")) self._tg_host = check_url(os.getenv("TEXTGRID_HOST"))
@property @property
def auth_wsdl(self) -> str: def dsn(self) -> Optional[str]:
if self._auth_wsdl: """Sentry DSN."""
return self._auth_wsdl return self._dsn
raise EnvNotSetError(lookup_env_name("_auth_wsdl"))
@property @property
def auth_address(self) -> str: def host(self) -> str:
if self._auth_address: """Service hostname."""
return self._auth_address return self._host
raise EnvNotSetError(lookup_env_name("_auth_address"))
@property @property
def nav_address(self) -> str: def port(self) -> int:
if self._nav_address: """Service port."""
return self._nav_address return self._port
raise EnvNotSetError(lookup_env_name("_nav_address"))
@property @property
def host(self) -> str: def tg_host(self) -> Optional[str]:
if self._host: """Textgrid host URL."""
return self._host return self._tg_host
raise EnvNotSetError(lookup_env_name("_host"))
...@@ -21,6 +21,16 @@ from repdav.stream_tools import FileLikeQueue ...@@ -21,6 +21,16 @@ from repdav.stream_tools import FileLikeQueue
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
def tg_config(environ):
"""Initialize Textgrid configuration either with or without host setting. This
works around an [issue with tgclients](https://gitlab.gwdg.de/dariah-de/textgridrep/textgrid-python-clients/-/issues/59).
"""
tg_host = environ["wsgidav.config"]["tg_host"]
if tg_host:
return TextgridConfig(tg_host)
return TextgridConfig()
class TextgridRoot(DAVCollection): class TextgridRoot(DAVCollection):
"""Top level collection that incorporates Textgrid projects. """Top level collection that incorporates Textgrid projects.
...@@ -30,8 +40,9 @@ class TextgridRoot(DAVCollection): ...@@ -30,8 +40,9 @@ class TextgridRoot(DAVCollection):
def __init__(self, path, environ): def __init__(self, path, environ):
DAVCollection.__init__(self, path, environ) DAVCollection.__init__(self, path, environ)
self._sid = environ["wsgidav.auth.user_name"] self._sid = environ["wsgidav.auth.user_name"]
config = TextgridConfig() config = tg_config(environ)
self._auth = TextgridAuth(config) self._auth = TextgridAuth(config)
self.projects = ()
def get_display_info(self): def get_display_info(self):
return {"type": "Textgrid root collection"} return {"type": "Textgrid root collection"}
...@@ -83,7 +94,7 @@ class TextgridProject(DAVCollection): ...@@ -83,7 +94,7 @@ class TextgridProject(DAVCollection):
_logger.debug("Called TextgridProject.__init__(self, %s, environ).", path) _logger.debug("Called TextgridProject.__init__(self, %s, environ).", path)
DAVCollection.__init__(self, path, environ) DAVCollection.__init__(self, path, environ)
self._sid = environ["wsgidav.auth.user_name"] self._sid = environ["wsgidav.auth.user_name"]
config = TextgridConfig() config = tg_config(environ)
self._tgsearch = TextgridSearch(config.search) self._tgsearch = TextgridSearch(config.search)
self._project_id = self.path.split("/")[-1] self._project_id = self.path.split("/")[-1]
...@@ -175,7 +186,7 @@ class TextgridAggregation(DAVCollection): ...@@ -175,7 +186,7 @@ class TextgridAggregation(DAVCollection):
DAVCollection.__init__(self, path, environ) DAVCollection.__init__(self, path, environ)
self._sid = environ["wsgidav.auth.user_name"] self._sid = environ["wsgidav.auth.user_name"]
self._info = info self._info = info
config = TextgridConfig() config = tg_config(environ)
self._tgsearch = TextgridSearch(config.search) self._tgsearch = TextgridSearch(config.search)
self._tguri = self.path.split("/")[-1] self._tguri = self.path.split("/")[-1]
...@@ -261,7 +272,7 @@ class TextgridResource(DAVNonCollection): ...@@ -261,7 +272,7 @@ class TextgridResource(DAVNonCollection):
self._tguri = self.path.split("/")[-1] self._tguri = self.path.split("/")[-1]
self._info = info self._info = info
self.upload_thread = None self.upload_thread = None
config = TextgridConfig() config = tg_config(environ)
self._crud = TextgridCRUD(config.crud) self._crud = TextgridCRUD(config.crud)
def get_content_length(self): def get_content_length(self):
...@@ -273,7 +284,9 @@ class TextgridResource(DAVNonCollection): ...@@ -273,7 +284,9 @@ class TextgridResource(DAVNonCollection):
return self._info[self.name]["format"] return self._info[self.name]["format"]
def get_content(self): def get_content(self):
_logger.debug("Called TextgridResource.get_content(self) with path: %s", self.path) _logger.debug(
"Called TextgridResource.get_content(self) with path: %s", self.path
)
return io.BytesIO(self._crud.read_data(self._tguri, self._sid).content) return io.BytesIO(self._crud.read_data(self._tguri, self._sid).content)
def get_content_title(self): def get_content_title(self):
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment