Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DWI-29 Fast API Docs #8

Merged
merged 11 commits into from
Oct 16, 2024
6 changes: 5 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,9 @@
"tests"
],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true
"python.testing.pytestEnabled": true,
"[python]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "charliermarsh.ruff"
},
}
109 changes: 99 additions & 10 deletions aim/digifeeds/database/main.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from fastapi import Depends, FastAPI, HTTPException
from fastapi import Depends, FastAPI, HTTPException, Path, Query
from sqlalchemy import create_engine
from sqlalchemy.orm import Session, sessionmaker
from aim.digifeeds.database import crud, schemas
Expand All @@ -11,7 +11,18 @@
engine = create_engine(S.mysql_database)

SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
app = FastAPI()

description = """
The Digifeeds API enables tracking of images sent to HathiTrust and Google
through the digifeeds workflow
"""
tags_metadata = [
{
"name": "Digifeeds Database",
"description": "Digifeeds items and statuses.",
},
]
app = FastAPI(title="Digifeeds", description=description)


# Dependency
Expand All @@ -23,36 +34,108 @@ def get_db(): # pragma: no cover
db.close()


@app.get("/items/", response_model_by_alias=False)
@app.get("/items/", response_model_by_alias=False, tags=["Digifeeds Database"])
def get_items(
in_zephir: bool | None = None, db: Session = Depends(get_db)
in_zephir: bool | None = Query(
None, description="Filter for items that do or do not have metadata in Zephir"
),
db: Session = Depends(get_db),
) -> list[schemas.Item]:
"""
Get the digifeeds items.

These items can be filtered by whether or not their metadata is in Zephir or
all of them can be fetched.
"""

db_items = crud.get_items(in_zephir=in_zephir, db=db)
return db_items


@app.get("/items/{barcode}", response_model_by_alias=False)
def get_item(barcode: str, db: Session = Depends(get_db)) -> schemas.Item:
@app.get(
"/items/{barcode}",
response_model_by_alias=False,
responses={
404: {
"description": "Bad request: The item doesn't exist",
"model": schemas.Response404,
}
},
tags=["Digifeeds Database"],
)
def get_item(
barcode: str = Path(..., description="The barcode of the item"),
db: Session = Depends(get_db),
) -> schemas.Item:
"""
Get a digifeeds item.

The item can be fetched by the barcode of the item.
"""

db_item = crud.get_item(barcode=barcode, db=db)
if db_item is None:
raise HTTPException(status_code=404, detail="Item not found")
return db_item


@app.post("/items/{barcode}", response_model_by_alias=False)
def create_item(barcode: str, db: Session = Depends(get_db)) -> schemas.Item:
@app.post(
"/items/{barcode}",
response_model_by_alias=False,
responses={
400: {
"description": "Bad request: The item already exists",
"model": schemas.Response400,
}
},
tags=["Digifeeds Database"],
)
def create_item(
barcode: str = Path(..., description="The barcode of the item"),
db: Session = Depends(get_db),
) -> schemas.Item:
"""
Create a digifeeds item.

The item can be created with the barcode of the item and must not already exist.
"""

item = schemas.ItemCreate(barcode=barcode)
db_item = crud.get_item(barcode=item.barcode, db=db)
if db_item:
raise HTTPException(status_code=400, detail="Item already exists")
db_item = crud.add_item(item=item, db=db)
return db_item

desc_put_404 = """
Bad request: The item or status doesn't exist<br><br>
Possible reponses:
<ul>
<li>Item not found</li>
<li>Status not found</li>
</ul>
"""

@app.put("/items/{barcode}/status/{status_name}", response_model_by_alias=False)
@app.put(
"/items/{barcode}/status/{status_name}",
response_model_by_alias=False,
responses={
404: {
"description": desc_put_404,
"model": schemas.Response404,
}
},
tags=["Digifeeds Database"],
)
def update_item(
barcode: str, status_name: str, db: Session = Depends(get_db)
) -> schemas.Item:
"""
Update a digifeeds item.

This is how to add a status to an existing item.
"""

db_status = crud.get_status(name=status_name, db=db)
if db_status is None:
raise HTTPException(status_code=404, detail="Status not found")
Expand All @@ -62,7 +145,13 @@ def update_item(
return crud.add_item_status(db=db, item=db_item, status=db_status)


@app.get("/statuses")
@app.get("/statuses", tags=["Digifeeds Database"])
def get_statuses(db: Session = Depends(get_db)) -> list[schemas.Status]:
"""
Get digifeeds statuses.

Get a list of statuses.
"""

db_statuses = crud.get_statuses(db=db)
return db_statuses
47 changes: 47 additions & 0 deletions aim/digifeeds/database/schemas.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,47 @@
from pydantic import BaseModel, Field, ConfigDict
from datetime import datetime


class ItemStatus(BaseModel):
name: str = Field(alias="status_name")
description: str = Field(alias="status_description")
created_at: datetime

model_config = ConfigDict(populate_by_name=True, from_attributes=True)


class ItemBase(BaseModel):
model_config = ConfigDict(populate_by_name=True, from_attributes=True)

barcode: str = Field(alias="item_barcode")


class Item(ItemBase):
created_at: datetime
statuses: list[ItemStatus] = []
model_config = ConfigDict(
json_schema_extra={
"examples": [
{
"barcode": "39015040218748",
"created_at": "2024-09-25T17:12:39",
"statuses": [
{
"name": "in_zephir",
"description": "Item is in zephir",
"created_at": "2024-09-25T17:13:28",
}
],
}
]
}
)


class ItemCreate(ItemBase):
pass


class StatusBase(BaseModel):
name: str

Expand All @@ -28,3 +50,28 @@ class Status(StatusBase):
description: str


class Response(BaseModel):
detail: str


class Response400(Response):
model_config = ConfigDict(
json_schema_extra={
"examples": [
{
"detail": "Item already exists",
}
]
}
)

class Response404(Response):
model_config = ConfigDict(
json_schema_extra={
"examples": [
{
"detail": "Item not found",
}
]
}
)