Skip to content

Commit e8a62c4

Browse files
committed
Merge remote-tracking branch 'origin/main' into feature/eja-eli-343-adding-deletion-protection-and-IG-public-access-block
2 parents 199abd6 + 3f05f51 commit e8a62c4

35 files changed

Lines changed: 354 additions & 254 deletions

poetry.lock

Lines changed: 93 additions & 93 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ pytest = "^8.4.1"
4646
pytest-asyncio = "^1.0.0"
4747
pytest-cov = "^6.0.0"
4848
pytest-nhsd-apim = "^5.0.0"
49-
aiohttp = "^3.12.13"
49+
aiohttp = "^3.12.14"
5050
awscli = "^1.37.24"
5151
awscli-local = "^0.22.0"
5252
polyfactory = "^2.20.0"

src/eligibility_signposting_api/app.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88
from mangum.types import LambdaContext, LambdaEvent
99

1010
from eligibility_signposting_api import audit, repos, services
11+
from eligibility_signposting_api.common.error_handler import handle_exception
12+
from eligibility_signposting_api.common.request_validator import validate_request_params
1113
from eligibility_signposting_api.config.config import config, init_logging
12-
from eligibility_signposting_api.error_handler import handle_exception
1314
from eligibility_signposting_api.views import eligibility_blueprint
14-
from eligibility_signposting_api.wrapper import validate_request_params
1515

1616
init_logging()
1717
logger = logging.getLogger(__name__)

src/eligibility_signposting_api/audit/audit_context.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
RequestAuditQueryParams,
1919
)
2020
from eligibility_signposting_api.audit.audit_service import AuditService
21-
from eligibility_signposting_api.model.eligibility import (
21+
from eligibility_signposting_api.model.eligibility_status import (
2222
CohortGroupResult,
2323
ConditionName,
2424
IterationResult,

src/eligibility_signposting_api/common/__init__.py

Whitespace-only changes.
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# How to Use the API Error Response Module
2+
3+
This document outlines how to use the `api_error_response.py` module for standardized error handling within the Eligibility Signposting API. The module ensures that all API errors are consistent, logged appropriately, and conform to the FHIR `OperationOutcome` standard.
4+
5+
## Core Concepts
6+
7+
The error handling mechanism is built around the class `APIErrorResponse`.
8+
9+
1. **`APIErrorResponse` Class**: This class is a constructor for a specific type of error. An instance of this class holds configuration for an error, such as the `HTTPStatus`, severity, and various FHIR-specific codes.
10+
2. **Pre-defined Error Instances**: The module defines several singleton instances of for common, application-specific errors. Examples include:
11+
- `INVALID_CATEGORY_ERROR`
12+
- `NHS_NUMBER_MISMATCH_ERROR`
13+
- `INTERNAL_SERVER_ERROR`
14+
3. **`log_and_generate_response()` Method**: This is the primary method to be used. When called on an `APIErrorResponse` instance, it performs two actions:
15+
- Logs the error with a detailed internal message.
16+
- Generates a complete HTTP response dictionary (`statusCode`, `headers`, `body`) containing a FHIR-compliant `OperationOutcome` payload.
17+
18+
## How to Use
19+
20+
The primary way to handle errors is to import a pre-defined error object from `api_error_response.py` and call its `log_and_generate_response()` method.
21+
22+
### 1. Handling Specific, Known Errors
23+
24+
For handling validation failures or other expected error conditions, use one of the pre-defined error instances.
25+
The `wrapper.py` module uses this pattern to validate query parameters. If a parameter is invalid, it calls the corresponding error function.
26+
27+
#### Example: Invalid "category" parameter
28+
29+
``` python
30+
# wrapper.py
31+
32+
from eligibility_signposting_api.api_error_response import INVALID_CATEGORY_ERROR
33+
34+
def get_category_error_response(category: str) -> dict[str, Any]:
35+
"""Generates an error response for an invalid category."""
36+
37+
return INVALID_CATEGORY_ERROR.log_and_generate_response(
38+
log_message=f"Invalid category query param: '{category}'",
39+
diagnostics=f"{category} is not a category that is supported by the API",
40+
location_param="category"
41+
)
42+
```
43+
44+
#### Key Parameters for `log_and_generate_response()`
45+
46+
- `log_message`: A detailed message for internal logging. This should contain specific information useful for debugging.
47+
- `diagnostics`: The user-facing error message that will be included in the API response body.
48+
- `location_param` (optional): The name of the parameter that caused the error. This helps pinpoint the issue for API consumers.
49+
50+
### 2. Handling Unexpected Exceptions (Global Error Handler)
51+
52+
For unexpected errors, a global exception handler in `error_handler.py` catches any unhandled exception and returns a generic 500 Internal Server Error. This prevents sensitive information from leaking in stack traces.
53+
54+
``` python
55+
# error_handler.py
56+
57+
from eligibility_signposting_api.api_error_response import INTERNAL_SERVER_ERROR
58+
59+
def handle_exception(e: Exception) -> ResponseReturnValue | HTTPException:
60+
# Generate a generic, safe response for the client
61+
response = INTERNAL_SERVER_ERROR.log_and_generate_response(
62+
log_message=f"An unexpected error occurred: {traceback.format_exception(e)}",
63+
diagnostics="An unexpected error occurred."
64+
)
65+
return make_response(response.get("body"), response.get("statusCode"), response.get("headers"))
66+
```
67+
68+
### 3. Creating New Error Types
69+
70+
If a new, reusable error condition is identified, you should add a new instance of `APIErrorResponse` to `api_error_response.py`
71+
Follow the existing pattern:
72+
73+
``` python
74+
# api_error_response.py
75+
76+
# ... (other error definitions)
77+
78+
SOME_NEW_ERROR = APIErrorResponse(
79+
status_code=HTTPStatus.BAD_REQUEST,
80+
fhir_issue_code=FHIRIssueCode.VALUE,
81+
fhir_issue_severity=FHIRIssueSeverity.ERROR,
82+
fhir_coding_system=FHIR_SPINE_ERROR_CODE_SYSTEM,
83+
fhir_error_code=FHIRSpineErrorCode.INVALID_PARAMETER,
84+
fhir_display_message="A new specific error message for display",
85+
)
86+
```
87+
88+
By centralizing error definitions, we ensure that the API provides a consistent and predictable experience for its consumers.

src/eligibility_signposting_api/api_error_response.py renamed to src/eligibility_signposting_api/common/api_error_response.py

File renamed without changes.

src/eligibility_signposting_api/error_handler.py renamed to src/eligibility_signposting_api/common/error_handler.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from flask.typing import ResponseReturnValue
66
from werkzeug.exceptions import HTTPException
77

8-
from eligibility_signposting_api.api_error_response import INTERNAL_SERVER_ERROR
8+
from eligibility_signposting_api.common.api_error_response import INTERNAL_SERVER_ERROR
99

1010
logger = logging.getLogger(__name__)
1111

src/eligibility_signposting_api/wrapper.py renamed to src/eligibility_signposting_api/common/request_validator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
from mangum.types import LambdaContext, LambdaEvent
88

9-
from eligibility_signposting_api.api_error_response import (
9+
from eligibility_signposting_api.common.api_error_response import (
1010
INVALID_CATEGORY_ERROR,
1111
INVALID_CONDITION_FORMAT_ERROR,
1212
INVALID_INCLUDE_ACTIONS_ERROR,

src/eligibility_signposting_api/model/eligibility.py renamed to src/eligibility_signposting_api/model/eligibility_status.py

File renamed without changes.

0 commit comments

Comments
 (0)