updated database path logic
This commit is contained in:
@@ -39,6 +39,7 @@ The app is served below `/fatima` by default. Configure the external path in
|
|||||||
flask:
|
flask:
|
||||||
url_prefix: "/fatima"
|
url_prefix: "/fatima"
|
||||||
log_file: "/tmp/fatima.log"
|
log_file: "/tmp/fatima.log"
|
||||||
|
database: "instance/calendar.sqlite"
|
||||||
```
|
```
|
||||||
|
|
||||||
Set `URL_PREFIX` in the environment to override this value at deployment time.
|
Set `URL_PREFIX` in the environment to override this value at deployment time.
|
||||||
@@ -83,6 +84,11 @@ For deployments, `APP_CONFIG` can point to a YAML file elsewhere. `SECRET_KEY`,
|
|||||||
override the corresponding values. `DATABASE` should be an absolute path to the
|
override the corresponding values. `DATABASE` should be an absolute path to the
|
||||||
SQLite file used in production.
|
SQLite file used in production.
|
||||||
|
|
||||||
|
The YAML `flask.database` path is resolved relative to the directory containing
|
||||||
|
the selected config file. If it is omitted, the app uses
|
||||||
|
`instance/calendar.sqlite` beside `flask_fatima.py`; it no longer relies on
|
||||||
|
Flask's environment-dependent inferred instance directory.
|
||||||
|
|
||||||
At startup, the app prints a database summary to the Waitress log containing the
|
At startup, the app prints a database summary to the Waitress log containing the
|
||||||
resolved path, amount and status totals, distinct recorded-date total, and date
|
resolved path, amount and status totals, distinct recorded-date total, and date
|
||||||
range. Compare that path and those totals with the importer's output to confirm
|
range. Compare that path and those totals with the importer's output to confirm
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ flask:
|
|||||||
url_prefix: "/fatima"
|
url_prefix: "/fatima"
|
||||||
# Startup database summary log.
|
# Startup database summary log.
|
||||||
log_file: "/tmp/fatima.log"
|
log_file: "/tmp/fatima.log"
|
||||||
|
# Relative paths are resolved from the directory containing this config file.
|
||||||
|
database: "instance/calendar.sqlite"
|
||||||
|
|
||||||
auth:
|
auth:
|
||||||
username: "your-username"
|
username: "your-username"
|
||||||
|
|||||||
+13
-2
@@ -39,7 +39,7 @@ class UrlPrefixMiddleware:
|
|||||||
|
|
||||||
|
|
||||||
def load_yaml_config(path):
|
def load_yaml_config(path):
|
||||||
config_path = Path(path)
|
config_path = Path(path).expanduser().resolve()
|
||||||
if not config_path.is_file():
|
if not config_path.is_file():
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
f"Configuration file not found: {config_path}. "
|
f"Configuration file not found: {config_path}. "
|
||||||
@@ -59,6 +59,14 @@ def load_yaml_config(path):
|
|||||||
"URL_PREFIX": normalize_url_prefix(flask_config.get("url_prefix", "/fatima")),
|
"URL_PREFIX": normalize_url_prefix(flask_config.get("url_prefix", "/fatima")),
|
||||||
"LOG_FILE": flask_config.get("log_file", "/tmp/fatima.log"),
|
"LOG_FILE": flask_config.get("log_file", "/tmp/fatima.log"),
|
||||||
}
|
}
|
||||||
|
configured_database = flask_config.get("database")
|
||||||
|
if configured_database is not None:
|
||||||
|
if not isinstance(configured_database, str) or not configured_database.strip():
|
||||||
|
raise RuntimeError(f"{config_path}: flask.database must be a non-empty path")
|
||||||
|
database_path = Path(configured_database).expanduser()
|
||||||
|
if not database_path.is_absolute():
|
||||||
|
database_path = config_path.parent / database_path
|
||||||
|
required["DATABASE"] = str(database_path.resolve())
|
||||||
missing = [
|
missing = [
|
||||||
name for name, value in required.items()
|
name for name, value in required.items()
|
||||||
if name != "URL_PREFIX" and (not isinstance(value, str) or not value)
|
if name != "URL_PREFIX" and (not isinstance(value, str) or not value)
|
||||||
@@ -119,7 +127,10 @@ def create_app(test_config=None):
|
|||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
config_path = os.environ.get("APP_CONFIG", Path(app.root_path) / "config.yaml")
|
config_path = os.environ.get("APP_CONFIG", Path(app.root_path) / "config.yaml")
|
||||||
app.config.from_mapping(load_yaml_config(config_path))
|
app.config.from_mapping(load_yaml_config(config_path))
|
||||||
app.config.setdefault("DATABASE", str(Path(app.instance_path) / "calendar.sqlite"))
|
app.config.setdefault(
|
||||||
|
"DATABASE",
|
||||||
|
str(Path(__file__).resolve().parent / "instance" / "calendar.sqlite"),
|
||||||
|
)
|
||||||
|
|
||||||
# Environment variables remain useful when deploying without a local file.
|
# Environment variables remain useful when deploying without a local file.
|
||||||
for name in ("SECRET_KEY", "APP_USERNAME", "APP_PASSWORD", "DATABASE", "LOG_FILE"):
|
for name in ("SECRET_KEY", "APP_USERNAME", "APP_PASSWORD", "DATABASE", "LOG_FILE"):
|
||||||
|
|||||||
+22
-1
@@ -1,7 +1,7 @@
|
|||||||
import tempfile
|
import tempfile
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from flask_fatima import create_app, log_database_summary
|
from flask_fatima import create_app, load_yaml_config, log_database_summary
|
||||||
|
|
||||||
URL_PREFIX = "/fatima"
|
URL_PREFIX = "/fatima"
|
||||||
|
|
||||||
@@ -44,6 +44,27 @@ def test_database_summary_is_appended_to_log(capsys):
|
|||||||
assert "Fatima database:" in capsys.readouterr().out
|
assert "Fatima database:" in capsys.readouterr().out
|
||||||
|
|
||||||
|
|
||||||
|
def test_database_path_is_resolved_relative_to_config_file():
|
||||||
|
with tempfile.TemporaryDirectory() as directory:
|
||||||
|
config_path = Path(directory) / "production.yaml"
|
||||||
|
config_path.write_text(
|
||||||
|
"""
|
||||||
|
flask:
|
||||||
|
secret_key: test-secret
|
||||||
|
database: data/calendar.sqlite
|
||||||
|
auth:
|
||||||
|
username: user
|
||||||
|
password: pass
|
||||||
|
""",
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
|
||||||
|
config = load_yaml_config(config_path)
|
||||||
|
assert config["DATABASE"] == str(
|
||||||
|
(Path(directory) / "data" / "calendar.sqlite").resolve()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_prefix_applies_to_generated_links_and_unprefixed_routes_are_rejected():
|
def test_prefix_applies_to_generated_links_and_unprefixed_routes_are_rejected():
|
||||||
test_client = client()
|
test_client = client()
|
||||||
test_client.post(url("/login"), data={"username": "user", "password": "pass"})
|
test_client.post(url("/login"), data={"username": "user", "password": "pass"})
|
||||||
|
|||||||
Reference in New Issue
Block a user