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
1 change: 0 additions & 1 deletion .github/workflows/stage-3-build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ jobs:
- name: "Build lambda artefact"
run: |
make dependencies install-python
poetry self add poetry-plugin-lambda-build poetry-plugin-export
make build
- name: "Upload lambda artefact"
uses: actions/upload-artifact@v4
Expand Down
572 changes: 309 additions & 263 deletions poetry.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ eval-type-backport = "^0.2.2"
mangum = "^0.19.0"
wireup = "^1.0.1"
python-json-logger = "^3.3.0"
fhir-resources = "^8.0.0"

[tool.poetry.group.dev.dependencies]
ruff = "^0.11.0"
Expand All @@ -55,7 +56,7 @@ pytest-docker = "^3.2.0"
stamina = "^25.1.0"

[tool.poetry-plugin-lambda-build]
docker-image = "public.ecr.aws/sam/build-python3.13:1.135-x86_64"
docker-image = "public.ecr.aws/sam/build-python3.13:1.137-x86_64"
docker-network = "host"
docker-platform = "linux/x86_64"
package-artifact-path = "dist/lambda.zip"
Expand Down
2 changes: 1 addition & 1 deletion scripts/dependencies.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ if ! [ -x "$(command -v poetry)" ]; then
fi
pipx install poetry
fi
poetry self add poetry-plugin-lambda-build poetry-plugin-export
poetry self add poetry-plugin-lambda-build@2.1.0 poetry-plugin-export@1.9.0
14 changes: 7 additions & 7 deletions src/eligibility_signposting_api/error_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@
import traceback
from http import HTTPStatus

from fhir.resources.operationoutcome import OperationOutcome, OperationOutcomeIssue
from flask import make_response
from flask.typing import ResponseReturnValue
from werkzeug.exceptions import HTTPException

from eligibility_signposting_api.views.response_models import Problem

logger = logging.getLogger(__name__)


Expand All @@ -18,10 +17,11 @@ def handle_exception(e: Exception) -> ResponseReturnValue | HTTPException:
if isinstance(e, HTTPException):
return e

problem = Problem(
title="Unexpected Exception",
type=str(type(e)),
status=HTTPStatus.INTERNAL_SERVER_ERROR,
detail="".join(traceback.format_exception(e)),
problem = OperationOutcome(
issue=[
OperationOutcomeIssue(
severity="severe", code="unexpected", diagnostics="".join(traceback.format_exception(e))
) # pyright: ignore[reportCallIssue]
]
)
return make_response(problem.model_dump(), HTTPStatus.INTERNAL_SERVER_ERROR)
13 changes: 10 additions & 3 deletions src/eligibility_signposting_api/views/eligibility.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import logging
from http import HTTPStatus

from fhir.resources.operationoutcome import OperationOutcome, OperationOutcomeIssue
from flask import Blueprint, make_response, request
from flask.typing import ResponseReturnValue
from wireup import Injected

from eligibility_signposting_api.model.eligibility import Eligibility, NHSNumber
from eligibility_signposting_api.services import EligibilityService, UnknownPersonError
from eligibility_signposting_api.views.response_models import EligibilityResponse, Problem
from eligibility_signposting_api.views.response_models import EligibilityResponse

logger = logging.getLogger(__name__)

Expand All @@ -22,8 +23,14 @@ def check_eligibility(eligibility_service: Injected[EligibilityService]) -> Resp
eligibility = eligibility_service.get_eligibility(nhs_number)
except UnknownPersonError:
logger.debug("nhs_number %r not found", nhs_number, extra={"nhs_number": nhs_number})
problem = Problem(
title="nhs_number not found", status=HTTPStatus.NOT_FOUND, detail=f"nhs_number {nhs_number} not found."
problem = OperationOutcome(
issue=[
OperationOutcomeIssue(
severity="information",
code="nhs-number-not-found",
diagnostics=f'NHS Number "{nhs_number}" not found.',
) # pyright: ignore[reportCallIssue]
]
)
return make_response(problem.model_dump(), HTTPStatus.NOT_FOUND)
else:
Expand Down
21 changes: 0 additions & 21 deletions src/eligibility_signposting_api/views/response_models.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,5 @@
from pydantic import BaseModel


class HelloResponse(BaseModel):
status: int
message: str


class Error(BaseModel):
name: str
reason: str


class Problem(BaseModel):
"""RFC 9457 problem detail - see https://pinboard.in/u:brunns/t:rfc-9457/"""

type: str | None = None
title: str | None = None
status: int | None = None
detail: str | None = None
instance: str | None = None
errors: list[Error] | None = None


class EligibilityResponse(BaseModel):
processed_suggestions: list[dict]
13 changes: 9 additions & 4 deletions tests/integration/lambda/test_app_running_as_lambda.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from brunns.matchers.data import json_matching as is_json_that
from brunns.matchers.response import is_response
from faker import Faker
from hamcrest import assert_that, contains_string, has_entries, has_item
from hamcrest import assert_that, contains_exactly, contains_string, has_entries, has_item
from yarl import URL

from eligibility_signposting_api.model.eligibility import DateOfBirth, NHSNumber, Postcode
Expand Down Expand Up @@ -94,9 +94,14 @@ def test_install_and_call_flask_lambda_with_unknown_nhs_number(
.and_body(
is_json_that(
has_entries(
title="nhs_number not found",
detail=f"nhs_number {nhs_number} not found.",
status=HTTPStatus.NOT_FOUND,
resourceType="OperationOutcome",
issue=contains_exactly(
has_entries(
severity="information",
code="nhs-number-not-found",
diagnostics=f'NHS Number "{nhs_number}" not found.',
)
),
)
)
),
Expand Down
34 changes: 31 additions & 3 deletions tests/unit/views/test_eligibility.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from brunns.matchers.werkzeug import is_werkzeug_response as is_response
from flask import Flask
from flask.testing import FlaskClient
from hamcrest import assert_that, has_entries
from hamcrest import assert_that, contains_exactly, has_entries
from wireup.integration.flask import get_app_container

from eligibility_signposting_api.model.eligibility import Eligibility, NHSNumber
Expand Down Expand Up @@ -58,11 +58,39 @@ def test_no_nhs_number_given(app: Flask, client: FlaskClient):
response = client.get("/eligibility/")

# Then
assert_that(response, is_response().with_status_code(HTTPStatus.NOT_FOUND))
assert_that(
response,
is_response()
.with_status_code(HTTPStatus.NOT_FOUND)
.and_text(
is_json_that(
has_entries(
resourceType="OperationOutcome",
issue=contains_exactly(
has_entries(
severity="information", code="nhs-number-not-found", diagnostics='NHS Number "" not found.'
)
),
)
)
),
)


def test_unexpected_error(app: Flask, client: FlaskClient):
# Given
with get_app_container(app).override.service(EligibilityService, new=FakeUnexpectedErrorEligibilityService()):
response = client.get("/eligibility/?nhs_number=12345")
assert_that(response, is_response().with_status_code(HTTPStatus.INTERNAL_SERVER_ERROR))
assert_that(
response,
is_response()
.with_status_code(HTTPStatus.INTERNAL_SERVER_ERROR)
.and_text(
is_json_that(
has_entries(
resourceType="OperationOutcome",
issue=contains_exactly(has_entries(severity="severe", code="unexpected")),
)
)
),
)
Loading