Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions .github/workflows/validate_config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: Validate canton configuration

# Guard rail: the cantonal geoservice configuration lives in per-canton YAML
# files (src/drillapi/cantons_configuration/data/*.yaml). A malformed config
# would break the running app, so this workflow validates every file against
# the Pydantic schema on every push and PR. A failure here blocks the merge /
# release before any deploy.

on:
push:
branches:
- '**'
pull_request:
branches:
- '**'

jobs:
validate-config:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.14]

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v4

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}

- name: Cache uv
uses: actions/cache@v4
with:
path: ~/.cache/uv
key: uv-${{ runner.os }}-${{ hashFiles('uv.lock') }}

- name: Install dependencies
run: uv sync --extra dev

# Fast fail-fast load: validates every YAML file against the schema and
# exits non-zero with an aggregated error listing every problem found.
- name: Validate cantonal YAML config loads
run: |
uv run python -c "from drillapi.cantons_configuration.loader import load_cantons; c = load_cantons(); print(f'OK: {len(c)} canton configuration(s) validated')"

# Full structural test suite for the configuration (schema invariants,
# ESRI id presence, name/filename agreement, fail-fast behaviour).
- name: Run configuration test suite
run: uv run python -m pytest tests/test_configuration_structure.py -v
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,34 @@ Run project
uv run python -m drillapi
```

## Cantonal configuration

The per-canton geoservice configuration lives as one YAML file per canton under
`src/drillapi/cantons_configuration/data/<CODE>.yaml` (e.g. `ZH.yaml`). Each file
is validated against a Pydantic schema (`cantons_configuration/schema.py`) when
the app starts, so an invalid config (unknown/typo'd key, missing field, wrong
type, filename/`name` mismatch) fails fast at startup — and in CI via the
`Validate canton configuration` workflow — rather than mid-request against a
live geoservice.

To add or change a canton, edit the relevant `data/<CODE>.yaml` file. The
filename (minus extension) must match the canton's `name` field.

### Hot-reload the config in dev

The config is loaded and cached once at startup, so a plain `--reload` server
(which watches only `*.py`) will **not** pick up YAML edits. For local work on
cantonal data, start the server watching the YAML files too (requires the dev
dependencies, which include `watchfiles`):

```bash
uv run uvicorn drillapi.app:app --app-dir src --reload --reload-dir src/drillapi --reload-include "*.yaml"
```

Editing any `data/*.yaml` then restarts and re-validates the config
automatically. An invalid edit surfaces the validation error in the server log
and the app will not come back up until it is fixed.

## Explore

OpenAPI doc
Expand Down
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ dependencies = [
"mangum>=0.22.0",
"owslib>=0.36.0",
"pydantic-settings>=2.15.0",
"pyyaml>=6.0.3",
"slowapi>=0.1.10",
"uvicorn>=0.54.0",
]
Expand All @@ -26,6 +27,7 @@ dev = [
"respx",
"pytest-asyncio",
"ruff>=0.16.10",
"watchfiles>=1.3.0",
]

[tool.coverage.run]
Expand Down
Loading
Loading