Skip to content

Commit

Permalink
Update API documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
hluk committed Sep 15, 2023
1 parent 41f2947 commit d95f5e3
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 29 deletions.
2 changes: 1 addition & 1 deletion product_listings_manager/products.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def my_sort(x, y):


def get_product_info(db, label):
"""Get the latest version of product and it's variants."""
"""Get the latest version of product and its variants."""
products = db.query(models.Products).filter_by(label=label).all()
versions = [x.version for x in products]
# Use functools.cmp_to_key for python3
Expand Down
121 changes: 108 additions & 13 deletions product_listings_manager/rest_api_v1.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import logging
import os
from functools import lru_cache
from typing import Any

from fastapi import APIRouter, Depends, HTTPException, Request, status
from sqlalchemy import text
Expand Down Expand Up @@ -56,7 +57,7 @@ def parse_permissions(filename) -> list[Permission]:

@router.get("/")
def api_index(request: Request):
"""Link to the the v1 API endpoints."""
"""Links to the v1 API endpoints."""
return {
"about_url": str(request.url_for("about")),
"health_url": str(request.url_for("health")),
Expand All @@ -83,6 +84,7 @@ def api_index(request: Request):

@router.get("/about")
def about():
"""Shows information about the application."""
return {
"source": "https://github.com/release-engineering/product-listings-manager",
"version": __version__,
Expand All @@ -97,7 +99,7 @@ def about():
},
)
def health(db: Session = Depends(get_db)):
"""Provides status report"""
"""Provides status report."""

try:
permissions()
Expand Down Expand Up @@ -129,18 +131,39 @@ def health(db: Session = Depends(get_db)):
return Message(message=HEALTH_OK_MESSAGE)


@router.get("/login")
@router.get("/login", responses={401: {}})
def login(request: Request) -> LoginInfo:
"""Shows credentials for the current user."""
"""Shows the current user and assigned groups."""
ldap_config_ = ldap_config()
user, headers = get_user(request)
groups = set(get_user_groups(user, ldap_config_))

return LoginInfo(user=user, groups=sorted(groups))


@router.get("/product-info/{label}")
def product_info(label: str, request: Request, db: Session = Depends(get_db)):
@router.get(
"/product-info/{label}",
responses={
200: {
"content": {
"application/json": {
"example": [
"8.2.0",
[
"Supplementary-8.2.0.GA",
"AppStream-8.2.0.GA",
"BaseOS-8.2.0.GA",
],
]
}
},
},
},
)
def product_info(
label: str, request: Request, db: Session = Depends(get_db)
) -> tuple[str, list[str]]:
"""Get the latest version of a product and its variants."""
try:
versions, variants = products.get_product_info(db, label)
except products.ProductListingsNotFoundError as ex:
Expand All @@ -154,11 +177,27 @@ def product_info(label: str, request: Request, db: Session = Depends(get_db)):
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(ex)
)
return [versions, variants]
return (versions, variants)


@router.get("/product-labels")
@router.get(
"/product-labels",
responses={
200: {
"content": {
"application/json": {
"example": [
{"label": "RHEL-8.2.1.MAIN"},
{"label": "RHEL-8.2.0.GA"},
{"label": "RHEL-8.2.0"},
]
}
},
},
},
)
def product_labels(request: Request, db: Session = Depends(get_db)):
"""List all product labels."""
try:
return products.get_product_labels(db)
except Exception as ex:
Expand All @@ -170,13 +209,49 @@ def product_labels(request: Request, db: Session = Depends(get_db)):
)


