Skip to content
Merged
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.2.1

- Handle BaseFHIRError exceptions from fhir-py in operation handlers

## 0.2.0

- Make pytest_plugin confiugurable via `aidbox_create_app` pytest settings for create_app function
Expand Down
2 changes: 1 addition & 1 deletion aidbox_python_sdk/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
__title__ = "aidbox-python-sdk"
__version__ = "0.2.0"
__version__ = "0.2.1"
__author__ = "beda.software"
__license__ = "None"
__copyright__ = "Copyright 2024 beda.software"
Expand Down
17 changes: 12 additions & 5 deletions aidbox_python_sdk/handlers.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import asyncio
import json
import logging
from typing import Any

from aiohttp import web
from fhirpy.base.exceptions import OperationOutcome
from fhirpy.base.exceptions import BaseFHIRError, OperationOutcome

from . import app_keys as ak

Expand Down Expand Up @@ -48,6 +49,12 @@ async def operation(request: web.Request, data: dict[str, Any]):
return result
except OperationOutcome as exc:
return web.json_response(exc.resource, status=422)
except BaseFHIRError as exc:
try:
payload = json.loads(str(exc))
return web.json_response(payload, status=422)
except (json.JSONDecodeError, TypeError):
return web.Response(text=str(exc), status=422, content_type="text/plain")


TYPES = {
Expand All @@ -59,10 +66,10 @@ async def operation(request: web.Request, data: dict[str, Any]):
@routes.post("/aidbox")
async def dispatch(request):
logger.debug("Dispatch new request %s %s", request.method, request.url)
json = await request.json()
if "type" in json and json["type"] in TYPES:
logger.debug("Dispatch to `%s` handler", json["type"])
return await TYPES[json["type"]](request, json)
data = await request.json()
if "type" in data and data["type"] in TYPES:
logger.debug("Dispatch to `%s` handler", data["type"])
return await TYPES[data["type"]](request, data)
req = {
"method": request.method,
"url": str(request.url),
Expand Down
45 changes: 43 additions & 2 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import asyncio
import json
import logging
from datetime import datetime

import coloredlogs
from aiohttp import web
from fhirpy.base.exceptions import BaseFHIRError, OperationOutcome
from sqlalchemy.sql.expression import select

from aidbox_python_sdk.db import DBProxy
Expand Down Expand Up @@ -119,7 +121,7 @@ async def daily_patient_report(operation, request):
GET /Patient/$weekly-report
GET /Patient/$daily-report
"""
patients = request.app["client"].resources("Patient")
patients = request["app"]["client"].resources("Patient")
async for p in patients:
logging.debug(p.serialize())
logging.debug("`daily_patient_report` operation handler")
Expand All @@ -133,7 +135,7 @@ async def get_app_ids(db: DBProxy):
return await db.alchemy(select(app.c.id))


@routes.get("/db_tests")
@routes.get("/db-tests")
async def db_tests(request):
db = request.app["db"]

Expand All @@ -151,3 +153,42 @@ async def db_tests(request):
)
async def observation_custom_op(operation, request):
return {"message": "Observation custom operation response"}


@sdk.operation(
["POST"],
["$operation-outcome-test"],
)
async def operation_outcome_test_op(operation, request):
raise OperationOutcome(reason="test reason")


@sdk.operation(
["POST"],
["$base-fhir-error-json-test"],
)
async def base_fhir_error_json_test_op(operation, request):
raise BaseFHIRError(
json.dumps(
{
"resourceType": "OperationOutcome",
"id": "not-found",
"text": {"status": "generated", "div": "Resource Patient/id not found"},
"issue": [
{
"severity": "fatal",
"code": "not-found",
"diagnostics": "Resource Patient/id not found",
}
],
}
)
)


@sdk.operation(
["POST"],
["$base-fhir-error-text-test"],
)
async def base_fhir_error_text_test_op(operation, request):
raise BaseFHIRError("plain")
21 changes: 20 additions & 1 deletion tests/test_sdk.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import pytest
from fhirpathpy import evaluate
from fhirpy.base.exceptions import OperationOutcome

import main
from aidbox_python_sdk.db import DBProxy
Expand Down Expand Up @@ -182,10 +183,28 @@ async def test_aidbox_db_fixture(client, aidbox_db: DBProxy, safe_db):
"""
Test that aidbox_db fixture works with isolated DB Proxy from app's instance
"""
response = await client.get("/db_tests")
response = await client.get("/db-tests")
assert response.status == 200
json = await response.json()
assert json == [{"id": "app-test"}]

app_ids = await main.get_app_ids(aidbox_db)
assert app_ids == [{"id": "app-test"}]


async def test_operation_base_fhir_error_json_test_op(aidbox_client):
with pytest.raises(OperationOutcome) as exc:
await aidbox_client.execute("/$base-fhir-error-json-test")
assert exc.value.resource.get("issue")[0].get("diagnostics") == "Resource Patient/id not found"


async def test_operation_base_fhir_error_text_test_op(aidbox_client):
with pytest.raises(OperationOutcome) as exc:
await aidbox_client.execute("/$base-fhir-error-text-test")
assert exc.value.resource.get("issue")[0].get("diagnostics") == "plain"


async def test_operation_outcome_test_op(aidbox_client):
with pytest.raises(OperationOutcome) as exc:
await aidbox_client.execute("/$operation-outcome-test")
assert exc.value.resource.get("issue")[0].get("diagnostics") == "test reason"
Loading