✨(backend) add OpenShift-compatible recording download endpoint - #1622
✨(backend) add OpenShift-compatible recording download endpoint#1622mbenhammouda wants to merge 1 commit into
Conversation
fc01b5c to
1905897
Compare
PR Summary by QodoAdd OpenShift-compatible recording download endpoint (stream from S3 via Django)
AI Description
Diagram
High-Level Assessment
Files changed (4)
|
Code Review by Qodo
1. Unhandled S3 get_object errors
|
| s3_client = default_storage.connection.meta.client | ||
| s3_object = s3_client.get_object( | ||
| Bucket=default_storage.bucket_name, | ||
| Key=recording.key, | ||
| ) |
There was a problem hiding this comment.
1. Unhandled s3 get_object errors 🐞 Bug ☼ Reliability
RecordingViewSet.media_download calls s3_client.get_object without handling storage/client errors, so missing objects or transient S3/MinIO failures will surface as 500 responses instead of a controlled 404/503. This can break downloads in normal operational scenarios and may expose internal exception details via the API error handler.
Agent Prompt
### Issue description
`media_download` performs a direct `get_object` call to S3/MinIO without any exception handling. Any `ClientError` (eg, NoSuchKey), connectivity issues, or misconfiguration will bubble up as a 500.
### Issue Context
This endpoint is intended to replace ingress-level behavior; it should fail in a predictable way (eg 404 if object missing, 503 if backend unavailable) rather than crashing the request.
### Fix Focus Areas
- src/backend/core/api/viewsets.py[1265-1283]
### Suggested fix
- Wrap `get_object` in `try/except`.
- Map "object not found"-type errors to `drf_exceptions.NotFound`.
- Map transient backend issues to `drf_exceptions.APIException` (or a dedicated 503 exception if you have one in the project).
- Add a test that simulates `get_object` raising and asserts the expected HTTP status code.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| streaming_response = StreamingHttpResponse( | ||
| s3_object["Body"].iter_chunks(chunk_size=65536), | ||
| content_type=content_type, | ||
| ) |
There was a problem hiding this comment.
2. Streaming body never closed 🐞 Bug ☼ Reliability
media_download streams s3_object["Body"].iter_chunks(...) but never closes the underlying body/connection, which can leak S3/MinIO HTTP connections (especially on client disconnects) and eventually exhaust worker or backend resources. This is a long-lived endpoint for large downloads, so resource cleanup needs to be explicit.
Agent Prompt
### Issue description
`StreamingHttpResponse` is fed an iterator created from `s3_object["Body"].iter_chunks(...)`, but the code never ensures `s3_object["Body"]` is closed.
### Issue Context
This endpoint is a streaming proxy for potentially large media files. If the client disconnects early or an exception occurs mid-stream, not closing the upstream body can leave connections open.
### Fix Focus Areas
- src/backend/core/api/viewsets.py[1271-1283]
### Suggested fix
- Create a small generator wrapper:
- `body = s3_object["Body"]`
- `def stream():
try:
yield from body.iter_chunks(chunk_size=65536)
finally:
body.close()`
- Pass `stream()` to `StreamingHttpResponse`.
- Add/extend a unit test to assert `close()` is called on the mocked body when the iterator is exhausted (and ideally when aborted).
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| if content_length: | ||
| streaming_response["Content-Length"] = content_length | ||
|
|
There was a problem hiding this comment.
3. Zero content-length dropped 🐞 Bug ≡ Correctness
media_download only sets the Content-Length header when content_length is truthy; for valid zero-byte objects (ContentLength == 0) the header will be omitted. This is a small correctness issue that can cause inconsistent client behavior vs always emitting the known length.
Agent Prompt
### Issue description
The code checks `if content_length:` before setting the header, which skips the header when the length is 0.
### Issue Context
S3 returns `ContentLength` as an integer that can legitimately be 0.
### Fix Focus Areas
- src/backend/core/api/viewsets.py[1271-1282]
### Suggested fix
Change the condition to `if content_length is not None:` so 0 is preserved.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
bb40cf4 to
2721bcd
Compare
Implement a Django-based download endpoint that streams recording files
2721bcd to
7ed6091
Compare
|



Implement a Django-based download endpoint that streams recording files directly from S3 and removes the dependency on NGINX Ingress authentication annotations required by the existing download flow.
Purpose
The current recording download flow relies on NGINX Ingress authentication annotations (
auth-urlandauth-response-headers) to authorize access to recordings stored in S3.This approach works with NGINX Ingress but is not supported by OpenShift, making the recording download feature unavailable on OpenShift deployments.
Proposal
Introduce a dedicated Django endpoint to handle recording downloads without relying on ingress-level authentication annotations.
The endpoint:
media_authendpointStreamingHttpResponseContent-Disposition,Content-Type,Content-Length) to trigger a native browser download.Benefits