Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
22 changes: 12 additions & 10 deletions src/mavedb/routers/hgvs.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
from cdot.hgvs.dataproviders import RESTDataProvider
from fastapi import APIRouter, Depends, HTTPException
from hgvs import parser, validator
from hgvs.exceptions import HGVSDataNotAvailableError, HGVSInvalidVariantError
from hgvs.exceptions import HGVSDataNotAvailableError, HGVSError

from mavedb.deps import hgvs_data_provider
from mavedb.routers.shared import BASE_400_RESPONSE, PUBLIC_ERROR_RESPONSES, ROUTER_BASE_PREFIX
from mavedb.routers.shared import PUBLIC_ERROR_RESPONSES, ROUTER_BASE_PREFIX, VALIDATION_ERROR_RESPONSES
from mavedb.view_models.hgvs import HgvsValidationRequest

TAG_NAME = "Transcripts"

Expand Down Expand Up @@ -44,22 +45,23 @@ def hgvs_fetch(accession: str, hdp: RESTDataProvider = Depends(hgvs_data_provide
"/validate",
status_code=200,
response_model=bool,
responses={**BASE_400_RESPONSE},
responses={**VALIDATION_ERROR_RESPONSES},
summary="Validate a provided variant",
)
def hgvs_validate(variant: dict[str, str], hdp: RESTDataProvider = Depends(hgvs_data_provider)) -> bool:
def hgvs_validate(request: HgvsValidationRequest, hdp: RESTDataProvider = Depends(hgvs_data_provider)) -> bool:
"""
Validate the provided HGVS variant string.

Parsing and validation failures both stem from caller-supplied input, so any ``HGVSError`` — a syntactic
parse failure, an inconsistent variant, an unknown accession — is surfaced as a 400 rather than escaping
to the catch-all 500 handler.
"""
hp = parser.Parser()
variant_hgvs = hp.parse(variant["variant"])

try:
valid = validator.Validator(hdp=hdp).validate(variant_hgvs, strict=False)
except HGVSInvalidVariantError as e:
variant_hgvs = hp.parse(request.variant)
return validator.Validator(hdp=hdp).validate(variant_hgvs, strict=False)
except HGVSError as e:
raise HTTPException(400, str(e))
else:
return valid


@router.get("/assemblies", status_code=200, response_model=list[str], summary="List stored assemblies")
Expand Down
5 changes: 5 additions & 0 deletions src/mavedb/view_models/hgvs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from mavedb.view_models.base.base import BaseModel


class HgvsValidationRequest(BaseModel):
variant: str
16 changes: 16 additions & 0 deletions tests/routers/test_hgvs.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
INVALID_TRANSCRIPT = "NX_99999.1"
VALID_VARIANT = VALID_NT_ACCESSION + ":c.1G>A"
INVALID_VARIANT = VALID_NT_ACCESSION + ":c.1delA"
UNPARSEABLE_VARIANT = "not a valid hgvs string"
HAS_PROTEIN_ACCESSION = "NM_000014.4"
PROTEIN_ACCESSION = "NP_000005.2"

Expand Down Expand Up @@ -64,6 +65,21 @@ def test_hgvs_validate_invalid(client, setup_router_db):
assert "does not agree" in response.json()["detail"]


def test_hgvs_validate_unparseable(client, setup_router_db):
# A syntactically invalid HGVS string fails at the parse stage, before validation. This is a caller
# error and must surface as a 400 rather than escaping to the catch-all 500 handler.
payload = {"variant": UNPARSEABLE_VARIANT}
response = client.post("/api/v1/hgvs/validate", json=payload)

assert response.status_code == 400


def test_hgvs_validate_missing_field(client, setup_router_db):
response = client.post("/api/v1/hgvs/validate", json={})

assert response.status_code == 422


def test_hgvs_list_assemblies(client, setup_router_db):
response = client.get("/api/v1/hgvs/assemblies")
assert response.status_code == 200
Expand Down
Loading