Skip to content

Commit

Permalink
refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
MartinBelthle committed Sep 19, 2024
1 parent b454dc8 commit 7288b38
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 67 deletions.
6 changes: 3 additions & 3 deletions antarest/core/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,14 +343,14 @@ def __init__(self, is_variant: bool) -> None:
super().__init__(HTTPStatus.EXPECTATION_FAILED, "Upgrade not supported for parent of variants")


class FileOrFolderDeletionFailed(HTTPException):
class FileDeletionNotAllowed(HTTPException):
"""
Exception raised when deleting a file or a folder inside the User folder.
Exception raised when deleting a file or a folder which isn't inside the 'User' folder.
"""

def __init__(self, message: str) -> None:
msg = f"Raw deletion failed because {message}"
super().__init__(HTTPStatus.EXPECTATION_FAILED, msg)
super().__init__(HTTPStatus.FORBIDDEN, msg)


class ReferencedObjectDeletionNotAllowed(HTTPException):
Expand Down
30 changes: 21 additions & 9 deletions antarest/study/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
BadEditInstructionException,
ChildNotFoundError,
CommandApplicationError,
FileOrFolderDeletionFailed,
FileDeletionNotAllowed,
IncorrectPathError,
NotAManagedStudyException,
ReferencedObjectDeletionNotAllowed,
Expand Down Expand Up @@ -2642,18 +2642,30 @@ def asserts_no_thermal_in_binding_constraints(
raise ReferencedObjectDeletionNotAllowed(cluster_id, binding_ids, object_type="Cluster")

def delete_file_or_folder(self, study_id: str, path: str, current_user: JWTUser) -> None:
"""
Deletes a file or a folder of the study.
The data must be located inside the 'User' folder.
Also, it can not be inside the 'expansion' folder.
Args:
study_id: UUID of the concerned study
path: Path corresponding to the resource to be deleted
current_user: User that called the endpoint
Raises:
FileDeletionNotAllowed: if the path does not comply with the above rules
"""
study = self.get_study(study_id)
assert_permission(current_user, study, StudyPermissionType.WRITE)

url = [item for item in path.split("/") if item]
if len(url) < 2:
raise FileOrFolderDeletionFailed(f"the given path cannot be treated: {path}")
if url[0] == "user" and url[1] == "expansion":
raise FileOrFolderDeletionFailed("you cannot delete a file/folder inside expansion folder")
if len(url) < 2 or url[0] != "user":
raise FileDeletionNotAllowed(f"the targeted data isn't inside the 'User' folder: {path}")
if url[1] == "expansion":
raise FileDeletionNotAllowed(f"you cannot delete a file/folder inside 'expansion' folder : {path}")

study_tree = self.storage_service.raw_study_service.get_raw(study, True).tree.build()
try:
study_tree["user"].delete(url[1:])
except ChildNotFoundError as e:
raise FileOrFolderDeletionFailed(
f"the file doesn't exist or you're not inside the 'User' folder: {e.detail}"
)
# todo: I don't really like the way exception are handled. I would prefer having a FORBIDDEN if so and an 404 otherwise.
raise FileDeletionNotAllowed(f"the given path doesn't exist: {e.detail}")
4 changes: 2 additions & 2 deletions antarest/study/web/raw_studies_blueprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,12 +188,12 @@ def get_study(
@bp.delete(
"/studies/{uuid}/raw",
tags=[APITag.study_raw_data],
summary="Delete files or folders from Study inside User folder",
summary="Delete files or folders located inside the 'User' folder",
response_model=None,
)
def delete_file(
uuid: str,
path: str = Param("/", examples=get_path_examples()), # type: ignore
path: str = Param("/", examples=["user/wind_solar/synthesis_windSolar.xlsx"]), # type: ignore
current_user: JWTUser = Depends(auth.get_current_user),
) -> t.Any:
uuid = sanitize_uuid(uuid)
Expand Down
48 changes: 48 additions & 0 deletions tests/integration/raw_studies_blueprint/test_fetch_raw_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,3 +264,51 @@ def test_get_study(
headers=headers,
)
assert res.status_code == 200, f"Error for path={path} and depth={depth}"


def test_delete_raw(client: TestClient, user_access_token: str, internal_study_id: str) -> None:
client.headers = {"Authorization": f"Bearer {user_access_token}"}

# =============================
# SET UP + NOMINAL CASES
# =============================

content = io.BytesIO(b"This is the end!")
file_1_path = "user/file_1.txt"
file_2_path = "user/folder/file_2.txt"
for f in [file_1_path, file_2_path]:
# Creates a file / folder inside user folder.
res = client.put(
f"/v1/studies/{internal_study_id}/raw", params={"path": f, "create_missing": True}, files={"file": content}
)
assert res.status_code == 204, res.json()

# Deletes the file / folder
res = client.delete(f"/v1/studies/{internal_study_id}/raw?path={f}")
assert res.status_code == 200
# Asserts it doesn't exist anymore
res = client.get(f"/v1/studies/{internal_study_id}/raw?path={f}")
assert res.status_code == 404
assert "not a child of" in res.json()["description"]

# =============================
# ERRORS
# =============================

# try to delete expansion folder
res = client.delete(f"/v1/studies/{internal_study_id}/raw?path=/user/expansion")
assert res.status_code == 403
assert res.json()["exception"] == "FileDeletionNotAllowed"
assert "you cannot delete a file/folder inside 'expansion' folder" in res.json()["description"]

# try to delete a file which isn't inside the 'User' folder
res = client.delete(f"/v1/studies/{internal_study_id}/raw?path=/input/thermal")
assert res.status_code == 403
assert res.json()["exception"] == "FileDeletionNotAllowed"
assert "the targeted data isn't inside the 'User' folder" in res.json()["description"]

# With a path that doesn't exist
res = client.delete(f"/v1/studies/{internal_study_id}/raw?path=user/fake_folder/fake_file.txt")
assert res.status_code == 403
assert res.json()["exception"] == "FileDeletionNotAllowed"
assert "the given path doesn't exist" in res.json()["description"]
53 changes: 0 additions & 53 deletions tests/integration/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -1913,56 +1913,3 @@ def test_links_deletion_with_binding_constraints(
# delete the link
res = client.delete(f"/v1/studies/{internal_study_id}/links/area_1/area_2")
assert res.status_code == 200, res.json()


def test_delete_raw(client: TestClient, user_access_token: str, internal_study_id: str) -> None:
client.headers = {"Authorization": f"Bearer {user_access_token}"}

# =============================
# SET UP + NOMINAL CASES
# =============================

content = io.BytesIO(b"This is the end!")
file_1_path = "user/file_1.txt"
file_2_path = "user/folder/file_2.txt"
for f in [file_1_path, file_2_path]:
# Creates a file / folder inside user folder.
res = client.put(
f"/v1/studies/{internal_study_id}/raw",
params={"path": f, "create_missing": True},
files={"file": content}
)
assert res.status_code == 204, res.json()

# Deletes the file / folder
res = client.delete(f"/v1/studies/{internal_study_id}/raw?path={f}")
assert res.status_code == 200
# Asserts it doesn't exist anymore
res = client.get(f"/v1/studies/{internal_study_id}/raw?path={f}")
assert res.status_code == 404
assert "not a child of User" in res.json()["description"]

# =============================
# ERRORS
# =============================

# try to delete expansion folder
res = client.delete(f"/v1/studies/{internal_study_id}/raw?path=/user/expansion")
assert res.status_code == 417
assert res.json()["exception"] == "FileOrFolderDeletionFailed"
assert "you cannot delete a file/folder inside expansion folder" in res.json()["description"]
# try to delete a file which isn't inside the 'User' folder
res = client.delete(f"/v1/studies/{internal_study_id}/raw?path=/input/thermal")
assert res.status_code == 417
assert res.json()["exception"] == "FileOrFolderDeletionFailed"
assert "the file doesn't exist or you're not inside the 'User' folder" in res.json()["description"]
# With an empty path
res = client.delete(f"/v1/studies/{internal_study_id}/raw?path=fake_path")
assert res.status_code == 417
assert res.json()["exception"] == "FileOrFolderDeletionFailed"
assert "the given path cannot be treated" in res.json()["description"]
# With a path that doesn't exist
res = client.delete(f"/v1/studies/{internal_study_id}/raw?path=fake_folder/fake_file")
assert res.status_code == 417
assert res.json()["exception"] == "FileOrFolderDeletionFailed"
assert "the file doesn't exist or you're not inside the 'User' folder" in res.json()["description"]

0 comments on commit 7288b38

Please sign in to comment.