Skip to content

Commit

Permalink
Merge pull request #44 from ricardogsilva/add-info-api-endpoint
Browse files Browse the repository at this point in the history
Added endpoint to display current version and git commit
  • Loading branch information
francbartoli authored Apr 23, 2024
2 parents 5507459 + b0b740b commit b27a0b6
Show file tree
Hide file tree
Showing 3 changed files with 19 additions and 186 deletions.
2 changes: 2 additions & 0 deletions arpav_ppcv/webapp/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from .v2.app import create_app as create_v2_app
from .v1.app import create_app as create_v1_app
from .legacy.app import create_django_app
from .routers import router


def create_app_from_settings(settings: config.ArpavPpcvSettings) -> fastapi.FastAPI:
Expand Down Expand Up @@ -35,6 +36,7 @@ def create_app_from_settings(settings: config.ArpavPpcvSettings) -> fastapi.Fast
"email": settings.contact.email
},
)
app.include_router(router)
app.mount(settings.v1_mount_prefix, v1_app)
app.mount(settings.v2_mount_prefix, v2_app)
app.mount(settings.django_app.mount_prefix, WSGIMiddleware(django_app))
Expand Down
197 changes: 11 additions & 186 deletions arpav_ppcv/webapp/routers.py
Original file line number Diff line number Diff line change
@@ -1,195 +1,20 @@
import importlib.metadata
import logging
import urllib.parse
from typing import Annotated
import os

import httpx
from fastapi import (
APIRouter,
Depends,
HTTPException,
Request,
Response,
status,
)
from fastapi import APIRouter

from ..config import ArpavPpcvSettings
from ..schemas import models
from ..thredds import utils as thredds_utils
from ..operations import thredds as thredds_ops
from . import dependencies
from . import schemas


logger = logging.getLogger(__name__)
router = APIRouter()


@router.get("/")
async def landing_page():
...


@router.get(
"/thredds-dataset-configurations/",
response_model=models.ThreddsDatasetConfigurationList
)
async def list_thredds_dataset_configurations(
request: Request,
setttings: Annotated[ArpavPpcvSettings, Depends(dependencies.get_settings)]
):
"""List THREDDS dataset configurations.
A THREDDS dataset configuration represents a set of multiple NetCDF files that are
available in the ARPAV THREDDS server.
A dataset configuration can be used to generate ids that refer to individual
NetCDF files by constructing a string based on the `dataset_id_pattern` property.
For example, If there is a dataset configuration with the following properties:
```yaml
identifier: myds
dataset_id_pattern: {identifier}-something-{scenario}-{year_period}
allowed_values:
scenario:
- scen1
- scen2
year_period:
- winter
- autumn
```
Then the following would be valid dataset identifiers:
- `myds-something-scen1-winter`
- `myds-something-scen1-autumn`
- `myds-something-scen2-winter`
- `myds-something-scen2-autumn`
Each of these dataset identifiers could further be used to gain access to the WMS
endpoint.
"""
items = []
for ds_id, ds in thredds_ops.list_dataset_configurations(setttings).items():
items.append(
models.ThreddsDatasetConfiguration(
identifier=ds_id,
dataset_id_pattern=ds.dataset_id_pattern,
unit=ds.unit,
palette=ds.palette,
range=ds.range,
allowed_values=ds.allowed_values,
)
)
return models.ThreddsDatasetConfigurationList(
meta=models.ListMeta(
returned_records=len(items),
total_records=len(items),
total_filtered_records=len(items)
),
links=models.ListLinks(
self=str(request.url_for("list_thredds_dataset_configurations"))
),
items=items
)


@router.get(
"/thredds_dataset_configurations/{configuration_id}/dataset-ids/",
response_model=models.ThreddsDatasetConfigurationIdentifierList
)
async def list_dataset_identifiers(
request: Request,
settings: Annotated[ArpavPpcvSettings, Depends(dependencies.get_settings)],
configuration_id: str):
ds_config = settings.thredds_server.datasets[configuration_id]
items = thredds_ops.list_dataset_identifiers(configuration_id, ds_config)
return models.ThreddsDatasetConfigurationIdentifierList(
meta=models.ListMeta(
returned_records=len(items),
total_records=len(items),
total_filtered_records=len(items),
),
links=models.ListLinks(
self=str(
request.url_for(
"list_dataset_identifiers",
configuration_id=configuration_id
)
)
),
items=items
)


@router.get("/wms/{dataset_id}")
async def wms_endpoint(
request: Request,
settings: Annotated[ArpavPpcvSettings, Depends(dependencies.get_settings)],
http_client: Annotated[httpx.AsyncClient, Depends(dependencies.get_http_client)],
dataset_id: str,
version: str = "1.3.0",
):
"""Serve dataset via OGC Web Map Service.
Pass additional relevant WMS query parameters directly to this endpoint.
"""
ds_config_id = dataset_id.partition("-")[0]
logger.debug(f"{settings=}")
try:
ds_config = settings.thredds_server.datasets[ds_config_id]
except KeyError as err:
raise HTTPException(status_code=400, detail="Invalid dataset_id") from err
else:
id_parameters = ds_config.validate_dataset_id(dataset_id)
parsed_id_parameters = {
k: thredds_utils.get_parameter_internal_value(k, v)
for k, v in id_parameters.items()
}
logger.debug(f"{parsed_id_parameters=}")
base_wms_url = thredds_utils.build_dataset_service_url(
ds_config_id,
parsed_id_parameters,
url_path_pattern=ds_config.thredds_url_pattern,
thredds_base_url=settings.thredds_server.base_url,
service_url_fragment=settings.thredds_server.wms_service_url_fragment
)
parsed_url = urllib.parse.urlparse(base_wms_url)
logger.info(f"{base_wms_url=}")
query_params = {k.lower(): v for k, v in request.query_params.items()}
if query_params.get("request") in ("GetMap", "GetLegendGraphic"):
query_params = thredds_utils.tweak_wms_get_map_request(
query_params,
ds_config,
settings.thredds_server.uncertainty_visualization_scale_range
)
logger.debug(f"{query_params=}")
wms_url = parsed_url._replace(
query=urllib.parse.urlencode(
{
**query_params,
"service": "WMS",
"version": version,
}
)
).geturl()
logger.info(f"{wms_url=}")
try:
wms_response = await thredds_utils.proxy_request(wms_url, http_client)
except httpx.HTTPStatusError as err:
logger.exception(msg=f"THREDDS server replied with an error: {err.response.text}")
raise HTTPException(
status_code=status.HTTP_502_BAD_GATEWAY,
detail=err.response.text
)
except httpx.HTTPError as err:
logger.exception(msg=f"THREDDS server replied with an error")
raise HTTPException(
status_code=status.HTTP_502_BAD_GATEWAY,
) from err
else:
response = Response(
content=wms_response.content,
status_code=wms_response.status_code,
headers=dict(wms_response.headers)
)
return response
@router.get("/", response_model=schemas.AppInformation)
async def get_app_info():
"""Return information about the ARPAV-PPCV application."""
return {
"version": importlib.metadata.version("arpav_ppcv_backend"),
"git_commit": os.getenv("GIT_COMMIT", "unknown"),
}
6 changes: 6 additions & 0 deletions arpav_ppcv/webapp/schemas.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import pydantic


class AppInformation(pydantic.BaseModel):
version: str
git_commit: str

0 comments on commit b27a0b6

Please sign in to comment.