Skip to content
This repository was archived by the owner on Aug 20, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 9 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
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,4 @@ ENV PYTHONPATH "${PYTHONPATH}:/app:/app/dependencies"
# Change user
USER microdata

CMD ["/app/dependencies/bin/gunicorn", "--logger-class", "metadata_service.config.gunicorn.CustomLogger", "metadata_service.app:app", "--workers", "2", "--limit-request-line", "8190"]
CMD ["/app/dependencies/bin/gunicorn", "-k", "uvicorn.workers.UvicornWorker", "--logger-class", "metadata_service.config.gunicorn.CustomLogger", "metadata_service.app:app", "--workers", "2", "--limit-request-line", "8190"]
8 changes: 4 additions & 4 deletions metadata_service/adapter/datastore.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,16 @@ def get_datastore_versions() -> dict:
return json.load(f)


def _get_draft_metadata_all():
def _get_draft_metadata_all() -> dict:
metadata_all_file_path = (
f"{DATASTORE_ROOT_DIR}/datastore/metadata_all__DRAFT.json"
)
with open(metadata_all_file_path, "r", encoding="utf-8") as f:
return json.load(f)


@lru_cache(maxsize=32)
def _get_versioned_metadata_all(version: Version):
@lru_cache
def _get_versioned_metadata_all(version: Version) -> dict:
file_version = version.to_3_underscored()
metadata_all_file_path = (
f"{DATASTORE_ROOT_DIR}/datastore/metadata_all__{file_version}.json"
Expand All @@ -43,7 +43,7 @@ def _get_versioned_metadata_all(version: Version):
return json.load(f)


def get_metadata_all(version: Version) -> str:
def get_metadata_all(version: Version) -> dict:
try:
if version.is_draft():
return _get_draft_metadata_all()
Expand Down
82 changes: 31 additions & 51 deletions metadata_service/api/metadata_api.py
Original file line number Diff line number Diff line change
@@ -1,100 +1,80 @@
import logging

from flask import Blueprint, jsonify, request
from fastapi import APIRouter, Depends

from metadata_service.api.request_models import NameParam, MetadataQuery
from metadata_service.api.response_models import metadata_response
from metadata_service.domain import metadata
from metadata_service.domain.version import get_version_from_string

logger = logging.getLogger()
metadata_api = Blueprint("metadata_api", __name__)
metadata_router = APIRouter()


@metadata_api.get("/metadata/data-store")
def get_data_store():
@metadata_router.get("/metadata/data-store")
def get_data_store(response=Depends(metadata_response)):
logger.info("GET /metadata/data-store")
return response(metadata.find_all_datastore_versions())

response = jsonify(metadata.find_all_datastore_versions())
response.headers.set("content-language", "no")
return response


@metadata_api.get("/metadata/data-structures/status")
def get_data_structure_current_status():
validated_query = NameParam(**request.args)
@metadata_router.get("/metadata/data-structures/status")
def get_data_structure_current_status(validated_query: NameParam = Depends()):
logger.info(
f"GET /metadata/data-structures/status with name = {validated_query.names}"
)
response = jsonify(
metadata.find_current_data_structure_status(
validated_query.get_names_as_list()
)
return metadata.find_current_data_structure_status(
validated_query.get_names_as_list()
)
response.headers.set("content-language", "no")
return response


@metadata_api.post("/metadata/data-structures/status")
def get_data_structure_current_status_as_post():
validated_body = NameParam(**request.json)

@metadata_router.post("/metadata/data-structures/status")
def get_data_structure_current_status_as_post(validated_body: NameParam):
logger.info(
f"POST /metadata/data-structures/status with name = {validated_body.names}"
)
response = jsonify(
metadata.find_current_data_structure_status(
validated_body.get_names_as_list()
)
return metadata.find_current_data_structure_status(
validated_body.get_names_as_list()
)
response.headers.set("content-language", "no")
return response


@metadata_api.get("/metadata/data-structures")
def get_data_structures():
validated_query = MetadataQuery(**request.args)
@metadata_router.get("/metadata/data-structures")
def get_data_structures(
validated_query: MetadataQuery = Depends(),
response=Depends(metadata_response),
):
validated_query.include_attributes = True
logger.info(f"GET /metadata/data-structures with query: {validated_query}")

response = jsonify(
return response(
metadata.find_data_structures(
validated_query.names,
validated_query.names_as_list(),
get_version_from_string(validated_query.version),
validated_query.include_attributes,
validated_query.skip_code_lists,
)
)
response.headers.set("content-language", "no")
return response


@metadata_api.get("/metadata/all-data-structures")
@metadata_router.get("/metadata/all-data-structures")
def get_all_data_structures_ever():
logger.info("GET /metadata/all-data-structures")

response = jsonify(metadata.find_all_data_structures_ever())
response.headers.set("content-language", "no")
return response
return metadata.find_all_data_structures_ever()


@metadata_api.get("/metadata/all")
def get_all_metadata():
validated_query = MetadataQuery(**request.args)
@metadata_router.get("/metadata/all")
def get_all_metadata(
validated_query: MetadataQuery = Depends(),
respond=Depends(metadata_response),
):
logger.info(f"GET /metadata/all with version: {validated_query.version}")

response = jsonify(
return respond(
metadata.find_all_metadata(
get_version_from_string(validated_query.version),
validated_query.skip_code_lists,
)
)
response.headers.set("content-language", "no")
return response


@metadata_api.get("/languages")
@metadata_router.get("/languages")
def get_languages():
logger.info("GET /languages")

response = jsonify(metadata.find_languages())
return response
return metadata.find_languages()
12 changes: 6 additions & 6 deletions metadata_service/api/observability.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
from flask import Blueprint
from fastapi import APIRouter


observability = Blueprint("observability", __name__)
observability_router = APIRouter()


@observability.get("/health/alive")
def alive():
@observability_router.get("/health/alive")
async def alive():
return "I'm alive!"


@observability.get("/health/ready")
def ready():
@observability_router.get("/health/ready")
async def ready():
return "I'm ready!"
17 changes: 4 additions & 13 deletions metadata_service/api/request_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,11 @@


class MetadataQuery(BaseModel, extra="forbid", validate_assignment=True):
names: List[str] = []
names: str | None = None
version: str
include_attributes: bool = False
skip_code_lists: bool = False

@field_validator("names", mode="before")
@classmethod
def split_str(cls, names):
if isinstance(names, List):
return names[0].split(",")
elif isinstance(names, str):
return names.split(",")
else:
raise RequestValidationException(
"names field must be a list or a string"
)

@field_validator("version", mode="before")
@classmethod
def validate_version(cls, version: str):
Expand All @@ -38,6 +26,9 @@ def validate_version(cls, version: str):
)
return version

def names_as_list(self) -> List[str]:
return [] if self.names is None else self.names.split(",")


class NameParam(BaseModel, extra="forbid"):
names: str
Expand Down
30 changes: 30 additions & 0 deletions metadata_service/api/response_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from collections.abc import Callable
from typing import Any
from fastapi import Request
from fastapi.responses import Response, JSONResponse
import msgpack


class MsgPackResponse(Response):
media_type = "application/x-msgpack"

def render(self, content) -> bytes:
compressed_response = msgpack.packb(content, use_bin_type=True)
return compressed_response or b""


def metadata_response(
request: Request,
) -> Callable[[Any], JSONResponse | MsgPackResponse]:
"""
Returns a response function that compresses the response
if the accept header in the request is applicaion/x-msgpack
"""

def respond(content):
accept = request.headers.get("accept", "")
if "application/x-msgpack" in accept:
return MsgPackResponse(content)
return JSONResponse(content)

return respond
Loading