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
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ pythonVersion = "3.13"

[tool.pytest.ini_options]
log_cli = true
log_cli_level = "INFO"
log_cli_level = "DEBUG"
log_format = "%(asctime)s %(levelname)s %(message)s"
log_date_format = "%Y-%m-%d %H:%M:%S"

Expand Down
4 changes: 3 additions & 1 deletion src/eligibility_signposting_api/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from eligibility_signposting_api import repos, services
from eligibility_signposting_api.config import LOG_LEVEL, config, init_logging
from eligibility_signposting_api.error_handler import handle_exception
from eligibility_signposting_api.views.eligibility import eligibility
from eligibility_signposting_api.views.hello import hello


Expand All @@ -32,7 +33,8 @@ def create_app() -> Flask:
app.logger.info("app created")

# Register views & error handler
app.register_blueprint(hello)
app.register_blueprint(eligibility, url_prefix="/eligibility")
app.register_blueprint(hello, url_prefix="/hello")
app.register_error_handler(Exception, handle_exception)

# Set up dependency injection using wireup
Expand Down
3 changes: 3 additions & 0 deletions src/eligibility_signposting_api/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,8 @@ def init_logging() -> None:
"wsgi": {"class": "logging.StreamHandler", "stream": "ext://sys.stdout", "formatter": "default"}
},
"root": {"level": level, "handlers": ["wsgi"]},
"loggers": {
"eligibility_signposting_api.app": {"level": level, "handlers": ["wsgi"], "propagate": False},
},
}
)
8 changes: 7 additions & 1 deletion src/eligibility_signposting_api/error_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,20 @@

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__)


def handle_exception(e: BaseException) -> ResponseReturnValue:
def handle_exception(e: Exception) -> ResponseReturnValue | HTTPException:
logger.exception("Unexpected Exception", exc_info=e)

# Let Flask handle its own exceptions for now.
if isinstance(e, HTTPException):
return e

problem = Problem(
title="Unexpected Exception",
type=str(type(e)),
Expand Down
3 changes: 3 additions & 0 deletions src/eligibility_signposting_api/model/eligibility.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from typing import NewType

NHSNumber = NewType("NHSNumber", str)
17 changes: 17 additions & 0 deletions src/eligibility_signposting_api/views/eligibility.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import logging
from http import HTTPStatus

from flask import Blueprint, make_response
from flask.typing import ResponseReturnValue

from eligibility_signposting_api.model.eligibility import NHSNumber

logger = logging.getLogger(__name__)

eligibility = Blueprint("eligibility", __name__)


@eligibility.get("/<nhs_number>")
def check_eligibility(nhs_number: NHSNumber) -> ResponseReturnValue:
logger.info("nhs_number: %s", nhs_number)
return make_response({}, HTTPStatus.OK)
26 changes: 26 additions & 0 deletions tests/integration/in_process/test_eligibility_endpoint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from http import HTTPStatus

from brunns.matchers.data import json_matching as is_json_that
from brunns.matchers.werkzeug import is_werkzeug_response as is_response
from flask.testing import FlaskClient
from hamcrest import assert_that, empty, is_


def test_nhs_number_given(client: FlaskClient):
# Given

# When
response = client.get("/eligibility/12345")

# Then
assert_that(response, is_response().with_status_code(HTTPStatus.OK).and_text(is_json_that(is_(empty()))))


def test_no_nhs_number_given(client: FlaskClient):
# Given

# When
response = client.get("/eligibility/")

# Then
assert_that(response, is_response().with_status_code(HTTPStatus.NOT_FOUND))
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def test_no_name_given(client: FlaskClient):
# Given

# When
response = client.get("/")
response = client.get("/hello/")

# Then
assert_that(
Expand All @@ -40,7 +40,7 @@ def test_app_for_name_with_nickname(client: FlaskClient):
# Given

# When
response = client.get("/simon")
response = client.get("/hello/simon")

# Then
assert_that(
Expand All @@ -55,7 +55,7 @@ def test_app_for_nonexistent_name(client: FlaskClient):
# Given

# When
response = client.get("/fred")
response = client.get("/hello/fred")

# Then
assert_that(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ def test_install_and_call_lambda_flask(lambda_client: BaseClient, flask_function
"rawPath": "/",
"rawQueryString": "",
"headers": {"accept": "application/json", "content-type": "application/json"},
"requestContext": {"http": {"sourceIp": "192.0.0.1", "method": "GET", "path": "/", "protocol": "HTTP/1.1"}},
"requestContext": {
"http": {"sourceIp": "192.0.0.1", "method": "GET", "path": "/hello/", "protocol": "HTTP/1.1"}
},
"body": None,
"isBase64Encoded": False,
}
Expand All @@ -60,7 +62,7 @@ def test_install_and_call_flask_lambda_over_http(flask_function_url: URL):
# Given

# When
response = httpx.get(str(flask_function_url))
response = httpx.get(str(flask_function_url / "hello" / ""))

# Then
assert_that(
Expand All @@ -76,7 +78,7 @@ def test_install_and_call_flask_lambda_with_nickname_over_http(flask_function_ur
# Given

# When
response = httpx.get(str(flask_function_url / "ayesh"), timeout=30)
response = httpx.get(str(flask_function_url / "hello" / "ayesh"), timeout=30)

# Then
assert_that(
Expand Down
29 changes: 29 additions & 0 deletions tests/unit/views/test_eligibility.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import logging
from http import HTTPStatus

from brunns.matchers.data import json_matching as is_json_that
from brunns.matchers.werkzeug import is_werkzeug_response as is_response
from flask.testing import FlaskClient
from hamcrest import assert_that, empty, is_

logger = logging.getLogger(__name__)


def test_nhs_number_given(client: FlaskClient):
# Given

# When
response = client.get("/eligibility/12345")

# Then
assert_that(response, is_response().with_status_code(HTTPStatus.OK).and_text(is_json_that(is_(empty()))))


def test_no_nhs_number_given(client: FlaskClient):
# Given

# When
response = client.get("/eligibility/")

# Then
assert_that(response, is_response().with_status_code(HTTPStatus.NOT_FOUND))
8 changes: 4 additions & 4 deletions tests/unit/views/test_hello.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,23 +39,23 @@ def get_nickname(self, _: str | None = None) -> str:

def test_name_given(app: Flask, client: FlaskClient):
with get_container(app).override.service(PersonService, new=FakePersonService()):
response = client.get("/simon")
response = client.get("/hello/simon")
assert_that(response, is_response().with_status_code(HTTPStatus.OK).and_text(contains_string("SIMON")))


def test_default_name(app: Flask, client: FlaskClient):
with get_container(app).override.service(PersonService, new=FakePersonService()):
response = client.get("/")
response = client.get("/hello/")
assert_that(response, is_response().with_status_code(HTTPStatus.OK).and_text(contains_string("Default")))


def test_unknown_name(app: Flask, client: FlaskClient):
with get_container(app).override.service(PersonService, new=FakeUnknownPersonService()):
response = client.get("/fred")
response = client.get("/hello/fred")
assert_that(response, is_response().with_status_code(HTTPStatus.NOT_FOUND))


def test_unexpected_error(app: Flask, client: FlaskClient):
with get_container(app).override.service(PersonService, new=FakeUnexpectedErrorPersonService()):
response = client.get("/fred")
response = client.get("/hello/fred")
assert_that(response, is_response().with_status_code(HTTPStatus.INTERNAL_SERVER_ERROR))
Loading