Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add object #26

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
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
71 changes: 70 additions & 1 deletion cloud_storage_handler/api/elixircloud/csh/controllers.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,84 @@
"""ELIXIR's Cloud Storage Handler controllers."""

import logging
import os
import uuid
from http import HTTPStatus

from flask import jsonify
from flask import current_app, jsonify, request
from minio.error import S3Error

logger = logging.getLogger(__name__)

UPLOAD_DIRECTORY = "/tmp/upload"
psankhe28 marked this conversation as resolved.
Show resolved Hide resolved
CHUNK_SIZE = 5 * 1024 * 1024


def home():
"""Endpoint to return a welcome message."""
return jsonify(
{"message": "Welcome to the Cloud Storage Handler server!"}
), HTTPStatus.OK


def upload_object():
psankhe28 marked this conversation as resolved.
Show resolved Hide resolved
"""Handles file uploads to cloud storage.

Retrieves files from the request, processes each file into chunks,
and uploads them to the specified storage bucket. Returns a response
with a success message and details about the uploaded file(s).
"""
files = request.files.getlist("files")
responses = []
minio_config = current_app.config.foca.custom.minio
bucket_name = minio_config.bucket_name
minio_client = current_app.config.foca.custom.minio.client.client

for file in files:
if not file:
return jsonify({"error": "No file provided"}), 400

file_id = str(uuid.uuid4())
file_path = os.path.join(UPLOAD_DIRECTORY, f"{file_id}.temp")

file.save(file_path)
psankhe28 marked this conversation as resolved.
Show resolved Hide resolved

total_size = os.path.getsize(file_path)
total_chunks = (total_size // CHUNK_SIZE) + (
1 if total_size % CHUNK_SIZE > 0 else 0
)

file_dir = os.path.join(UPLOAD_DIRECTORY, file_id)
os.makedirs(file_dir, exist_ok=True)

with open(file_path, "rb") as f:
for i in range(total_chunks):
chunk_data = f.read(CHUNK_SIZE)
chunk_filename = os.path.join(file_dir, f"chunk_{i}")
with open(chunk_filename, "wb") as chunk_file:
chunk_file.write(chunk_data)

try:
minio_client.fput_object(
bucket_name,
f"{file_id}/chunk_{i}",
chunk_filename,
metadata={
"description": "Chunk upload via Flask",
},
)
except S3Error as e:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Add proper cleanup in error cases and improve error handling

Temporary files aren't cleaned up if an error occurs. Consider using a try/finally block and return a sanitized error response instead of exposing raw S3Error messages.

return jsonify({"error": str(e)}), 500

os.remove(file_path)
os.rmdir(file_dir)

responses.append(
{
"message": "File uploaded successfully",
"file_id": file_id,
"total_chunks": total_chunks,
}
)

return jsonify(responses), 200
34 changes: 33 additions & 1 deletion cloud_storage_handler/api/specs/specs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,36 @@
description: The request is malformed.
'500':
description: An unexpected error occurred.
...
/object:
post:
description: |
Create a new object upload using the TUS protocol. The request creates an empty object and returns an upload URL.

Check failure on line 41 in cloud_storage_handler/api/specs/specs.yaml

View workflow job for this annotation

GitHub Actions / Pre-commit checks

41:81 [line-length] line too long (121 > 80 characters)

Check failure on line 41 in cloud_storage_handler/api/specs/specs.yaml

View workflow job for this annotation

GitHub Actions / Pre-commit checks

41:81 [line-length] line too long (121 > 80 characters)
operationId: upload_object
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
properties:
file:
type: string
format: binary
description: The file to be uploaded (supports .bib, .txt, .docx, and other file types).

Check failure on line 53 in cloud_storage_handler/api/specs/specs.yaml

View workflow job for this annotation

GitHub Actions / Pre-commit checks

53:81 [line-length] line too long (106 > 80 characters)

Check failure on line 53 in cloud_storage_handler/api/specs/specs.yaml

View workflow job for this annotation

GitHub Actions / Pre-commit checks

53:81 [line-length] line too long (106 > 80 characters)
responses:
'201':
description: Object upload created successfully.
headers:
Location:
schema:
type: string
description: URL to upload object chunks using PATCH requests.
Tus-Resumable:
schema:
type: string
description: Version of the TUS protocol used.
'400':
description: Bad request or malformed request body.
'500':
description: An unexpected error occurred.
...
Loading