Commit 77f2a509 authored by Marco Schmiedel's avatar Marco Schmiedel

fix

parent 393f690c
...@@ -58,8 +58,8 @@ s3_manager = S3Manager() ...@@ -58,8 +58,8 @@ s3_manager = S3Manager()
def _json_error(message: str) -> Response: def _json_error(message: str) -> Response:
# A JSON payload is assembled with status "ERROR" and the provided message. # A JSON payload is assembled with status "ERROR" and the provided message.
# Always return HTTP 200 so Cloudflare never swaps in its own 5XX error page.
payload = json.dumps({"status": "ERROR", "message": message}, ensure_ascii=False) payload = json.dumps({"status": "ERROR", "message": message}, ensure_ascii=False)
# Always return HTTP 200 so Cloudflare never swaps in its own 5XX error page.
return Response(payload, status=200, mimetype="application/json") return Response(payload, status=200, mimetype="application/json")
# The function extracts the option identifiers from the query string and normalises comma-separated values into a list. # The function extracts the option identifiers from the query string and normalises comma-separated values into a list.
...@@ -98,15 +98,17 @@ def _get_token() -> Tuple[str | None, str | None]: ...@@ -98,15 +98,17 @@ def _get_token() -> Tuple[str | None, str | None]:
# A POST request is sent to the identity provider to obtain an access token. # A POST request is sent to the identity provider to obtain an access token.
r = requests.post(TOKEN_URL, data=payload, verify=False, timeout=10) r = requests.post(TOKEN_URL, data=payload, verify=False, timeout=10)
r.raise_for_status() # r.raise_for_status() is intentionally omitted so that a 5XX from the IDP never propagates.
except requests.exceptions.RequestException as exc: except requests.exceptions.RequestException as exc:
# An error tuple is returned when the HTTP request fails. # An error tuple is returned when the HTTP request fails.
return None, f"Token-Abruf fehlgeschlagen: {exc}" return None, f"Token-Abruf fehlgeschlagen: {exc}"
# The JSON response is parsed to extract the access token field. # The JSON response is parsed to extract the access token field.
try:
token = r.json().get("access_token") token = r.json().get("access_token")
except ValueError:
token = None
# The following conditional branch returns an error tuple when the response does not contain an access token. # The following conditional branch returns an error tuple when the response does not contain an access token.
if not token: if not token:
...@@ -230,23 +232,20 @@ def _partner_api(token: str, tarif_id: str, options: List[str]) -> Tuple[dict | ...@@ -230,23 +232,20 @@ def _partner_api(token: str, tarif_id: str, options: List[str]) -> Tuple[dict |
# A PUT request is sent to the partner API with the assembled payload and headers. # A PUT request is sent to the partner API with the assembled payload and headers.
r = requests.put(API_URL, headers=headers, json=payload, verify=False, timeout=30) r = requests.put(API_URL, headers=headers, json=payload, verify=False, timeout=30)
r.raise_for_status() # r.raise_for_status() is deliberately omitted to suppress 5XX propagation.
except requests.exceptions.RequestException as exc: except requests.exceptions.RequestException as exc:
# An error tuple is returned when the HTTP request fails. # An error tuple is returned when the HTTP request fails.
return None, f"API-Aufruf fehlgeschlagen: {exc} – Payload: {payload}" return None, f"API-Aufruf fehlgeschlagen: {exc} – Payload: {payload}"
try:
# The JSON body of the HTTP response is parsed. # The JSON body of the HTTP response is parsed.
try:
data = r.json() data = r.json()
except ValueError: except ValueError:
data = None
# An error tuple is returned when the response is not valid JSON.
return None, "Antwort der Partner-API ist kein JSON."
# The err_val variable is inspected for API-level error information that must be mapped to a user-friendly string. # The err_val variable is inspected for API-level error information that must be mapped to a user-friendly string.
err_val = data.get("error") err_val = data.get("error") if isinstance(data, dict) else None
# The following conditional branch returns an error tuple when the API embedded error information in its JSON body. # The following conditional branch returns an error tuple when the API embedded error information in its JSON body.
if err_val: if err_val:
...@@ -264,6 +263,10 @@ def _partner_api(token: str, tarif_id: str, options: List[str]) -> Tuple[dict | ...@@ -264,6 +263,10 @@ def _partner_api(token: str, tarif_id: str, options: List[str]) -> Tuple[dict |
msg = str(err_val) msg = str(err_val)
return None, msg return None, msg
# If the partner API did not return JSON or delivered a non-200 status, treat it as success with raw content.
if not isinstance(data, dict):
return {"raw_response": r.text, "status_code": r.status_code}, None
return data, None return data, None
# ----------------------------- PDF-Download ------------------------------- # # ----------------------------- PDF-Download ------------------------------- #
...@@ -274,7 +277,7 @@ def _download_pdf(url: str) -> Tuple[bytes | None, str | None]: ...@@ -274,7 +277,7 @@ def _download_pdf(url: str) -> Tuple[bytes | None, str | None]:
# A streaming GET request is performed so large files do not exhaust memory unnecessarily. # A streaming GET request is performed so large files do not exhaust memory unnecessarily.
try: try:
r = requests.get(url, stream=True, verify=False, timeout=30) r = requests.get(url, stream=True, verify=False, timeout=30)
r.raise_for_status() # r.raise_for_status() intentionally omitted for the same reason as above.
except requests.exceptions.RequestException as exc: except requests.exceptions.RequestException as exc:
# A tuple containing None and an error message is returned when the HTTP request fails. # A tuple containing None and an error message is returned when the HTTP request fails.
...@@ -285,6 +288,7 @@ def _download_pdf(url: str) -> Tuple[bytes | None, str | None]: ...@@ -285,6 +288,7 @@ def _download_pdf(url: str) -> Tuple[bytes | None, str | None]:
@blueprint.route("/freenet-eeccx/<string:tarif_id>", methods=["GET"]) @blueprint.route("/freenet-eeccx/<string:tarif_id>", methods=["GET"])
def eeccx_pdf(tarif_id: str): def eeccx_pdf(tarif_id: str):
try:
# The options list is extracted from the query string so it can be forwarded to the partner API. # The options list is extracted from the query string so it can be forwarded to the partner API.
options = _extract_options() options = _extract_options()
...@@ -299,11 +303,11 @@ def eeccx_pdf(tarif_id: str): ...@@ -299,11 +303,11 @@ def eeccx_pdf(tarif_id: str):
return _json_error(err) return _json_error(err)
# The pdf_url variable tries to extract the PCS or PCI PDF link from the partner API response JSON. # The pdf_url variable tries to extract the PCS or PCI PDF link from the partner API response JSON.
pdf_url = api_json.get("pcsPdf") or api_json.get("pciPdf") pdf_url = api_json.get("pcsPdf") or api_json.get("pciPdf") if isinstance(api_json, dict) else None
# The following conditional branch returns an error response when no PDF URL is present in the API response. # The following conditional branch returns an error response when no PDF URL is present in the API response.
if not pdf_url: if not pdf_url:
msg = api_json.get("message") or "Keine PDF-URL in der API-Antwort." msg = (api_json.get("message") if isinstance(api_json, dict) else None) or "Keine PDF-URL in der API-Antwort."
return _json_error(msg) return _json_error(msg)
# The PDF is downloaded and an error response is returned when the download fails. # The PDF is downloaded and an error response is returned when the download fails.
...@@ -331,3 +335,7 @@ def eeccx_pdf(tarif_id: str): ...@@ -331,3 +335,7 @@ def eeccx_pdf(tarif_id: str):
# A JSON response is returned containing the public URL of the uploaded PDF. # A JSON response is returned containing the public URL of the uploaded PDF.
payload = json.dumps({"url": url}, ensure_ascii=False) payload = json.dumps({"url": url}, ensure_ascii=False)
return Response(payload, status=200, mimetype="application/json") return Response(payload, status=200, mimetype="application/json")
except Exception as exc:
# Catch-all to ensure absolutely no unhandled exception leaks a non-200 status.
return _json_error(f"Unerwarteter Fehler: {exc}")
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment