Source code for fmn.api.handlers.misc
# SPDX-FileCopyrightText: Contributors to the Fedora Project
#
# SPDX-License-Identifier: MIT
import logging
from functools import cache
from importlib import metadata
from itertools import chain
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy_helpers import DatabaseStatus
from ...backends import PagureAsyncProxy, get_distgit_proxy
from ...core.constants import ArtifactType
from .. import api_models
from ..database import gen_db_manager
log = logging.getLogger(__name__)
router = APIRouter()
[docs]
@router.get("/applications", response_model=list[str], tags=["misc"])
@cache
def get_applications():
entrypoints = metadata.entry_points().select(group="fedora.messages")
# dictionary of normalized, lower case application name to pristine name
applications = {}
for ep in entrypoints:
msg_cls = ep.load()
try:
app_name = msg_cls.app_name.fget(None)
if app_name is None:
raise ValueError("app_name is None")
except Exception:
# Sometimes the schema hasn't set the app_name. Fallback on the entry point name.
app_name = ep.name.partition(".")[0]
app_name_lower = app_name.lower()
if (
# we will always have the base message in there, so lets discard that
app_name_lower != "base"
# and prefer variations of names with more leading capital characters
and (app_name_lower not in applications or app_name < applications[app_name_lower])
):
applications[app_name_lower] = app_name
# return list sorted by lowercase name, but with case left intact
return [item[1] for item in sorted(applications.items(), key=lambda item: item[0])]
[docs]
@router.get("/artifacts", response_model=list[api_models.Artifact], tags=["misc"])
async def get_artifacts(
names: list[str] = Query(default=[]),
users: list[str] = Query(default=[]),
groups: list[str] = Query(default=[]),
distgit_proxy: PagureAsyncProxy = Depends(get_distgit_proxy),
):
"""This handler queries artifacts from Pagure
Proxying Pagure queries lets the API cache results to reduce load on the
backend service.
:param names: Name patterns of artifacts which should be returned
:param users: Names of users whose artifacts should be returned
:param groups: Names of groups whose artifacts should be returned
"""
backend_coroutines = chain(
(distgit_proxy.get_projects(pattern=name_pattern) for name_pattern in names),
(distgit_proxy.get_user_projects(username=user) for user in users),
(distgit_proxy.get_group_projects(name=group) for group in groups),
)
backend_results = chain.from_iterable([await coroutine for coroutine in backend_coroutines])
artifacts = sorted(
{
(project["name"], project["namespace"])
for project in backend_results
if ArtifactType.has_value(project["namespace"])
}
)
return [{"type": artifact[1], "name": artifact[0]} for artifact in artifacts]
[docs]
@router.get("/healthz/live", tags=["healthz"])
async def liveness_check():
return {"detail": "OK"}
[docs]
@router.get("/healthz/ready", tags=["healthz"])
async def readiness_check(db_manager=Depends(gen_db_manager)):
try:
status = await db_manager.get_status()
except Exception as e:
raise HTTPException(status_code=500, detail=f"Can't get the database status: {e}") from e
if status is DatabaseStatus.NO_INFO:
raise HTTPException(status_code=500, detail="Can't connect to the database")
if status is DatabaseStatus.UPGRADE_AVAILABLE:
raise HTTPException(status_code=500, detail="Database schema needs to be upgraded")
return {"detail": "OK"}