Skip to content

Commit 5f719ff

Browse files
authored
fix: Fixed media duplication (#207)
* chore: Added python-magic to deps * feat: Updated bucket name resolution and delete media
1 parent 7fdeded commit 5f719ff

File tree

6 files changed

+52
-16
lines changed

6 files changed

+52
-16
lines changed

poetry.lock

Lines changed: 20 additions & 8 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 & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ aiofiles = "==0.6.0"
2222
requests = "^2.22.0"
2323
sentry-sdk = "^1.5.12"
2424
qarnot = "^2.2.1"
25+
python-magic = "^0.4.17"
2526

2627
flake8 = { version = ">=3.9.0,<5.0.0", optional = true }
2728
isort = { version = "^5.7.0", optional = true }

src/app/api/routes/media.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33
# This program is licensed under the Apache License version 2.
44
# See LICENSE or go to <https://www.apache.org/licenses/LICENSE-2.0.txt> for full license details.
55

6+
from datetime import datetime
7+
from mimetypes import guess_extension
68
from typing import Any, Dict, List, Optional
79

10+
import magic
811
from fastapi import APIRouter, BackgroundTasks, Depends, File, HTTPException, Path, Security, UploadFile, status
912

1013
from app.api import crud
@@ -116,7 +119,11 @@ async def delete_media(media_id: int = Path(..., gt=0), _=Security(get_current_a
116119
"""
117120
Based on a media_id, deletes the specified media
118121
"""
119-
return await crud.delete_entry(media, media_id)
122+
# Delete entry
123+
entry = await crud.delete_entry(media, media_id)
124+
# Delete media file
125+
await bucket_service.delete_file(entry["bucket_key"])
126+
return entry
120127

121128

122129
@router.post("/{media_id}/upload", response_model=MediaOut, status_code=200)
@@ -133,9 +140,13 @@ async def upload_media_from_device(
133140
# Check in DB
134141
entry = await check_media_registration(media_id, current_device.id)
135142

136-
# Concatenate the first 32 chars (to avoid system interactions issues) of SHA256 hash with file extension
143+
# Concatenate the first 8 chars (to avoid system interactions issues) of SHA256 hash with file extension
137144
file_hash = hash_content_file(file.file.read())
138-
file_name = f"{file_hash[:32]}.{file.filename.rpartition('.')[-1]}"
145+
await file.seek(0)
146+
# guess_extension will return none if this fails
147+
extension = guess_extension(magic.from_buffer(file.file.read(), mime=True)) or ""
148+
# Concatenate timestamp & hash
149+
file_name = f"{datetime.utcnow().strftime('%Y%m%d%H%M%S')}-{file_hash[:8]}{extension}"
139150
# Reset byte position of the file (cf. https://fastapi.tiangolo.com/tutorial/request-files/#uploadfile)
140151
await file.seek(0)
141152
# If files are in a subfolder of the bucket, prepend the folder path
@@ -167,6 +178,9 @@ async def upload_media_from_device(
167178
raise HTTPException(
168179
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Data was corrupted during upload"
169180
)
181+
# If a file was previously uploaded, delete it
182+
if isinstance(entry["bucket_key"], str):
183+
await bucket_service.delete_file(entry["bucket_key"])
170184

171185
entry_dict = dict(**entry)
172186
entry_dict["bucket_key"] = bucket_key

src/app/requirements.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ aiofiles==0.6.0
22
anyio==3.6.1; python_version >= "3.6" and python_full_version >= "3.6.2"
33
asyncpg==0.25.0; python_full_version >= "3.6.0"
44
bcrypt==3.2.2; python_version >= "3.6"
5-
boto3==1.24.19; python_version >= "3.7"
6-
botocore==1.27.19; python_version >= "3.7"
5+
boto3==1.24.20; python_version >= "3.7"
6+
botocore==1.27.20; python_version >= "3.7"
77
certifi==2022.6.15; python_version >= "3.7" and python_version < "4"
88
cffi==1.15.0; python_version >= "3.6"
99
charset-normalizer==2.1.0; python_version >= "3.7" and python_version < "4" and python_full_version >= "3.6.0"
@@ -26,6 +26,7 @@ pydantic==1.9.1; python_full_version >= "3.6.1"
2626
pyparsing==3.0.9; python_full_version >= "3.6.8" and python_version >= "3.6"
2727
python-dateutil==2.8.2; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.7"
2828
python-jose==3.3.0
29+
python-magic==0.4.27; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0")
2930
python-multipart==0.0.5
3031
qarnot==2.10.0; python_version >= "3.6"
3132
requests==2.28.1; python_version >= "3.7" and python_version < "4"

src/requirements-dev.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ asyncpg==0.25.0; python_full_version >= "3.6.0"
66
atomicwrites==1.4.0; python_version >= "3.7" and python_full_version < "3.0.0" and sys_platform == "win32" or sys_platform == "win32" and python_version >= "3.7" and python_full_version >= "3.4.0"
77
attrs==21.4.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7"
88
bcrypt==3.2.2; python_version >= "3.6"
9-
boto3==1.24.19; python_version >= "3.7"
10-
botocore==1.27.19; python_version >= "3.7"
9+
boto3==1.24.20; python_version >= "3.7"
10+
botocore==1.27.20; python_version >= "3.7"
1111
certifi==2022.6.15; python_version >= "3.7" and python_version < "4"
1212
cffi==1.15.0; python_version >= "3.6"
1313
charset-normalizer==2.1.0; python_version >= "3.7" and python_version < "4" and python_full_version >= "3.6.0"
@@ -41,6 +41,7 @@ pytest==7.1.2; python_version >= "3.7"
4141
python-dateutil==2.8.2; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.7"
4242
python-editor==1.0.4; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0"
4343
python-jose==3.3.0
44+
python-magic==0.4.27; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0")
4445
python-multipart==0.0.5
4546
qarnot==2.10.0; python_version >= "3.6"
4647
requests==2.28.1; python_version >= "3.7" and python_version < "4"

src/tests/routes/test_media.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,13 +274,20 @@ async def test_update_media(
274274
],
275275
)
276276
@pytest.mark.asyncio
277-
async def test_delete_media(test_app_asyncio, init_test_db, access_idx, media_id, status_code, status_details):
277+
async def test_delete_media(
278+
test_app_asyncio, init_test_db, monkeypatch, access_idx, media_id, status_code, status_details
279+
):
278280

279281
# Create a custom access token
280282
auth = None
281283
if isinstance(access_idx, int):
282284
auth = await pytest.get_token(ACCESS_TABLE[access_idx]["id"], ACCESS_TABLE[access_idx]["scope"].split())
283285

286+
async def mock_delete_file(filename):
287+
return True
288+
289+
monkeypatch.setattr(bucket_service, "delete_file", mock_delete_file)
290+
284291
response = await test_app_asyncio.delete(f"/media/{media_id}/", headers=auth)
285292
assert response.status_code == status_code
286293
if isinstance(status_details, str):

0 commit comments

Comments
 (0)