Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
C
crawler
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Project - Tarifs Crawler & API
crawler
Commits
77f2a509
Commit
77f2a509
authored
May 12, 2025
by
Marco Schmiedel
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix
parent
393f690c
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
66 additions
and
58 deletions
+66
-58
EeccxRouter.py
routes/EeccxRouter.py
+66
-58
No files found.
routes/EeccxRouter.py
View file @
77f2a509
...
...
@@ -58,8 +58,8 @@ s3_manager = S3Manager()
def
_json_error
(
message
:
str
)
->
Response
:
# 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
)
# Always return HTTP 200 so Cloudflare never swaps in its own 5XX error page.
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.
...
...
@@ -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.
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
:
# An error tuple is returned when the HTTP request fails.
return
None
,
f
"Token-Abruf fehlgeschlagen: {exc}"
# The JSON response is parsed to extract the access token field.
token
=
r
.
json
()
.
get
(
"access_token"
)
try
:
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.
if
not
token
:
...
...
@@ -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.
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
:
# An error tuple is returned when the HTTP request fails.
return
None
,
f
"API-Aufruf fehlgeschlagen: {exc} – Payload: {payload}"
# The JSON body of the HTTP response is parsed.
try
:
# The JSON body of the HTTP response is parsed.
data
=
r
.
json
()
except
ValueError
:
# An error tuple is returned when the response is not valid JSON.
return
None
,
"Antwort der Partner-API ist kein JSON."
data
=
None
# 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.
if
err_val
:
...
...
@@ -264,6 +263,10 @@ def _partner_api(token: str, tarif_id: str, options: List[str]) -> Tuple[dict |
msg
=
str
(
err_val
)
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
# ----------------------------- PDF-Download ------------------------------- #
...
...
@@ -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.
try
:
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
:
# A tuple containing None and an error message is returned when the HTTP request fails.
...
...
@@ -285,49 +288,54 @@ def _download_pdf(url: str) -> Tuple[bytes | None, str | None]:
@
blueprint
.
route
(
"/freenet-eeccx/<string:tarif_id>"
,
methods
=
[
"GET"
])
def
eeccx_pdf
(
tarif_id
:
str
):
# The options list is extracted from the query string so it can be forwarded to the partner API.
options
=
_extract_options
()
# The OAuth token is obtained and an error response is returned when token retrieval fails.
token
,
err
=
_get_token
()
if
err
:
return
_json_error
(
err
)
# The partner API is called and an error response is returned when the API invocation fails.
api_json
,
err
=
_partner_api
(
token
,
tarif_id
,
options
)
if
err
:
return
_json_error
(
err
)
# 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"
)
# The following conditional branch returns an error response when no PDF URL is present in the API response.
if
not
pdf_url
:
msg
=
api_json
.
get
(
"message"
)
or
"Keine PDF-URL in der API-Antwort."
return
_json_error
(
msg
)
# The PDF is downloaded and an error response is returned when the download fails.
pdf_bytes
,
err
=
_download_pdf
(
pdf_url
)
if
err
:
return
_json_error
(
err
)
# A unique hash is generated so the temporary file and the S3 object name are collision-free.
hash_name
=
_hash_id_options
(
tarif_id
,
options
)
# A temporary file is opened so the PDF can be written to disk for uploading.
with
tempfile
.
NamedTemporaryFile
(
delete
=
False
,
suffix
=
".pdf"
)
as
tmp
:
tmp
.
write
(
pdf_bytes
)
tmp_path
=
tmp
.
name
# The PDF file is uploaded to S3 and the local temporary file is removed afterwards.
s3_key
=
f
"eeccx/{hash_name}.pdf"
url
=
s3_manager
.
uploadFile
(
tmp_path
,
s3_key
)
os
.
remove
(
tmp_path
)
# The following conditional branch returns an error response when the S3 upload fails.
if
not
url
:
return
_json_error
(
f
"Upload zu S3 fehlgeschlagen für key={s3_key}"
)
# A JSON response is returned containing the public URL of the uploaded PDF.
payload
=
json
.
dumps
({
"url"
:
url
},
ensure_ascii
=
False
)
return
Response
(
payload
,
status
=
200
,
mimetype
=
"application/json"
)
try
:
# The options list is extracted from the query string so it can be forwarded to the partner API.
options
=
_extract_options
()
# The OAuth token is obtained and an error response is returned when token retrieval fails.
token
,
err
=
_get_token
()
if
err
:
return
_json_error
(
err
)
# The partner API is called and an error response is returned when the API invocation fails.
api_json
,
err
=
_partner_api
(
token
,
tarif_id
,
options
)
if
err
:
return
_json_error
(
err
)
# 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"
)
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.
if
not
pdf_url
:
msg
=
(
api_json
.
get
(
"message"
)
if
isinstance
(
api_json
,
dict
)
else
None
)
or
"Keine PDF-URL in der API-Antwort."
return
_json_error
(
msg
)
# The PDF is downloaded and an error response is returned when the download fails.
pdf_bytes
,
err
=
_download_pdf
(
pdf_url
)
if
err
:
return
_json_error
(
err
)
# A unique hash is generated so the temporary file and the S3 object name are collision-free.
hash_name
=
_hash_id_options
(
tarif_id
,
options
)
# A temporary file is opened so the PDF can be written to disk for uploading.
with
tempfile
.
NamedTemporaryFile
(
delete
=
False
,
suffix
=
".pdf"
)
as
tmp
:
tmp
.
write
(
pdf_bytes
)
tmp_path
=
tmp
.
name
# The PDF file is uploaded to S3 and the local temporary file is removed afterwards.
s3_key
=
f
"eeccx/{hash_name}.pdf"
url
=
s3_manager
.
uploadFile
(
tmp_path
,
s3_key
)
os
.
remove
(
tmp_path
)
# The following conditional branch returns an error response when the S3 upload fails.
if
not
url
:
return
_json_error
(
f
"Upload zu S3 fehlgeschlagen für key={s3_key}"
)
# A JSON response is returned containing the public URL of the uploaded PDF.
payload
=
json
.
dumps
({
"url"
:
url
},
ensure_ascii
=
False
)
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}"
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment