Skip to content

Commit b1d5ee3

Browse files
remove PUT project - fixing unit tests
1 parent 9607181 commit b1d5ee3

File tree

5 files changed

+47
-173
lines changed

5 files changed

+47
-173
lines changed

packages/models-library/src/models_library/api_schemas_webserver/projects.py

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -121,18 +121,6 @@ class ProjectReplace(InputSchema):
121121
)
122122

123123

124-
class ProjectUpdate(InputSchema):
125-
name: ShortTruncatedStr = FieldNotRequired()
126-
description: LongTruncatedStr = FieldNotRequired()
127-
thumbnail: HttpUrlWithCustomMinLength = FieldNotRequired()
128-
workbench: NodesDict = FieldNotRequired()
129-
access_rights: dict[GroupIDStr, AccessRights] = FieldNotRequired()
130-
tags: list[int] = FieldNotRequired()
131-
classifiers: list[ClassifierID] = FieldNotRequired()
132-
ui: StudyUI | None = None
133-
quality: dict[str, Any] = FieldNotRequired()
134-
135-
136124
class ProjectPatch(InputSchema):
137125
name: ShortTruncatedStr = FieldNotRequired()
138126
description: LongTruncatedStr = FieldNotRequired()
@@ -143,6 +131,10 @@ class ProjectPatch(InputSchema):
143131
ui: StudyUI | None = FieldNotRequired()
144132
quality: dict[str, Any] = FieldNotRequired()
145133

134+
_empty_is_none = validator("thumbnail", allow_reuse=True, pre=True)(
135+
empty_str_to_none_pre_validator
136+
)
137+
146138

147139
__all__: tuple[str, ...] = (
148140
"EmptyModel",
@@ -151,6 +143,5 @@ class ProjectPatch(InputSchema):
151143
"ProjectGet",
152144
"ProjectListItem",
153145
"ProjectReplace",
154-
"ProjectUpdate",
155146
"TaskProjectGet",
156147
)

services/web/server/src/simcore_service_webserver/projects/_crud_handlers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ async def _wrapper(request: web.Request) -> web.StreamResponse:
101101
FolderAccessForbiddenError,
102102
WorkspaceAccessForbiddenError,
103103
) as exc:
104-
raise web.HTTPUnauthorized(reason=f"{exc}") from exc
104+
raise web.HTTPForbidden(reason=f"{exc}") from exc
105105

106106
return _wrapper
107107

services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers.py

Lines changed: 1 addition & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,9 @@
44
# pylint: disable=unused-argument
55
# pylint: disable=unused-variable
66