@router.get("/product-listings/{label}/{build_info}")
@router.get(
"/product-listings/{label}/{build_info}",
responses={
200: {
"content": {
"application/json": {
"example": [
{
"AppStream-8.3.0.GA": {
"python-dasbus-1.2-1.el8": {
"src": [
"aarch64",
"x86_64",
"s390x",
"ppc64le",
]
},
"python3-dasbus-1.2-1.el8": {
"noarch": [
"aarch64",
"x86_64",
"s390x",
"ppc64le",
]
},
}
}
]
}
},
},
},
)
def product_listings(
label: str,
build_info: str,
request: Request,
db: Session = Depends(get_db),
):
"""
Get a map of which variants of the given product included packages built
by the given build, and which arches each variant included.
"""
try:
return products.get_product_listings(db, label, build_info)
except products.ProductListingsNotFoundError as ex:
Expand All @@ -202,6 +277,10 @@ def module_product_listings(
request: Request,
db: Session = Depends(get_db),
):
"""
Get a map of which variants of the given product included the given module,
and which arches each variant included.
"""
try:
return products.get_module_product_listings(
db, label, module_build_nvr
Expand All @@ -222,7 +301,7 @@ def module_product_listings(
)


@router.get("/permissions")
@router.get("/permissions", responses={401: {}})
def permissions() -> list[Permission]:
"""
Lists user and group permissions for using **dbquery** API.
Expand All @@ -231,12 +310,28 @@ def permissions() -> list[Permission]:
return parse_permissions(filename)


@router.post("/dbquery")
@router.post(
"/dbquery",
responses={
200: {
"content": {
"application/json": {
"example": [
["HighAvailability-8.2.0.GA", False],
["NFV-8.2.0.GA", False],
]
}
},
},
401: {},
403: {},
},
)
async def dbquery(
query_or_queries: SqlQuery | list[SqlQuery | str] | str,
request: Request,
db: Session = Depends(get_db),
):
) -> list[list[Any]]:
"""
Executes given SQL queries with optionally provided parameters.
Expand Down Expand Up @@ -268,7 +363,7 @@ async def dbquery(
if not has_permission(user, queries, permissions(), ldap_config_):
logger.warning("Unauthorized access for user %s", user)
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
status_code=status.HTTP_403_FORBIDDEN,
detail=f"User {user} is not authorized to use this query",
)

Expand Down
2 changes: 2 additions & 0 deletions product_listings_manager/root.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,7 @@ def index(request: Request):
"""Link to the documentation and top-level API endpoints."""
return {
"documentation_url": DOCUMENTATION_URL,
"api_reference": str(request.url_for("redoc_html")),
"swagger_ui": str(request.url_for("swagger_ui_html")),
"api_v1_url": str(request.url_for("api_index")),
}
15 changes: 3 additions & 12 deletions product_listings_manager/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,12 @@ class SqlQuery(BaseModel):
"json_schema_extra": {
"examples": [
{
"query": (
"INSERT INTO products (label, version, variant, allow_source_only)"
" VALUES (:label, :version, :variant, :allow_source_only)"
),
"params": {
"label": "product1",
"version": "1.2",
"variant": "Client",
"allow_source_only": 1,
"label": "RHEL-8.2.0.GA",
},
},
{
"query": (
"SELECT label, version, variant, allow_source_only"
"FROM products LIMIT 1"
"SELECT variant, allow_source_only"
"FROM products WHERE label = :label LIMIT 1"
),
},
]
Expand Down
6 changes: 3 additions & 3 deletions tests/test_dbquery.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def test_db_query_unauthorized(self, auth_client):
r = auth_client.post(
"/api/v1.0/dbquery", json=query, headers=auth_headers()
)
assert r.status_code == 401, r.text
assert r.status_code == 403, r.text
assert r.json() == {
"message": "User test_user is not authorized to use this query"
}
Expand All @@ -40,7 +40,7 @@ def test_db_query_unauthorized_multiple_queries(self, auth_client):
r = auth_client.post(
"/api/v1.0/dbquery", json=queries, headers=auth_headers()
)
assert r.status_code == 401, r.text
assert r.status_code == 403, r.text
assert r.json() == {
"message": "User test_user is not authorized to use this query"
}
Expand All @@ -54,7 +54,7 @@ def test_db_query_unauthorized_no_matching_groups(self, auth_client):
r = auth_client.post(
"/api/v1.0/dbquery", json=query, headers=auth_headers()
)
assert r.status_code == 401, r.text
assert r.status_code == 403, r.text

def test_db_query_select_bad(self, auth_client):
query = "SELECT * FROM bad_table"
Expand Down
2 changes: 2 additions & 0 deletions tests/test_root.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ def test_get_version(self, client):
expected_json = {
"api_v1_url": "http://testserver/api/v1.0/",
"documentation_url": expected_docs,
"api_reference": "http://testserver/redoc",
"swagger_ui": "http://testserver/docs",
}
assert r.status_code == 200
assert r.json() == expected_json

0 comments on commit d95f5e3

Please sign in to comment.