Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
ea3d2a7
Standardize router tags for clarity and consistency
bencap Oct 28, 2025
18fcf17
fixed: AccessKey object using `created_at` when property was `creatio…
bencap Oct 28, 2025
0cf96b2
Enhance API Documentation: Add explicit summaries for access key rout…
bencap Oct 28, 2025
92236a2
fixed: raise appropriate 404 error when access key is not found durin…
bencap Oct 28, 2025
2fbae48
Enhance API Documentation: Add explicit summary for api version endpoint
bencap Oct 28, 2025
2b6b57a
Enhance API documentation: Add summaries and response status descript…
bencap Oct 28, 2025
aedbfd1
Enhance API Documentation: Add summaries to controlled keyword routes
bencap Oct 28, 2025
7864fcd
Enhance API Documentation: Improve summary and response descriptions …
bencap Oct 28, 2025
aa4ba52
fixed: permissions were not properly enforced on experiment set fetches
bencap Oct 28, 2025
ac41cdf
Enhance API Documentation: Add missing response descriptions and summ…
bencap Oct 28, 2025
20bdddb
fixed: Use permission module to check experiments in list router
bencap Oct 28, 2025
3783ad2
fixed: Simplify if/else logic of get experiment sets router
bencap Oct 28, 2025
357cb24
Enhance API responses and documentation: Add missing response descrip…
bencap Oct 28, 2025
091f3b0
Enhance API documentation: Add summaries and improve descriptions for…
bencap Oct 28, 2025
3cbaeea
Enhance API documentation: Improve summaries for licenses endpoints
bencap Oct 28, 2025
956dd27
Enhance API documentation: Add internal server error response descrip…
bencap Oct 28, 2025
f615f60
Enhance API documentation: Add response descriptions and summaries fo…
bencap Oct 28, 2025
ac6ee3e
Enhance API documentation: Add internal server error response and imp…
bencap Oct 28, 2025
74e8d10
Enhance API documentation: Update responses and summary for permissio…
bencap Oct 28, 2025
2ed520e
Enhance API documentation: Improve summaries and response description…
bencap Oct 28, 2025
abed834
Enhance API documentation: Update response descriptions and error han…
bencap Oct 28, 2025
566bf90
Enhance API documentation: Add summaries for service info and metadat…
bencap Oct 28, 2025
d9e3b3d
Enhance API documentation: Add summaries and response descriptions fo…
bencap Oct 28, 2025
9b46446
Enhance API documentation: Add summaries for sequence, metadata, and …
bencap Oct 28, 2025
b9a5090
Enhance API documentation: Add summaries for various statistics endpo…
bencap Oct 28, 2025
f418f9e
Enhance API Documentation: fix response status code for missing searc…
bencap Oct 28, 2025
b61cff8
Enhance API documentation: Add summaries and response descriptions fo…
bencap Oct 28, 2025
c6549ae
Enhance API documentation: Add summaries for taxonomy endpoints
bencap Oct 28, 2025
ee2d725
Enhance API documentation: Add summaries and response descriptions fo…
bencap Oct 28, 2025
73de569
Enhance API documentation: Add summaries and response descriptions fo…
bencap Oct 28, 2025
d133e30
Clarify HTTP status codes for email and role authorization checks
bencap Oct 28, 2025
9f327f3
Enhance API metadata and tag consistency across routers for improved …
bencap Oct 30, 2025
01e3db1
Merge branch 'release-2025.5.0' of https://github.com/VariantEffect/m…
bencap Nov 11, 2025
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
19 changes: 4 additions & 15 deletions src/mavedb/lib/authorization.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
from typing import Optional

from fastapi import Depends, HTTPException
from starlette import status

from mavedb.lib.authentication import UserData, get_current_user
from mavedb.lib.logging.context import logging_context, save_to_logging_context
Expand All @@ -21,10 +20,7 @@ async def require_current_user(
) -> UserData:
if user_data is None:
logger.info(msg="Non-authenticated user attempted to access protected route.", extra=logging_context())
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
)
raise HTTPException(status_code=401, detail="Could not validate credentials")

return user_data

Expand All @@ -38,8 +34,7 @@ async def require_current_user_with_email(
msg="User attempted to access email protected route without a valid email.", extra=logging_context()
)
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="There must be an email address associated with your account to use this feature.",
status_code=403, detail="There must be an email address associated with your account to use this feature."
)
return user_data

Expand All @@ -54,10 +49,7 @@ async def __call__(self, user_data: UserData = Depends(require_current_user)) ->
logger.info(
msg="User attempted to access role protected route without a required role.", extra=logging_context()
)
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="You are not authorized to use this feature",
)
raise HTTPException(status_code=403, detail="You are not authorized to use this feature")

return user_data

Expand All @@ -68,9 +60,6 @@ async def require_role(roles: list[UserRole], user_data: UserData = Depends(requ
logger.info(
msg="User attempted to access role protected route without a required role.", extra=logging_context()
)
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="You are not authorized to use this feature",
)
raise HTTPException(status_code=403, detail="You are not authorized to use this feature")

return user_data
2 changes: 1 addition & 1 deletion src/mavedb/lib/taxonomies.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,6 @@ async def search_NCBI_taxonomy(db: Session, search: str) -> Any:
else:
raise HTTPException(status_code=404, detail=f"Taxonomy with search {search_text} not found in NCBI")
else:
raise HTTPException(status_code=404, detail="Please enter valid searching words")
raise HTTPException(status_code=400, detail="Search text is required")

return taxonomy_record
57 changes: 45 additions & 12 deletions src/mavedb/routers/access_keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from fastapi import APIRouter, Depends
from fastapi.encoders import jsonable_encoder
from fastapi.exceptions import HTTPException
from sqlalchemy import and_
from sqlalchemy.orm import Session

from mavedb import deps
Expand All @@ -17,15 +18,27 @@
from mavedb.lib.logging.context import logging_context, save_to_logging_context
from mavedb.models.access_key import AccessKey
from mavedb.models.enums.user_role import UserRole
from mavedb.routers.shared import ACCESS_CONTROL_ERROR_RESPONSES, PUBLIC_ERROR_RESPONSES, ROUTER_BASE_PREFIX
from mavedb.view_models import access_key

TAG_NAME = "Access Keys"

router = APIRouter(
prefix="/api/v1",
tags=["access keys"],
responses={404: {"description": "Not found"}},
prefix=f"{ROUTER_BASE_PREFIX}",
tags=[TAG_NAME],
responses={**PUBLIC_ERROR_RESPONSES},
route_class=LoggedRoute,
)

metadata = {
"name": TAG_NAME,
"description": "Manage API access keys for programmatic access to the MaveDB API.",
"externalDocs": {
"description": "Access Keys Documentation",
"url": "https://mavedb.org/docs/mavedb/accounts.html#api-access-tokens",
},
}

logger = logging.getLogger(__name__)


Expand All @@ -49,7 +62,8 @@ def generate_key_pair():
"/users/me/access-keys",
status_code=200,
response_model=list[access_key.AccessKey],
responses={404: {}, 500: {}},
responses={**ACCESS_CONTROL_ERROR_RESPONSES},
summary="List my access keys",
)
def list_my_access_keys(*, user_data: UserData = Depends(require_current_user)) -> Any:
"""
Expand All @@ -62,7 +76,8 @@ def list_my_access_keys(*, user_data: UserData = Depends(require_current_user))
"/users/me/access-keys",
status_code=200,
response_model=access_key.NewAccessKey,
responses={404: {}, 500: {}},
responses={**ACCESS_CONTROL_ERROR_RESPONSES},
summary="Create a new access key for myself",
)
def create_my_access_key(
*,
Expand All @@ -88,7 +103,8 @@ def create_my_access_key(
"/users/me/access-keys/{role}",
status_code=200,
response_model=access_key.NewAccessKey,
responses={404: {}, 500: {}},
responses={**ACCESS_CONTROL_ERROR_RESPONSES},
summary="Create a new access key for myself with a specified role",
)
async def create_my_access_key_with_role(
*,
Expand Down Expand Up @@ -125,7 +141,12 @@ async def create_my_access_key_with_role(
return response_item


@router.delete("/users/me/access-keys/{key_id}", status_code=200, responses={404: {}, 500: {}})
@router.delete(
"/users/me/access-keys/{key_id}",
status_code=200,
responses={**ACCESS_CONTROL_ERROR_RESPONSES},
summary="Delete one of my access keys",
)
def delete_my_access_key(
*,
key_id: str,
Expand All @@ -135,8 +156,20 @@ def delete_my_access_key(
"""
Delete one of the current user's access keys.
"""
item = db.query(AccessKey).filter(AccessKey.key_id == key_id).one_or_none()
if item and item.user.id == user_data.user.id:
db.delete(item)
db.commit()
logger.debug(msg="Successfully deleted provided API key.", extra=logging_context())
item = (
db.query(AccessKey)
.filter(and_(AccessKey.key_id == key_id, AccessKey.user_id == user_data.user.id))
.one_or_none()
)

if not item:
logger.warning(
msg="Could not delete API key; Provided key ID does not exist and/or does not belong to the current user.",
extra=logging_context(),
)
# Never acknowledge the existence of an access key that doesn't belong to the user.
raise HTTPException(status_code=404, detail=f"Access key with ID {key_id} not found.")

db.delete(item)
db.commit()
logger.debug(msg="Successfully deleted provided API key.", extra=logging_context())
18 changes: 15 additions & 3 deletions src/mavedb/routers/api_information.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,27 @@
from fastapi import APIRouter

from mavedb import __project__, __version__
from mavedb.routers.shared import PUBLIC_ERROR_RESPONSES, ROUTER_BASE_PREFIX
from mavedb.view_models import api_version

router = APIRouter(prefix="/api/v1/api", tags=["api information"], responses={404: {"description": "Not found"}})
TAG_NAME = "API Information"

router = APIRouter(
prefix=f"{ROUTER_BASE_PREFIX}/api",
tags=[TAG_NAME],
responses={**PUBLIC_ERROR_RESPONSES},
)

@router.get("/version", status_code=200, response_model=api_version.ApiVersion, responses={404: {}})
metadata = {
"name": TAG_NAME,
"description": "Retrieve information about the MaveDB API.",
}


@router.get("/version", status_code=200, response_model=api_version.ApiVersion, summary="Show API version")
def show_version() -> Any:
"""
Describe the API version.
Describe the API version and project.
"""

return api_version.ApiVersion(name=__project__, version=__version__)
Loading