7-
import random
87
import re
98
import uuid as uuidlib
109
from collections.abc import Awaitable, Callable, Iterator
11-
from copy import deepcopy
1210
from http import HTTPStatus
1311
from math import ceil
1412
from typing import Any
@@ -19,10 +17,7 @@
1917
from aioresponses import aioresponses
2018
from faker import Faker
2119
from models_library.products import ProductName
22-
from models_library.projects_nodes import Node
2320
from models_library.projects_state import ProjectState
24-
from models_library.services import ServiceKey
25-
from models_library.utils.fastapi_encoders import jsonable_encoder
2621
from pydantic import parse_obj_as
2722
from pytest_simcore.helpers.assert_checks import assert_status
2823
from pytest_simcore.helpers.webserver_login import UserInfoDict
@@ -187,7 +182,7 @@ async def _replace_project(
187182
assert client.app
188183

189184
# PUT /v0/projects/{project_id}
190-
url = client.app.router["replace_project"].url_for(
185+
url = client.app.router["replace_project"].url_for( # <- MD: check this
191186
project_id=project_update["uuid"]
192187
)
193188
assert str(url) == f"{API_PREFIX}/projects/{project_update['uuid']}"
@@ -656,141 +651,6 @@ async def test_new_template_from_project(
656651
parse_obj_as(uuidlib.UUID, node_name)
657652

658653

659-
# PUT --------
660-
@pytest.mark.parametrize(
661-
"user_role,expected,expected_change_access",
662-
[
663-
(
664-
UserRole.ANONYMOUS,
665-
status.HTTP_401_UNAUTHORIZED,
666-
status.HTTP_401_UNAUTHORIZED,
667-
),
668-
(UserRole.GUEST, status.HTTP_200_OK, status.HTTP_403_FORBIDDEN),
669-
(UserRole.USER, status.HTTP_200_OK, status.HTTP_200_OK),
670-
(UserRole.TESTER, status.HTTP_200_OK, status.HTTP_200_OK),
671-
],
672-
)
673-
async def test_replace_project(
674-
client: TestClient,
675-
logged_user: UserInfoDict,
676-
user_project: ProjectDict,
677-
expected,
678-
expected_change_access,
679-
all_group,
680-
ensure_run_in_sequence_context_is_empty,
681-
):
682-
project_update = deepcopy(user_project)
683-
project_update["description"] = "some updated from original project!!!"
684-
await _replace_project(client, project_update, expected)
685-
686-
# replacing the owner access is not possible, it will keep the owner as well
687-
project_update["accessRights"].update(
688-
{str(all_group["gid"]): {"read": True, "write": True, "delete": True}}
689-
)
690-
await _replace_project(client, project_update, expected_change_access)
691-
692-
693-
@pytest.mark.parametrize(
694-
"user_role,expected",
695-
[
696-
(UserRole.ANONYMOUS, status.HTTP_401_UNAUTHORIZED),
697-
(UserRole.GUEST, status.HTTP_200_OK),
698-
(UserRole.USER, status.HTTP_200_OK),
699-
(UserRole.TESTER, status.HTTP_200_OK),
700-
],
701-
)
702-
async def test_replace_project_updated_inputs(
703-
client: TestClient,
704-
logged_user: UserInfoDict,
705-
user_project: ProjectDict,
706-
expected,
707-
ensure_run_in_sequence_context_is_empty,
708-
):
709-
project_update = deepcopy(user_project)
710-
#
711-
# "inputAccess": {
712-
# "Na": "ReadAndWrite", <--------
713-
# "Kr": "ReadOnly",
714-
# "BCL": "ReadAndWrite",
715-
# "NBeats": "ReadOnly",
716-
# "Ligand": "Invisible",
717-
# "cAMKII": "Invisible"
718-
# },
719-
project_update["workbench"]["5739e377-17f7-4f09-a6ad-62659fb7fdec"]["inputs"][
720-
"Na"
721-
] = 55
722-
await _replace_project(client, project_update, expected)
723-
724-
725-
@pytest.mark.parametrize(
726-
"user_role,expected",
727-
[
728-
(UserRole.ANONYMOUS, status.HTTP_401_UNAUTHORIZED),
729-
(UserRole.GUEST, status.HTTP_200_OK),
730-
(UserRole.USER, status.HTTP_200_OK),
731-
(UserRole.TESTER, status.HTTP_200_OK),
732-
],
733-
)
734-
async def test_replace_project_updated_readonly_inputs(
735-
client: TestClient,
736-
logged_user: UserInfoDict,
737-
user_project: ProjectDict,
738-
expected,
739-
ensure_run_in_sequence_context_is_empty,
740-
):
741-
project_update = deepcopy(user_project)
742-
project_update["workbench"]["5739e377-17f7-4f09-a6ad-62659fb7fdec"]["inputs"][
743-
"Na"
744-
] = 55
745-
project_update["workbench"]["5739e377-17f7-4f09-a6ad-62659fb7fdec"]["inputs"][
746-
"Kr"
747-
] = 5
748-
await _replace_project(client, project_update, expected)
749-
750-
751-
@pytest.fixture
752-
def random_minimal_node(faker: Faker) -> Callable[[], Node]:
753-
def _creator() -> Node:
754-
return Node(
755-
key=ServiceKey(f"simcore/services/comp/{faker.pystr().lower()}"),
756-
version=faker.numerify("#.#.#"),
757-
label=faker.pystr(),
758-
)
759-
760-
return _creator
761-
762-
763-
@pytest.mark.parametrize(
764-
"user_role,expected",
765-
[
766-
(UserRole.ANONYMOUS, status.HTTP_401_UNAUTHORIZED),
767-
(UserRole.GUEST, status.HTTP_409_CONFLICT),
768-
(UserRole.USER, status.HTTP_409_CONFLICT),
769-
(UserRole.TESTER, status.HTTP_409_CONFLICT),
770-
],
771-
)
772-
async def test_replace_project_adding_or_removing_nodes_raises_conflict(
773-
client: TestClient,
774-
logged_user: UserInfoDict,
775-
user_project: ProjectDict,
776-
expected,
777-
ensure_run_in_sequence_context_is_empty,
778-
faker: Faker,
779-
random_minimal_node: Callable[[], Node],
780-
):
781-
# try adding a node should not work
782-
project_update = deepcopy(user_project)
783-
new_node = random_minimal_node()
784-
project_update["workbench"][faker.uuid4()] = jsonable_encoder(new_node)
785-
await _replace_project(client, project_update, expected)
786-
# try removing a node should not work
787-
project_update = deepcopy(user_project)
788-
project_update["workbench"].pop(
789-
random.choice(list(project_update["workbench"].keys())) # noqa: S311
790-
)
791-
await _replace_project(client, project_update, expected)
792-
793-
794654
@pytest.fixture
795655
def mock_director_v2_inactivity(
796656
aioresponses_mocker: aioresponses, is_inactive: bool

services/web/server/tests/unit/with_dbs/02/test_projects_states_handlers.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -112,15 +112,18 @@ async def _replace_project(
112112
) -> ProjectDict:
113113
assert client.app
114114

115-
# PUT /v0/projects/{project_id}
116-
url = client.app.router["replace_project"].url_for(
117-
project_id=project_update["uuid"]
118-
)
115+
# PATCH /v0/projects/{project_id}
116+
url = client.app.router["patch_project"].url_for(project_id=project_update["uuid"])
119117
assert str(url) == f"{API_PREFIX}/projects/{project_update['uuid']}"
120-
resp = await client.put(f"{url}", json=project_update)
118+
resp = await client.patch(f"{url}", json=project_update)
121119
data, error = await assert_status(resp, expected)
122120
if not error:
123-
assert_replaced(current_project=data, update_data=project_update)
121+
url = client.app.router["get_project"].url_for(
122+
project_id=project_update["uuid"]
123+
)
124+
resp = await client.get(f"{url}")
125+
get_data, _ = await assert_status(resp, HTTPStatus.OK)
126+
assert_replaced(current_project=get_data, update_data=project_update)
124127
return data
125128

126129

@@ -307,10 +310,11 @@ async def test_share_project(
307310
# user 2 can update the project if user 2 has write access
308311
project_update = deepcopy(new_project)
309312
project_update["name"] = "my super name"
313+
project_update.pop("accessRights")
310314
await _replace_project(
311315
client,
312316
project_update,
313-
expected.ok if share_rights["write"] else expected.forbidden,
317+
expected.no_content if share_rights["write"] else expected.forbidden,
314318
)
315319

316320
# user 2 can delete projects if user 2 has delete access

services/web/server/tests/unit/with_dbs/03/meta_modeling/test_meta_modeling_iterations.py

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from models_library.projects import Project
1212
from models_library.projects_nodes import Node
1313
from models_library.services_resources import ServiceResourcesDict
14-
from models_library.utils.json_serialization import json_dumps
14+
from models_library.utils.json_serialization import json_dumps, json_loads
1515
from pytest_mock import MockerFixture
1616
from pytest_simcore.helpers.assert_checks import assert_status
1717
from pytest_simcore.helpers.monkeypatch_envs import EnvVarsDict, setenvs_from_dict
@@ -34,6 +34,7 @@
3434
meta_project_policy,
3535
projects_redirection_middleware,
3636
)
37+
from simcore_service_webserver.projects.db import ProjectDBAPI
3738
from simcore_service_webserver.projects.models import ProjectDict
3839

3940
REQUEST_MODEL_POLICY = {
@@ -144,13 +145,22 @@ async def test_iterators_workflow(
144145
project_data.update({key: modifications[key] for key in ("workbench", "ui")})
145146
project_data["ui"].setdefault("currentNodeId", project_uuid)
146147

147-
response = await client.put(
148-
f"/v0/projects/{project_data['uuid']}",
149-
json=project_data,
148+
# response = await client.patch(
149+
# f"/v0/projects/{project_data['uuid']}",
150+
# json=project_data,
151+
# )
152+
# assert (
153+
# response.status == status.HTTP_204_NO_CONTENT
154+
# ), await response.text()
155+
156+
db: ProjectDBAPI = ProjectDBAPI.get_from_app_context(client.app)
157+
project_data.pop("state")
158+
await db.replace_project(
159+
project_data,
160+
logged_user["id"],
161+
project_uuid=project_uuid,
162+
product_name="osparc",
150163
)
151-
assert (
152-
response.status == REPLACE_PROJECT_ON_MODIFIED.status_code
153-
), await response.text()
154164

155165
# TODO: create iterations, so user could explore parametrizations?
156166

@@ -260,11 +270,20 @@ async def _mock_catalog_get(*args, **kwarg):
260270
assert node.inputs
261271
node.inputs["linspace_stop"] = 4
262272

263-
response = await client.put(
264-
f"/v0/projects/{project_uuid}",
265-
data=json_dumps(new_project.dict(**REQUEST_MODEL_POLICY)),
273+
# response = await client.patch(
274+
# f"/v0/projects/{project_uuid}",
275+
# data=json_dumps(new_project.dict(**REQUEST_MODEL_POLICY)),
276+
# )
277+
# assert response.status == status.HTTP_204_NO_CONTENT, await response.text()
278+
# db: ProjectDBAPI = ProjectDBAPI.get_from_app_context(client.app)
279+
_new_project_data = new_project.dict(**REQUEST_MODEL_POLICY)
280+
_new_project_data.pop("state")
281+
await db.replace_project(
282+
json_loads(json_dumps(_new_project_data)),
283+
logged_user["id"],
284+
project_uuid=project_uuid,
285+
product_name="osparc",
266286
)
267-
assert response.status == status.HTTP_200_OK, await response.text()
268287

269288
# RUN again them ---------------------------------------------------------------------------
270289
response = await client.post(

0 commit comments

Comments
 (0)