Skip to content

Commit bb0c9e1

Browse files
committed
feat: add version metadata to postal_patterns.json (#34)
Add _meta block (version, date) to postal_patterns.json and surface patterns_version in /health endpoint. Bump to v0.14.0.
1 parent 1f82f46 commit bb0c9e1

6 files changed

Lines changed: 15 additions & 3 deletions

File tree

app/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "0.13.0"
1+
__version__ = "0.14.0"

app/main.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
normalize_country,
3333
)
3434
from app.models import ErrorResponse, HealthResponse, NUTSResult, PatternResponse
35-
from app.postal_patterns import POSTAL_PATTERNS
35+
from app.postal_patterns import PATTERNS_META, POSTAL_PATTERNS
3636

3737
logging.basicConfig(
3838
level=logging.INFO,
@@ -266,6 +266,7 @@ def health(response: Response):
266266
total_nuts_names=len(get_nuts_names()),
267267
nuts_version=settings.nuts_version,
268268
extra_sources=get_extra_source_count(),
269+
patterns_version=PATTERNS_META.get("version", "unknown"),
269270
data_stale=stale,
270271
last_updated=get_data_loaded_at(),
271272
)

app/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class HealthResponse(BaseModel):
3737
nuts_version: str
3838
total_nuts_names: int = Field(default=0, description="Number of NUTS region names loaded")
3939
extra_sources: int = Field(default=0, description="Number of extra ZIP source URLs configured")
40+
patterns_version: str = Field(description="Version of the postal_patterns.json file")
4041
data_stale: bool = Field(description="True if serving expired cache after a failed TERCET refresh")
4142
last_updated: str = Field(
4243
description="ISO 8601 timestamp of when TERCET data was last successfully loaded"

app/postal_patterns.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
2+
"_meta": { "version": "1.0", "date": "2026-03-03" },
23
"AT": {
34
"regex": "^(?:A[\\s\\-\u2013\u2014.]*|AT[\\s\\-\u2013\u2014.]*)?([0-9]{4})$",
45
"example": "1010, A-1010, AT-1010, A 1010, AT 1010",

app/postal_patterns.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,13 @@
3030
# Regexes are applied after .strip().upper() and are case-insensitive.
3131
_patterns_path = Path(__file__).parent / "postal_patterns.json"
3232
try:
33-
POSTAL_PATTERNS: dict[str, dict] = json.loads(_patterns_path.read_text())
33+
_raw: dict[str, dict] = json.loads(_patterns_path.read_text())
3434
except (json.JSONDecodeError, OSError) as _exc:
3535
raise SystemExit(f"Fatal: failed to load {_patterns_path}: {_exc}") from _exc
3636

37+
PATTERNS_META: dict[str, str] = _raw.pop("_meta", {})
38+
POSTAL_PATTERNS: dict[str, dict] = _raw
39+
3740
# Pre-compile all patterns for performance
3841
_COMPILED: dict[str, re.Pattern] = {
3942
cc: re.compile(pat["regex"], re.IGNORECASE) for cc, pat in POSTAL_PATTERNS.items()

tests/test_api.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,12 @@ def test_includes_estimates(self, client):
9797
assert "total_estimates" in data
9898
assert data["total_estimates"] >= 0
9999

100+
def test_includes_patterns_version(self, client):
101+
resp = client.get("/health")
102+
data = resp.json()
103+
assert "patterns_version" in data
104+
assert data["patterns_version"] == "1.0"
105+
100106
def test_includes_nuts_names(self, client):
101107
resp = client.get("/health")
102108
data = resp.json()

0 commit comments

Comments
 (0)