Skip to content

Commit

Permalink
chore(release): v2.17.6
Browse files Browse the repository at this point in the history
  • Loading branch information
skamril committed Sep 26, 2024
2 parents eb92a05 + f2ff7a4 commit 96ca99d
Show file tree
Hide file tree
Showing 54 changed files with 5,489 additions and 2,581 deletions.
4 changes: 2 additions & 2 deletions antarest/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@

# Standard project metadata

__version__ = "2.17.5"
__version__ = "2.17.6"
__author__ = "RTE, Antares Web Team"
__date__ = "2024-08-02"
__date__ = "2024-09-25"
# noinspection SpellCheckingInspection
__credits__ = "(c) Réseau de Transport de l’Électricité (RTE)"

Expand Down
10 changes: 10 additions & 0 deletions antarest/core/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,16 @@ def __init__(self, is_variant: bool) -> None:
super().__init__(HTTPStatus.EXPECTATION_FAILED, "Upgrade not supported for parent of variants")


class FileDeletionNotAllowed(HTTPException):
"""
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.FORBIDDEN, msg)


class ReferencedObjectDeletionNotAllowed(HTTPException):
"""
Exception raised when a binding constraint is not allowed to be deleted because it references
Expand Down
40 changes: 39 additions & 1 deletion antarest/study/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
BadEditInstructionException,
ChildNotFoundError,
CommandApplicationError,
FileDeletionNotAllowed,
IncorrectPathError,
NotAManagedStudyException,
ReferencedObjectDeletionNotAllowed,
Expand All @@ -46,7 +47,7 @@
)
from antarest.core.filetransfer.model import FileDownloadTaskDTO
from antarest.core.filetransfer.service import FileTransferManager
from antarest.core.interfaces.cache import ICache
from antarest.core.interfaces.cache import CacheConstants, ICache
from antarest.core.interfaces.eventbus import Event, EventType, IEventBus
from antarest.core.jwt import DEFAULT_ADMIN_USER, JWTGroup, JWTUser
from antarest.core.model import JSON, SUB_JSON, PermissionInfo, PublicMode, StudyPermissionType
Expand Down Expand Up @@ -130,6 +131,7 @@
from antarest.study.storage.rawstudy.model.filesystem.matrix.matrix import MatrixFrequency
from antarest.study.storage.rawstudy.model.filesystem.matrix.output_series_matrix import OutputSeriesMatrix
from antarest.study.storage.rawstudy.model.filesystem.raw_file_node import RawFileNode
from antarest.study.storage.rawstudy.model.filesystem.root.user.user import User
from antarest.study.storage.rawstudy.raw_study_service import RawStudyService
from antarest.study.storage.storage_service import StudyStorageService
from antarest.study.storage.study_download_utils import StudyDownloader, get_output_variables_information
Expand Down Expand Up @@ -2639,3 +2641,39 @@ def asserts_no_thermal_in_binding_constraints(
if ref_bcs:
binding_ids = [bc.id for bc in ref_bcs]
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 or url[0] != "user":
raise FileDeletionNotAllowed(f"the targeted data isn't inside the 'User' folder: {path}")

study_tree = self.storage_service.raw_study_service.get_raw(study, True).tree
user_node = t.cast(User, study_tree.get_node(["user"]))
if url[1] in [file.filename for file in user_node.registered_files]:
raise FileDeletionNotAllowed(f"you are not allowed to delete this resource : {path}")

try:
user_node.delete(url[1:])
except ChildNotFoundError as e:
raise FileDeletionNotAllowed("the given path doesn't exist") from e

# update cache
cache_id = f"{CacheConstants.RAW_STUDY}/{study.id}"
updated_tree = study_tree.get()
self.storage_service.get_storage(study).cache.put(cache_id, updated_tree) # type: ignore
15 changes: 15 additions & 0 deletions antarest/study/web/raw_studies_blueprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,21 @@ def get_study(
json_response = to_json(output)
return Response(content=json_response, media_type="application/json")

@bp.delete(
"/studies/{uuid}/raw",
tags=[APITag.study_raw_data],
summary="Delete files or folders located inside the 'User' folder",
response_model=None,
)
def delete_file(
uuid: str,
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)
logger.info(f"Deleting path {path} inside study {uuid}", extra={"user": current_user.id})
study_service.delete_file_or_folder(uuid, path, current_user)

@bp.get(
"/studies/{uuid}/areas/aggregate/mc-ind/{output_id}",
tags=[APITag.study_raw_data],
Expand Down
48 changes: 48 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,54 @@
Antares Web Changelog
=====================

v2.17.6 (2024-09-25)
--------------------

## What's Changed

### Features

* **upgrader**: use `antares-study-version` package [`2108`](https://github.com/AntaresSimulatorTeam/AntaREST/pull/2108)
* **ts-gen**: add timeseries generation [`2112`](https://github.com/AntaresSimulatorTeam/AntaREST/pull/2112)
* **bc**: show existing matrices only [`2109`](https://github.com/AntaresSimulatorTeam/AntaREST/pull/2109)
* **aggregation-api**: add new endpoint for `economy/mc-all` aggregation [`2092`](https://github.com/AntaresSimulatorTeam/AntaREST/pull/2092)
* **installer**: add installer as a submodule [`2110`](https://github.com/AntaresSimulatorTeam/AntaREST/pull/2110)
* **ui-debug**: update the view [`2093`](https://github.com/AntaresSimulatorTeam/AntaREST/pull/2093)

### Bug Fixes

* **ui-areas**: add correct unit in title in Properties form [`2115`](https://github.com/AntaresSimulatorTeam/AntaREST/pull/2115)
* **hydro**: wrong frequency for inflow pattern [`2116`](https://github.com/AntaresSimulatorTeam/AntaREST/pull/2116)
* **adequacy-patch**: csr relaxation must be an integer [`2123`](https://github.com/AntaresSimulatorTeam/AntaREST/pull/2123)
* **launcher-api**: remove orphan JobResults visibility permissions [`2128`](https://github.com/AntaresSimulatorTeam/AntaREST/pull/2128)
* **ui**: add missing `i18n` key and styles on `EmptyView` [`2127`](https://github.com/AntaresSimulatorTeam/AntaREST/pull/2127)

### Continuous integration

* **github**: split npm jobs and add dependency caching [`2118`](https://github.com/AntaresSimulatorTeam/AntaREST/pull/2118)
* **test**: parallel pytest execution [`2133`](https://github.com/AntaresSimulatorTeam/AntaREST/pull/2133)

### Documentation

* Add installer directions [`2114`](https://github.com/AntaresSimulatorTeam/AntaREST/pull/2114)

### Build

* Move all build and project configuration to pyproject.toml [`2122`](https://github.com/AntaresSimulatorTeam/AntaREST/pull/2122)
* Use relative paths in coverage report [`2125`](https://github.com/AntaresSimulatorTeam/AntaREST/pull/2125)
* Remove auto-generated changelog from desktop package [`2126`](https://github.com/AntaresSimulatorTeam/AntaREST/pull/2126)

### Chore

* Fix licensing-related issues [`2132`](https://github.com/AntaresSimulatorTeam/AntaREST/pull/2132)
* Add license headers for projects files [`2130`](https://github.com/AntaresSimulatorTeam/AntaREST/pull/2130)

### BREAKING CHANGES

* **aggregation-api**: add new endpoint for `economy/mc-all` aggregation [`2092`](https://github.com/AntaresSimulatorTeam/AntaREST/pull/2092)

**Full Changelog**: https://github.com/AntaresSimulatorTeam/AntaREST/compare/v2.17.5...v2.17.6

v2.17.5 (2024-08-05)
--------------------

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ requires = ["setuptools"]

[project]
name = "AntaREST"
version = "2.17.5"
version = "2.17.6"
authors = [{name="RTE, Antares Web Team", email="[email protected]" }]
description="Antares Server"
readme = {file = "README.md", content-type = "text/markdown"}
Expand Down
2 changes: 1 addition & 1 deletion sonar-project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ sonar.exclusions=antarest/gui.py,antarest/main.py
sonar.python.coverage.reportPaths=coverage.xml
sonar.python.version=3.8
sonar.javascript.lcov.reportPaths=webapp/coverage/lcov.info
sonar.projectVersion=2.17.5
sonar.projectVersion=2.17.6
sonar.coverage.exclusions=antarest/gui.py,antarest/main.py,antarest/singleton_services.py,antarest/worker/archive_worker_service.py,webapp/**/*,,antarest/fastapi_jwt_auth/**
63 changes: 63 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,66 @@ 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"
file_3_path = "user/folder_2/file_3.txt"
for f in [file_1_path, file_2_path, file_3_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
if f == file_2_path:
f = "user/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"]

# checks debug view
res = client.get(f"/v1/studies/{internal_study_id}/raw?path=&depth=-1")
assert res.status_code == 200
tree = res.json()["user"]
if f == file_3_path:
# asserts the folder that wasn't deleted is still here.
assert list(tree.keys()) == ["expansion", "folder_2"]
assert tree["folder_2"] == {}
else:
# asserts deleted files cannot be seen inside the debug view
assert list(tree.keys()) == ["expansion"]

# =============================
# 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 are not allowed to delete this resource" 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"]
Loading

0 comments on commit 96ca99d

Please sign in to comment.