From 50594d6dacdbf31e8973492b6540c09f42550f58 Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Fri, 18 Oct 2024 15:58:05 +0200 Subject: [PATCH 01/16] status code --- .../{status_utils.py => status_codes_utils.py} | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) rename packages/service-library/src/servicelib/{status_utils.py => status_codes_utils.py} (87%) diff --git a/packages/service-library/src/servicelib/status_utils.py b/packages/service-library/src/servicelib/status_codes_utils.py similarity index 87% rename from packages/service-library/src/servicelib/status_utils.py rename to packages/service-library/src/servicelib/status_codes_utils.py index 2ebbf61d19b..d50ae7ec681 100644 --- a/packages/service-library/src/servicelib/status_utils.py +++ b/packages/service-library/src/servicelib/status_codes_utils.py @@ -2,7 +2,7 @@ - on aiohttp services from servicelib.aiohttp import status - from servicelib.status_utils import is_success + from servicelib.status_codes_utils import is_success assert is_success(status.HTTP_200_OK) @@ -10,7 +10,7 @@ - on fastapi services from fastapi import status - from servicelib.status_utils import is_success + from servicelib.status_codes_utils import is_success assert is_success(status.HTTP_200_OK) @@ -37,6 +37,7 @@ def get_code_display_name(status_code: int) -> str: return f"HTTP_{status_code}_{code.name}" except ValueError: if status_code == 306: # noqa: PLR2004 + # NOTE: HttpStatus does not include 306 return "HTTP_306_RESERVED" return _INVALID_STATUS_CODE_MSG @@ -65,35 +66,35 @@ def get_code_description(status_code: int) -> str: ) -def is_informational(status_code: int) -> bool: +def is_1xx_informational(status_code: int) -> bool: """ Returns `True` for 1xx status codes, `False` otherwise. """ return 100 <= status_code <= 199 # noqa: PLR2004 -def is_success(status_code: int) -> bool: +def is_2xx_success(status_code: int) -> bool: """ Returns `True` for 2xx status codes, `False` otherwise. """ return 200 <= status_code <= 299 # noqa: PLR2004 -def is_redirect(status_code: int) -> bool: +def is_3xx_redirect(status_code: int) -> bool: """ Returns `True` for 3xx status codes, `False` otherwise. """ return 300 <= status_code <= 399 # noqa: PLR2004 -def is_client_error(status_code: int) -> bool: +def is_4xx_client_error(status_code: int) -> bool: """ Returns `True` for 4xx status codes, `False` otherwise. """ return 400 <= status_code <= 499 # noqa: PLR2004 -def is_server_error(status_code: int) -> bool: +def is_5xx_server_error(status_code: int) -> bool: """ Returns `True` for 5xx status codes, `False` otherwise. """ From 322cc7da12a3c5da1b3ecbe8b05055a5dccdb23b Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Fri, 18 Oct 2024 16:02:07 +0200 Subject: [PATCH 02/16] adds tests --- .../tests/aiohttp/test__models_examples.py | 19 +++++++++ .../tests/aiohttp/test_status_utils.py | 41 +++++++++++++------ 2 files changed, 48 insertions(+), 12 deletions(-) create mode 100644 packages/service-library/tests/aiohttp/test__models_examples.py diff --git a/packages/service-library/tests/aiohttp/test__models_examples.py b/packages/service-library/tests/aiohttp/test__models_examples.py new file mode 100644 index 00000000000..c72bdfffe7f --- /dev/null +++ b/packages/service-library/tests/aiohttp/test__models_examples.py @@ -0,0 +1,19 @@ +import json +from typing import Any + +import pytest +import servicelib.aiohttp +from pydantic import BaseModel +from pytest_simcore.pydantic_models import walk_model_examples_in_package + + +@pytest.mark.parametrize( + "model_cls, example_name, example_data", + walk_model_examples_in_package(servicelib.aiohttp), +) +def test_all_models_config_examples_in_servicelib_aiohttp_package( + model_cls: type[BaseModel], example_name: int, example_data: Any +): + assert model_cls.parse_obj( + example_data + ), f"Failed {example_name} : {json.dumps(example_data)}" diff --git a/packages/service-library/tests/aiohttp/test_status_utils.py b/packages/service-library/tests/aiohttp/test_status_utils.py index cfddc0294f9..7e6415860a5 100644 --- a/packages/service-library/tests/aiohttp/test_status_utils.py +++ b/packages/service-library/tests/aiohttp/test_status_utils.py @@ -1,17 +1,22 @@ from http import HTTPStatus +import pytest from servicelib.aiohttp import status -from servicelib.status_utils import ( +from servicelib.aiohttp.web_exceptions_extension import ( + STATUS_CODES_WITHOUT_AIOHTTP_EXCEPTION_CLASS, + get_all_aiohttp_http_exceptions, +) +from servicelib.status_codes_utils import ( _INVALID_STATUS_CODE_MSG, get_code_description, get_code_display_name, get_http_status_codes, - is_client_error, + is_1xx_informational, + is_2xx_success, + is_3xx_redirect, + is_4xx_client_error, + is_5xx_server_error, is_error, - is_informational, - is_redirect, - is_server_error, - is_success, ) @@ -31,12 +36,12 @@ def test_description(): def test_status_codes_checks(): - assert is_informational(status.HTTP_102_PROCESSING) - assert is_success(status.HTTP_202_ACCEPTED) - assert is_redirect(status.HTTP_301_MOVED_PERMANENTLY) + assert is_1xx_informational(status.HTTP_102_PROCESSING) + assert is_2xx_success(status.HTTP_202_ACCEPTED) + assert is_3xx_redirect(status.HTTP_301_MOVED_PERMANENTLY) - assert is_client_error(status.HTTP_401_UNAUTHORIZED) - assert is_server_error(status.HTTP_503_SERVICE_UNAVAILABLE) + assert is_4xx_client_error(status.HTTP_401_UNAUTHORIZED) + assert is_5xx_server_error(status.HTTP_503_SERVICE_UNAVAILABLE) assert is_error(status.HTTP_401_UNAUTHORIZED) assert is_error(status.HTTP_503_SERVICE_UNAVAILABLE) @@ -45,7 +50,7 @@ def test_status_codes_checks(): def test_predicates_with_status(): # in formational - assert get_http_status_codes(status, is_informational) == [ + assert get_http_status_codes(status, is_1xx_informational) == [ status.HTTP_100_CONTINUE, status.HTTP_101_SWITCHING_PROTOCOLS, status.HTTP_102_PROCESSING, @@ -61,3 +66,15 @@ def test_predicates_with_status(): for c in get_http_status_codes(status) if c != status.HTTP_306_RESERVED ] + + +AIOHTTP_EXCEPTION_CLASSES_MAP = get_all_aiohttp_http_exceptions() + + +@pytest.mark.parametrize("status_code", get_http_status_codes(status)) +def test_how_status_codes_map_to_aiohttp_exception_class(status_code): + aiohttp_exception_cls = AIOHTTP_EXCEPTION_CLASSES_MAP.get(status_code) + if status_code in STATUS_CODES_WITHOUT_AIOHTTP_EXCEPTION_CLASS: + assert aiohttp_exception_cls is None + else: + assert aiohttp_exception_cls is not None From b3544020543465ecce7aae4e2372b518659f695d Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Fri, 18 Oct 2024 16:29:39 +0200 Subject: [PATCH 03/16] returns instead of raise --- .../aiohttp/long_running_tasks/_routes.py | 2 +- .../tests/aiohttp/test__aiohttp_web.py | 7 +++++++ .../simcore_service_storage/handlers_files.py | 4 ++-- .../handlers_simcore_s3.py | 2 +- .../api_keys/_handlers.py | 2 +- .../director_v2/_handlers.py | 8 +------- .../folders/_folders_handlers.py | 2 +- .../groups/_handlers.py | 6 +++--- .../login/_registration_handlers.py | 2 +- .../projects/_comments_handlers.py | 2 +- .../projects/_crud_handlers.py | 4 ++-- .../projects/_folders_handlers.py | 2 +- .../projects/_groups_handlers.py | 2 +- .../projects/_nodes_handlers.py | 18 +++++++++--------- .../projects/_states_handlers.py | 2 +- .../projects/_workspaces_handlers.py | 2 +- .../publications/_handlers.py | 2 +- .../tags/_handlers.py | 2 +- .../users/_handlers.py | 2 +- .../users/_notifications_handlers.py | 6 +++--- .../users/_preferences_handlers.py | 2 +- .../users/_tokens_handlers.py | 2 +- .../wallets/_groups_handlers.py | 2 +- .../workspaces/_workspaces_handlers.py | 2 +- 24 files changed, 44 insertions(+), 43 deletions(-) create mode 100644 packages/service-library/tests/aiohttp/test__aiohttp_web.py diff --git a/packages/service-library/src/servicelib/aiohttp/long_running_tasks/_routes.py b/packages/service-library/src/servicelib/aiohttp/long_running_tasks/_routes.py index 8aad57ba6ce..1cade423f3e 100644 --- a/packages/service-library/src/servicelib/aiohttp/long_running_tasks/_routes.py +++ b/packages/service-library/src/servicelib/aiohttp/long_running_tasks/_routes.py @@ -89,7 +89,7 @@ async def cancel_and_delete_task(request: web.Request) -> web.Response: tasks_manager = get_tasks_manager(request.app) task_context = get_task_context(request) await tasks_manager.remove_task(path_params.task_id, with_task_context=task_context) - raise web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) __all__: tuple[str, ...] = ( diff --git a/packages/service-library/tests/aiohttp/test__aiohttp_web.py b/packages/service-library/tests/aiohttp/test__aiohttp_web.py new file mode 100644 index 00000000000..16c5cb4c513 --- /dev/null +++ b/packages/service-library/tests/aiohttp/test__aiohttp_web.py @@ -0,0 +1,7 @@ +from aiohttp import web +from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON + + +def test_http_errors(): + + err = web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) diff --git a/services/storage/src/simcore_service_storage/handlers_files.py b/services/storage/src/simcore_service_storage/handlers_files.py index 8cb96d267a2..2af7a98ea99 100644 --- a/services/storage/src/simcore_service_storage/handlers_files.py +++ b/services/storage/src/simcore_service_storage/handlers_files.py @@ -261,7 +261,7 @@ async def abort_upload_file(request: web.Request) -> NoReturn: dsm = get_dsm_provider(request.app).get(path_params.location_id) await dsm.abort_file_upload(query_params.user_id, path_params.file_id) - raise web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) @routes.post( @@ -386,7 +386,7 @@ async def delete_file(request: web.Request) -> NoReturn: dsm = get_dsm_provider(request.app).get(path_params.location_id) await dsm.delete_file(query_params.user_id, path_params.file_id) - raise web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) @routes.post(f"/{API_VTAG}/files/{{file_id}}:soft-copy", name="copy_as_soft_link") diff --git a/services/storage/src/simcore_service_storage/handlers_simcore_s3.py b/services/storage/src/simcore_service_storage/handlers_simcore_s3.py index a4fff084329..7a04a00bd42 100644 --- a/services/storage/src/simcore_service_storage/handlers_simcore_s3.py +++ b/services/storage/src/simcore_service_storage/handlers_simcore_s3.py @@ -126,7 +126,7 @@ async def delete_folders_of_project(request: web.Request) -> NoReturn: query_params.node_id, ) - raise web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) @routes.post(f"/{API_VTAG}/simcore-s3/files/metadata:search", name="search_files") diff --git a/services/web/server/src/simcore_service_webserver/api_keys/_handlers.py b/services/web/server/src/simcore_service_webserver/api_keys/_handlers.py index ce7a7be0943..517ca037a4d 100644 --- a/services/web/server/src/simcore_service_webserver/api_keys/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/api_keys/_handlers.py @@ -84,4 +84,4 @@ async def delete_api_key(request: web.Request): "Failed to delete API key %s. Ignoring error", name, exc_info=err ) - raise web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) diff --git a/services/web/server/src/simcore_service_webserver/director_v2/_handlers.py b/services/web/server/src/simcore_service_webserver/director_v2/_handlers.py index fb80f3aa3fa..44b6092bc52 100644 --- a/services/web/server/src/simcore_service_webserver/director_v2/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/director_v2/_handlers.py @@ -204,13 +204,7 @@ async def stop_computation(request: web.Request) -> web.Response: await asyncio.gather( *[computations.stop(pid, req_ctx.user_id) for pid in project_ids] ) - - # NOTE: our middleware has this issue - # - # if 'return web.HTTPNoContent()' then 'await response.json()' raises ContentTypeError - # if 'raise web.HTTPNoContent()' then 'await response.json() == None' - # - raise web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) except DirectorServiceError as exc: return create_http_error( diff --git a/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py b/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py index 4b212524079..cdcdc49fd16 100644 --- a/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py +++ b/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py @@ -246,4 +246,4 @@ async def delete_folder_group(request: web.Request): folder_id=path_params.folder_id, product_name=req_ctx.product_name, ) - raise web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) diff --git a/services/web/server/src/simcore_service_webserver/groups/_handlers.py b/services/web/server/src/simcore_service_webserver/groups/_handlers.py index 2f0b0411601..3f31d1b8972 100644 --- a/services/web/server/src/simcore_service_webserver/groups/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/groups/_handlers.py @@ -169,7 +169,7 @@ async def delete_group(request: web.Request): path_params = parse_request_path_parameters_as(_GroupPathParams, request) await api.delete_user_group(request.app, req_ctx.user_id, path_params.gid) - raise web.HTTPNoContent + return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) @routes.get(f"/{API_VTAG}/groups/{{gid}}/users", name="get_group_users") @@ -215,7 +215,7 @@ async def add_group_user(request: web.Request): new_user_id=new_user_id, new_user_email=new_user_email, ) - raise web.HTTPNoContent + return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) class _GroupUserPathParams(BaseModel): @@ -275,7 +275,7 @@ async def delete_group_user(request: web.Request): await api.delete_user_in_group( request.app, req_ctx.user_id, path_params.gid, path_params.uid ) - raise web.HTTPNoContent + return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) # diff --git a/services/web/server/src/simcore_service_webserver/login/_registration_handlers.py b/services/web/server/src/simcore_service_webserver/login/_registration_handlers.py index 2ad608e3c12..09337aa0b9f 100644 --- a/services/web/server/src/simcore_service_webserver/login/_registration_handlers.py +++ b/services/web/server/src/simcore_service_webserver/login/_registration_handlers.py @@ -85,7 +85,7 @@ async def request_product_account(request: web.Request): task_suffix_name=f"{__name__}.request_product_account.send_account_request_email_to_support", fire_and_forget_tasks_collection=request.app[APP_FIRE_AND_FORGET_TASKS_KEY], ) - raise web.HTTPNoContent + return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) class _AuthenticatedContext(BaseModel): diff --git a/services/web/server/src/simcore_service_webserver/projects/_comments_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_comments_handlers.py index fff41cd016c..90aae8569fb 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_comments_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_comments_handlers.py @@ -223,7 +223,7 @@ async def delete_project_comment(request: web.Request): request=request, comment_id=path_params.comment_id, ) - raise web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) @routes.get( diff --git a/services/web/server/src/simcore_service_webserver/projects/_crud_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_crud_handlers.py index d2cce731d21..6897dbd2954 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_crud_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_crud_handlers.py @@ -576,7 +576,7 @@ async def patch_project(request: web.Request): product_name=req_ctx.product_name, ) - raise web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) # @@ -661,7 +661,7 @@ async def delete_project(request: web.Request): except ProjectDeleteError as err: raise web.HTTPConflict(reason=f"{err}") from err - raise web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) # diff --git a/services/web/server/src/simcore_service_webserver/projects/_folders_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_folders_handlers.py index 591fecf8a94..c44cfd5c39e 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_folders_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_folders_handlers.py @@ -69,4 +69,4 @@ async def replace_project_folder(request: web.Request): folder_id=path_params.folder_id, product_name=req_ctx.product_name, ) - raise web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) diff --git a/services/web/server/src/simcore_service_webserver/projects/_groups_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_groups_handlers.py index 607dd499df2..93075e0754f 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_groups_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_groups_handlers.py @@ -154,4 +154,4 @@ async def delete_project_group(request: web.Request): group_id=path_params.group_id, product_name=req_ctx.product_name, ) - raise web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) diff --git a/services/web/server/src/simcore_service_webserver/projects/_nodes_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_nodes_handlers.py index 255260c60ad..4fd7daf254f 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_nodes_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_nodes_handlers.py @@ -238,7 +238,7 @@ async def patch_project_node(request: web.Request) -> web.Response: node_patch=node_patch, ) - raise web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) @routes.delete(f"/{VTAG}/projects/{{project_id}}/nodes/{{node_id}}", name="delete_node") @@ -263,7 +263,7 @@ async def delete_node(request: web.Request) -> web.Response: req_ctx.product_name, ) - raise web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) @routes.post( @@ -333,7 +333,7 @@ async def start_node(request: web.Request) -> web.Response: node_id=path_params.node_id, ) - raise web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) async def _stop_dynamic_service_task( @@ -347,15 +347,15 @@ async def _stop_dynamic_service_task( await dynamic_scheduler_api.stop_dynamic_service( app, dynamic_service_stop=dynamic_service_stop ) - raise web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) - # in case there is an error reply as not found except (RPCServerError, ServiceWaitingForManualInterventionError) as exc: + # in case there is an error reply as not found raise web.HTTPNotFound(reason=f"{exc}") from exc - # in case the service is not found reply as all OK - except ServiceWasNotFoundError as exc: - raise web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) from exc + except ServiceWasNotFoundError: + # in case the service is not found reply as all OK + return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) @routes.post( @@ -413,7 +413,7 @@ async def restart_node(request: web.Request) -> web.Response: await director_v2_api.restart_dynamic_service(request.app, f"{path_params.node_id}") - raise web.HTTPNoContent + return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) # diff --git a/services/web/server/src/simcore_service_webserver/projects/_states_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_states_handlers.py index fe7c62960f0..7e9fb932639 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_states_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_states_handlers.py @@ -222,7 +222,7 @@ async def close_project(request: web.Request) -> web.Response: ), ) await project_logs.unsubscribe(request.app, path_params.project_id) - raise web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) # diff --git a/services/web/server/src/simcore_service_webserver/projects/_workspaces_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_workspaces_handlers.py index e262ce8dc29..d00fd684666 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_workspaces_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_workspaces_handlers.py @@ -81,4 +81,4 @@ async def replace_project_workspace(request: web.Request): workspace_id=path_params.workspace_id, product_name=req_ctx.product_name, ) - raise web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) diff --git a/services/web/server/src/simcore_service_webserver/publications/_handlers.py b/services/web/server/src/simcore_service_webserver/publications/_handlers.py index 3a88ca641c5..8cc4f545702 100644 --- a/services/web/server/src/simcore_service_webserver/publications/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/publications/_handlers.py @@ -93,4 +93,4 @@ async def service_submission(request: web.Request): _logger.exception("Error while sending the 'new service submission' mail.") raise web.HTTPServiceUnavailable from exc - raise web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) diff --git a/services/web/server/src/simcore_service_webserver/tags/_handlers.py b/services/web/server/src/simcore_service_webserver/tags/_handlers.py index 5492d5e468e..03c5eb2eda3 100644 --- a/services/web/server/src/simcore_service_webserver/tags/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/tags/_handlers.py @@ -107,7 +107,7 @@ async def delete_tag(request: web.Request): request.app, user_id=req_ctx.user_id, tag_id=path_params.tag_id ) - raise web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) # diff --git a/services/web/server/src/simcore_service_webserver/users/_handlers.py b/services/web/server/src/simcore_service_webserver/users/_handlers.py index 3e2018c7d9b..87d96a8e5f1 100644 --- a/services/web/server/src/simcore_service_webserver/users/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/users/_handlers.py @@ -85,7 +85,7 @@ async def update_my_profile(request: web.Request) -> web.Response: await api.update_user_profile( request.app, req_ctx.user_id, profile_update, as_patch=False ) - raise web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) class _SearchQueryParams(BaseModel): diff --git a/services/web/server/src/simcore_service_webserver/users/_notifications_handlers.py b/services/web/server/src/simcore_service_webserver/users/_notifications_handlers.py index b30a435210b..4b984f5f79e 100644 --- a/services/web/server/src/simcore_service_webserver/users/_notifications_handlers.py +++ b/services/web/server/src/simcore_service_webserver/users/_notifications_handlers.py @@ -84,7 +84,7 @@ async def create_user_notification(request: web.Request) -> web.Response: pipe.ltrim(key, 0, MAX_NOTIFICATIONS_FOR_USER_TO_KEEP - 1) await pipe.execute() - raise web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) class _NotificationPathParams(BaseModel): @@ -115,9 +115,9 @@ async def mark_notification_as_read(request: web.Request) -> web.Response: await handle_redis_returns_union_types( redis_client.lset(key, k, user_notification.json()) ) - raise web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) - raise web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) @routes.get(f"/{API_VTAG}/me/permissions", name="list_user_permissions") diff --git a/services/web/server/src/simcore_service_webserver/users/_preferences_handlers.py b/services/web/server/src/simcore_service_webserver/users/_preferences_handlers.py index 9f5513b904f..c32b0536570 100644 --- a/services/web/server/src/simcore_service_webserver/users/_preferences_handlers.py +++ b/services/web/server/src/simcore_service_webserver/users/_preferences_handlers.py @@ -66,4 +66,4 @@ async def set_frontend_preference(request: web.Request) -> web.Response: frontend_preference_identifier=req_path_params.preference_id, value=req_body.value, ) - raise web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) diff --git a/services/web/server/src/simcore_service_webserver/users/_tokens_handlers.py b/services/web/server/src/simcore_service_webserver/users/_tokens_handlers.py index ddca5a94b2f..7adb8fc8ad3 100644 --- a/services/web/server/src/simcore_service_webserver/users/_tokens_handlers.py +++ b/services/web/server/src/simcore_service_webserver/users/_tokens_handlers.py @@ -85,4 +85,4 @@ async def delete_token(request: web.Request) -> web.Response: req_ctx = UsersRequestContext.parse_obj(request) req_path_params = parse_request_path_parameters_as(_TokenPathParams, request) await _tokens.delete_token(request.app, req_ctx.user_id, req_path_params.service) - raise web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) diff --git a/services/web/server/src/simcore_service_webserver/wallets/_groups_handlers.py b/services/web/server/src/simcore_service_webserver/wallets/_groups_handlers.py index 6690d6d41e4..bbc67b24a21 100644 --- a/services/web/server/src/simcore_service_webserver/wallets/_groups_handlers.py +++ b/services/web/server/src/simcore_service_webserver/wallets/_groups_handlers.py @@ -161,4 +161,4 @@ async def delete_wallet_group(request: web.Request): group_id=path_params.group_id, product_name=req_ctx.product_name, ) - raise web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) diff --git a/services/web/server/src/simcore_service_webserver/workspaces/_workspaces_handlers.py b/services/web/server/src/simcore_service_webserver/workspaces/_workspaces_handlers.py index 5cc49639334..2d1657e9e53 100644 --- a/services/web/server/src/simcore_service_webserver/workspaces/_workspaces_handlers.py +++ b/services/web/server/src/simcore_service_webserver/workspaces/_workspaces_handlers.py @@ -209,4 +209,4 @@ async def delete_workspace(request: web.Request): workspace_id=path_params.workspace_id, product_name=req_ctx.product_name, ) - raise web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) From b7fdb3173127f504752af2a8ae0468716e782884 Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Fri, 18 Oct 2024 17:33:50 +0200 Subject: [PATCH 04/16] missing --- .../simcore_service_webserver/workspaces/_groups_handlers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/src/simcore_service_webserver/workspaces/_groups_handlers.py b/services/web/server/src/simcore_service_webserver/workspaces/_groups_handlers.py index 0bf7d09eb68..79c50ce3b91 100644 --- a/services/web/server/src/simcore_service_webserver/workspaces/_groups_handlers.py +++ b/services/web/server/src/simcore_service_webserver/workspaces/_groups_handlers.py @@ -162,4 +162,4 @@ async def delete_workspace_group(request: web.Request): group_id=path_params.group_id, product_name=req_ctx.product_name, ) - raise web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) From f36d43bf81098246c7e3270d39dbfabb6c3f5b43 Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Fri, 18 Oct 2024 17:36:23 +0200 Subject: [PATCH 05/16] bad imports --- .../pytest-simcore/src/pytest_simcore/helpers/assert_checks.py | 3 ++- .../src/pytest_simcore/helpers/webserver_parametrizations.py | 2 +- .../service-library/src/servicelib/aiohttp/rest_responses.py | 2 +- services/web/server/tests/integration/02/test_computation.py | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/pytest-simcore/src/pytest_simcore/helpers/assert_checks.py b/packages/pytest-simcore/src/pytest_simcore/helpers/assert_checks.py index e4df1c1bf80..2f71de33e25 100644 --- a/packages/pytest-simcore/src/pytest_simcore/helpers/assert_checks.py +++ b/packages/pytest-simcore/src/pytest_simcore/helpers/assert_checks.py @@ -7,7 +7,7 @@ from aiohttp import ClientResponse from servicelib.aiohttp import status from servicelib.aiohttp.rest_responses import unwrap_envelope -from servicelib.status_utils import get_code_display_name, is_error +from servicelib.status_codes_utils import get_code_display_name, is_error async def assert_status( @@ -15,6 +15,7 @@ async def assert_status( expected_status_code: int, expected_msg: str | None = None, expected_error_code: str | None = None, + *, include_meta: bool | None = False, include_links: bool | None = False, ) -> tuple[dict, ...]: diff --git a/packages/pytest-simcore/src/pytest_simcore/helpers/webserver_parametrizations.py b/packages/pytest-simcore/src/pytest_simcore/helpers/webserver_parametrizations.py index 747d1a1193f..6422122f4f4 100644 --- a/packages/pytest-simcore/src/pytest_simcore/helpers/webserver_parametrizations.py +++ b/packages/pytest-simcore/src/pytest_simcore/helpers/webserver_parametrizations.py @@ -2,7 +2,7 @@ from unittest import mock from servicelib.aiohttp import status -from servicelib.status_utils import get_code_display_name +from servicelib.status_codes_utils import get_code_display_name from simcore_postgres_database.models.users import UserRole diff --git a/packages/service-library/src/servicelib/aiohttp/rest_responses.py b/packages/service-library/src/servicelib/aiohttp/rest_responses.py index dda73210865..9e67dcc29ca 100644 --- a/packages/service-library/src/servicelib/aiohttp/rest_responses.py +++ b/packages/service-library/src/servicelib/aiohttp/rest_responses.py @@ -13,7 +13,7 @@ from servicelib.aiohttp.status import HTTP_200_OK from ..mimetype_constants import MIMETYPE_APPLICATION_JSON -from ..status_utils import get_code_description +from ..status_codes_utils import get_code_description from .rest_models import ErrorItemType, ErrorType _ENVELOPE_KEYS = ("data", "error") diff --git a/services/web/server/tests/integration/02/test_computation.py b/services/web/server/tests/integration/02/test_computation.py index 2aa2da0f3dd..0a73402e68d 100644 --- a/services/web/server/tests/integration/02/test_computation.py +++ b/services/web/server/tests/integration/02/test_computation.py @@ -20,7 +20,7 @@ from pytest_simcore.helpers.assert_checks import assert_status from servicelib.aiohttp import status from servicelib.aiohttp.application import create_safe_application -from servicelib.status_utils import get_code_display_name +from servicelib.status_codes_utils import get_code_display_name from settings_library.rabbit import RabbitSettings from settings_library.redis import RedisSettings from simcore_postgres_database.models.projects import projects From 25bb3ec11ed02b41a9914e880b59b179cce2dd2b Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Fri, 18 Oct 2024 17:38:15 +0200 Subject: [PATCH 06/16] udpates web-exceptions-extension --- .../aiohttp/web_exceptions_extension.py | 71 ++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/packages/service-library/src/servicelib/aiohttp/web_exceptions_extension.py b/packages/service-library/src/servicelib/aiohttp/web_exceptions_extension.py index 47bfa594171..7406fce6ae5 100644 --- a/packages/service-library/src/servicelib/aiohttp/web_exceptions_extension.py +++ b/packages/service-library/src/servicelib/aiohttp/web_exceptions_extension.py @@ -1,8 +1,77 @@ -from aiohttp.web_exceptions import HTTPClientError +import inspect +from typing import Any + +from aiohttp import web_exceptions +from aiohttp.web_exceptions import ( + HTTPClientError, + HTTPError, + HTTPException, + HTTPServerError, +) from . import status +# NOTE: these are the status codes that DO NOT have an aiohttp.HTTPException associated +STATUS_CODES_WITHOUT_AIOHTTP_EXCEPTION_CLASS = ( + status.HTTP_100_CONTINUE, + status.HTTP_101_SWITCHING_PROTOCOLS, + status.HTTP_102_PROCESSING, + status.HTTP_103_EARLY_HINTS, + status.HTTP_207_MULTI_STATUS, + status.HTTP_208_ALREADY_REPORTED, + status.HTTP_226_IM_USED, + status.HTTP_306_RESERVED, + status.HTTP_418_IM_A_TEAPOT, + status.HTTP_425_TOO_EARLY, +) + class HTTPLockedError(HTTPClientError): # pylint: disable=too-many-ancestors status_code = status.HTTP_423_LOCKED + + +class HTTPLoopDetectedError(HTTPServerError): + # pylint: disable=too-many-ancestors + status_code = status.HTTP_508_LOOP_DETECTED + + +def get_all_aiohttp_http_exceptions( + exception_cls: type[HTTPException] = HTTPException, +) -> dict[int, type[HTTPException]]: + # Inverse map from code to HTTPException classes + + def _pred(obj) -> bool: + return ( + inspect.isclass(obj) + and issubclass(obj, exception_cls) + and getattr(obj, "status_code", 0) > 0 + ) + + found: list[tuple[str, Any]] = inspect.getmembers(web_exceptions, _pred) + assert found # nosec + + status_to_http_exception_map = {cls.status_code: cls for _, cls in found} + assert len(status_to_http_exception_map) == len(found), "No duplicates" # nosec + + for cls in ( + HTTPLockedError, + HTTPLoopDetectedError, + ): + + status_to_http_exception_map[cls.status_code] = cls # type:ignore + + return status_to_http_exception_map + + +_STATUS_CODE_TO_HTTP_ERRORS: dict[ + int, type[HTTPError] +] = get_all_aiohttp_http_exceptions(HTTPError) + + +def get_http_error_class_or_none(status_code: int) -> type[HTTPError] | None: + """Returns aiohttp error class corresponding to a 4XX or 5XX status code + + NOTE: any non-error code (i.e. 2XX, 3XX and 4XX) will return None + """ + return _STATUS_CODE_TO_HTTP_ERRORS.get(status_code) From 4d26c344a27104e42224d0f7bb3dc8903662b707 Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Fri, 18 Oct 2024 17:48:10 +0200 Subject: [PATCH 07/16] mv functions from rest-responses to web-exceptions --- .../src/servicelib/aiohttp/rest_responses.py | 13 ------------- .../aiohttp/web_exceptions_extension.py | 4 ++++ .../tests/aiohttp/test_rest_responses.py | 13 ++++++------- .../director_v2/_handlers.py | 16 ++++++++-------- 4 files changed, 18 insertions(+), 28 deletions(-) diff --git a/packages/service-library/src/servicelib/aiohttp/rest_responses.py b/packages/service-library/src/servicelib/aiohttp/rest_responses.py index 9e67dcc29ca..52aa87497d8 100644 --- a/packages/service-library/src/servicelib/aiohttp/rest_responses.py +++ b/packages/service-library/src/servicelib/aiohttp/rest_responses.py @@ -151,16 +151,3 @@ def _pred(obj) -> bool: assert len(http_statuses) == len(found), "No duplicates" # nosec return http_statuses - - -_STATUS_CODE_TO_HTTP_ERRORS: dict[int, type[HTTPError]] = _collect_http_exceptions( - HTTPError -) - - -def get_http_error(status_code: int) -> type[HTTPError] | None: - """Returns aiohttp error class corresponding to a 4XX or 5XX status code - - NOTICE that any non-error code (i.e. 2XX, 3XX and 4XX) will return None - """ - return _STATUS_CODE_TO_HTTP_ERRORS.get(status_code) diff --git a/packages/service-library/src/servicelib/aiohttp/web_exceptions_extension.py b/packages/service-library/src/servicelib/aiohttp/web_exceptions_extension.py index 7406fce6ae5..2ad696b534b 100644 --- a/packages/service-library/src/servicelib/aiohttp/web_exceptions_extension.py +++ b/packages/service-library/src/servicelib/aiohttp/web_exceptions_extension.py @@ -1,3 +1,7 @@ +""" Extends `aiohttp.web_exceptions` classes to match `status` codes + and adds helper functions. +""" + import inspect from typing import Any diff --git a/packages/service-library/tests/aiohttp/test_rest_responses.py b/packages/service-library/tests/aiohttp/test_rest_responses.py index 7077a93cb0f..e4aabc65d6e 100644 --- a/packages/service-library/tests/aiohttp/test_rest_responses.py +++ b/packages/service-library/tests/aiohttp/test_rest_responses.py @@ -16,11 +16,10 @@ HTTPOk, ) from servicelib.aiohttp import status -from servicelib.aiohttp.rest_responses import ( +from servicelib.aiohttp.rest_responses import create_http_error, exception_to_response +from servicelib.aiohttp.web_exceptions_extension import ( _STATUS_CODE_TO_HTTP_ERRORS, - create_http_error, - exception_to_response, - get_http_error, + get_http_error_class_or_none, ) from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON @@ -36,17 +35,17 @@ @pytest.mark.parametrize( - "http_exc", (HTTPBadRequest, HTTPGone, HTTPInternalServerError) + "http_exc", [HTTPBadRequest, HTTPGone, HTTPInternalServerError] ) def test_get_http_exception_class_from_code(http_exc: HTTPException): - assert get_http_error(http_exc.status_code) == http_exc + assert get_http_error_class_or_none(http_exc.status_code) == http_exc @pytest.mark.parametrize( "status_code", itertools.chain(BELOW_1XX, NONE_ERRORS, ABOVE_599) ) def test_get_none_for_invalid_or_not_errors_code(status_code): - assert get_http_error(status_code) is None + assert get_http_error_class_or_none(status_code) is None @pytest.mark.parametrize( diff --git a/services/web/server/src/simcore_service_webserver/director_v2/_handlers.py b/services/web/server/src/simcore_service_webserver/director_v2/_handlers.py index 44b6092bc52..63f094eca62 100644 --- a/services/web/server/src/simcore_service_webserver/director_v2/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/director_v2/_handlers.py @@ -10,11 +10,8 @@ from models_library.utils.json_serialization import json_dumps from pydantic import BaseModel, Field, ValidationError, parse_obj_as from pydantic.types import NonNegativeInt -from servicelib.aiohttp.rest_responses import ( - create_http_error, - exception_to_response, - get_http_error, -) +from servicelib.aiohttp.rest_responses import create_http_error, exception_to_response +from servicelib.aiohttp.web_exceptions_extension import get_http_error_class_or_none from servicelib.common_headers import ( UNDEFINED_DEFAULT_SIMCORE_USER_AGENT_VALUE, X_SIMCORE_USER_AGENT, @@ -170,7 +167,8 @@ async def start_computation(request: web.Request) -> web.Response: create_http_error( exc, reason=exc.reason, - http_error_cls=get_http_error(exc.status) or web.HTTPServiceUnavailable, + http_error_cls=get_http_error_class_or_none(exc.status) + or web.HTTPServiceUnavailable, ) ) except UserDefaultWalletNotFoundError as exc: @@ -210,7 +208,8 @@ async def stop_computation(request: web.Request) -> web.Response: return create_http_error( exc, reason=exc.reason, - http_error_cls=get_http_error(exc.status) or web.HTTPServiceUnavailable, + http_error_cls=get_http_error_class_or_none(exc.status) + or web.HTTPServiceUnavailable, ) @@ -259,7 +258,8 @@ async def get_computation(request: web.Request) -> web.Response: return create_http_error( exc, reason=exc.reason, - http_error_cls=get_http_error(exc.status) or web.HTTPServiceUnavailable, + http_error_cls=get_http_error_class_or_none(exc.status) + or web.HTTPServiceUnavailable, ) except ValidationError as exc: return create_http_error(exc, http_error_cls=web.HTTPInternalServerError) From 9bb5ecd3e1c726c4afe1e5f47f5cd892712369af Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Fri, 18 Oct 2024 17:48:47 +0200 Subject: [PATCH 08/16] minor --- packages/service-library/tests/aiohttp/test_rest_responses.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/service-library/tests/aiohttp/test_rest_responses.py b/packages/service-library/tests/aiohttp/test_rest_responses.py index e4aabc65d6e..d1b28d1e9fe 100644 --- a/packages/service-library/tests/aiohttp/test_rest_responses.py +++ b/packages/service-library/tests/aiohttp/test_rest_responses.py @@ -23,8 +23,6 @@ ) from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON -# - # SEE https://httpstatuses.com/ # - below 1xx -> invalid BELOW_1XX = (-5, 0, 5, 99) From 9687f3202eb7c0be5a83faf683ca8d41e78882da Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Mon, 21 Oct 2024 09:32:34 +0200 Subject: [PATCH 09/16] rm unused test --- .../service-library/tests/aiohttp/test__aiohttp_web.py | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 packages/service-library/tests/aiohttp/test__aiohttp_web.py diff --git a/packages/service-library/tests/aiohttp/test__aiohttp_web.py b/packages/service-library/tests/aiohttp/test__aiohttp_web.py deleted file mode 100644 index 16c5cb4c513..00000000000 --- a/packages/service-library/tests/aiohttp/test__aiohttp_web.py +++ /dev/null @@ -1,7 +0,0 @@ -from aiohttp import web -from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON - - -def test_http_errors(): - - err = web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) From 02c99aba3020ac838ce6b24c0dc55fd2939285a9 Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Mon, 21 Oct 2024 09:45:32 +0200 Subject: [PATCH 10/16] uses json_resonpose with status --- .../aiohttp/long_running_tasks/_routes.py | 4 ++-- .../src/simcore_service_storage/handlers_files.py | 7 +++---- .../handlers_simcore_s3.py | 7 ++++--- .../api_keys/_handlers.py | 3 ++- .../director_v2/_handlers.py | 4 ++-- .../folders/_folders_handlers.py | 3 ++- .../simcore_service_webserver/groups/_handlers.py | 7 ++++--- .../login/_registration_handlers.py | 3 ++- .../projects/_comments_handlers.py | 3 ++- .../projects/_crud_handlers.py | 5 +++-- .../projects/_folders_handlers.py | 4 ++-- .../projects/_groups_handlers.py | 4 ++-- .../projects/_nodes_handlers.py | 15 ++++++++------- .../projects/_states_handlers.py | 4 ++-- .../projects/_workspaces_handlers.py | 4 ++-- .../publications/_handlers.py | 3 ++- .../simcore_service_webserver/tags/_handlers.py | 3 ++- .../simcore_service_webserver/users/_handlers.py | 4 ++-- .../users/_notifications_handlers.py | 8 ++++---- .../users/_preferences_handlers.py | 4 ++-- .../users/_tokens_handlers.py | 4 ++-- .../wallets/_groups_handlers.py | 4 ++-- .../wallets/_payments_handlers.py | 8 ++++---- .../workspaces/_groups_handlers.py | 4 ++-- .../workspaces/_workspaces_handlers.py | 3 ++- 25 files changed, 66 insertions(+), 56 deletions(-) diff --git a/packages/service-library/src/servicelib/aiohttp/long_running_tasks/_routes.py b/packages/service-library/src/servicelib/aiohttp/long_running_tasks/_routes.py index 1cade423f3e..d2e2dda98bf 100644 --- a/packages/service-library/src/servicelib/aiohttp/long_running_tasks/_routes.py +++ b/packages/service-library/src/servicelib/aiohttp/long_running_tasks/_routes.py @@ -4,11 +4,11 @@ from aiohttp import web from models_library.utils.json_serialization import json_dumps from pydantic import BaseModel +from servicelib.aiohttp import status from ...long_running_tasks._errors import TaskNotCompletedError, TaskNotFoundError from ...long_running_tasks._models import TaskGet, TaskId, TaskStatus from ...long_running_tasks._task import TrackedTask -from ...mimetype_constants import MIMETYPE_APPLICATION_JSON from ..requests_validation import parse_request_path_parameters_as from ._dependencies import get_task_context, get_tasks_manager @@ -89,7 +89,7 @@ async def cancel_and_delete_task(request: web.Request) -> web.Response: tasks_manager = get_tasks_manager(request.app) task_context = get_task_context(request) await tasks_manager.remove_task(path_params.task_id, with_task_context=task_context) - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) __all__: tuple[str, ...] = ( diff --git a/services/storage/src/simcore_service_storage/handlers_files.py b/services/storage/src/simcore_service_storage/handlers_files.py index 2af7a98ea99..7f691305647 100644 --- a/services/storage/src/simcore_service_storage/handlers_files.py +++ b/services/storage/src/simcore_service_storage/handlers_files.py @@ -25,7 +25,6 @@ parse_request_path_parameters_as, parse_request_query_parameters_as, ) -from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON from ._meta import API_VTAG from .dsm import get_dsm_provider @@ -249,7 +248,7 @@ async def upload_file(request: web.Request) -> web.Response: f"/{API_VTAG}/locations/{{location_id}}/files/{{file_id}}:abort", name="abort_upload_file", ) -async def abort_upload_file(request: web.Request) -> NoReturn: +async def abort_upload_file(request: web.Request) -> web.Response: query_params: StorageQueryParamsBase = parse_request_query_parameters_as( StorageQueryParamsBase, request ) @@ -261,7 +260,7 @@ async def abort_upload_file(request: web.Request) -> NoReturn: dsm = get_dsm_provider(request.app).get(path_params.location_id) await dsm.abort_file_upload(query_params.user_id, path_params.file_id) - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) @routes.post( @@ -386,7 +385,7 @@ async def delete_file(request: web.Request) -> NoReturn: dsm = get_dsm_provider(request.app).get(path_params.location_id) await dsm.delete_file(query_params.user_id, path_params.file_id) - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) @routes.post(f"/{API_VTAG}/files/{{file_id}}:soft-copy", name="copy_as_soft_link") diff --git a/services/storage/src/simcore_service_storage/handlers_simcore_s3.py b/services/storage/src/simcore_service_storage/handlers_simcore_s3.py index 7a04a00bd42..0f8e52fa7fc 100644 --- a/services/storage/src/simcore_service_storage/handlers_simcore_s3.py +++ b/services/storage/src/simcore_service_storage/handlers_simcore_s3.py @@ -1,5 +1,5 @@ import logging -from typing import NoReturn, cast +from typing import cast from aiohttp import web from aiohttp.web import RouteTableDef @@ -7,6 +7,7 @@ from models_library.projects import ProjectID from models_library.utils.fastapi_encoders import jsonable_encoder from models_library.utils.json_serialization import json_dumps +from servicelib.aiohttp import status from servicelib.aiohttp.long_running_tasks.server import ( TaskProgress, start_long_running_task, @@ -106,7 +107,7 @@ async def copy_folders_from_project(request: web.Request) -> web.Response: @routes.delete( f"/{API_VTAG}/simcore-s3/folders/{{folder_id}}", name="delete_folders_of_project" ) -async def delete_folders_of_project(request: web.Request) -> NoReturn: +async def delete_folders_of_project(request: web.Request) -> web.Response: query_params: DeleteFolderQueryParams = parse_request_query_parameters_as( DeleteFolderQueryParams, request ) @@ -126,7 +127,7 @@ async def delete_folders_of_project(request: web.Request) -> NoReturn: query_params.node_id, ) - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) @routes.post(f"/{API_VTAG}/simcore-s3/files/metadata:search", name="search_files") diff --git a/services/web/server/src/simcore_service_webserver/api_keys/_handlers.py b/services/web/server/src/simcore_service_webserver/api_keys/_handlers.py index 517ca037a4d..627d733d9c7 100644 --- a/services/web/server/src/simcore_service_webserver/api_keys/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/api_keys/_handlers.py @@ -5,6 +5,7 @@ from models_library.api_schemas_webserver.auth import ApiKeyCreate from models_library.users import UserID from pydantic import Field +from servicelib.aiohttp import status from servicelib.aiohttp.requests_validation import RequestParams, parse_request_body_as from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON from simcore_postgres_database.errors import DatabaseError @@ -84,4 +85,4 @@ async def delete_api_key(request: web.Request): "Failed to delete API key %s. Ignoring error", name, exc_info=err ) - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) diff --git a/services/web/server/src/simcore_service_webserver/director_v2/_handlers.py b/services/web/server/src/simcore_service_webserver/director_v2/_handlers.py index 63f094eca62..f794fa6f148 100644 --- a/services/web/server/src/simcore_service_webserver/director_v2/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/director_v2/_handlers.py @@ -10,13 +10,13 @@ from models_library.utils.json_serialization import json_dumps from pydantic import BaseModel, Field, ValidationError, parse_obj_as from pydantic.types import NonNegativeInt +from servicelib.aiohttp import status from servicelib.aiohttp.rest_responses import create_http_error, exception_to_response from servicelib.aiohttp.web_exceptions_extension import get_http_error_class_or_none from servicelib.common_headers import ( UNDEFINED_DEFAULT_SIMCORE_USER_AGENT_VALUE, X_SIMCORE_USER_AGENT, ) -from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON from servicelib.request_keys import RQT_USERID_KEY from simcore_postgres_database.utils_groups_extra_properties import ( GroupExtraPropertiesRepo, @@ -202,7 +202,7 @@ async def stop_computation(request: web.Request) -> web.Response: await asyncio.gather( *[computations.stop(pid, req_ctx.user_id) for pid in project_ids] ) - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) except DirectorServiceError as exc: return create_http_error( diff --git a/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py b/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py index cdcdc49fd16..f331c98da4a 100644 --- a/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py +++ b/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py @@ -17,6 +17,7 @@ from models_library.utils.common_validators import null_or_none_str_to_none_validator from models_library.workspaces import WorkspaceID from pydantic import Extra, Field, Json, parse_obj_as, validator +from servicelib.aiohttp import status from servicelib.aiohttp.requests_validation import ( RequestParams, StrictRequestParams, @@ -246,4 +247,4 @@ async def delete_folder_group(request: web.Request): folder_id=path_params.folder_id, product_name=req_ctx.product_name, ) - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) diff --git a/services/web/server/src/simcore_service_webserver/groups/_handlers.py b/services/web/server/src/simcore_service_webserver/groups/_handlers.py index 3f31d1b8972..b6284bed8bf 100644 --- a/services/web/server/src/simcore_service_webserver/groups/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/groups/_handlers.py @@ -13,6 +13,7 @@ from models_library.users import GroupID, UserID from models_library.utils.json_serialization import json_dumps from pydantic import BaseModel, Extra, Field, parse_obj_as +from servicelib.aiohttp import status from servicelib.aiohttp.requests_validation import ( parse_request_path_parameters_as, parse_request_query_parameters_as, @@ -169,7 +170,7 @@ async def delete_group(request: web.Request): path_params = parse_request_path_parameters_as(_GroupPathParams, request) await api.delete_user_group(request.app, req_ctx.user_id, path_params.gid) - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) @routes.get(f"/{API_VTAG}/groups/{{gid}}/users", name="get_group_users") @@ -215,7 +216,7 @@ async def add_group_user(request: web.Request): new_user_id=new_user_id, new_user_email=new_user_email, ) - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) class _GroupUserPathParams(BaseModel): @@ -275,7 +276,7 @@ async def delete_group_user(request: web.Request): await api.delete_user_in_group( request.app, req_ctx.user_id, path_params.gid, path_params.uid ) - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) # diff --git a/services/web/server/src/simcore_service_webserver/login/_registration_handlers.py b/services/web/server/src/simcore_service_webserver/login/_registration_handlers.py index 09337aa0b9f..869fa7a2973 100644 --- a/services/web/server/src/simcore_service_webserver/login/_registration_handlers.py +++ b/services/web/server/src/simcore_service_webserver/login/_registration_handlers.py @@ -8,6 +8,7 @@ ) from models_library.users import UserID from pydantic import BaseModel, Field +from servicelib.aiohttp import status from servicelib.aiohttp.application_keys import APP_FIRE_AND_FORGET_TASKS_KEY from servicelib.aiohttp.requests_validation import parse_request_body_as from servicelib.logging_utils import get_log_record_extra, log_context @@ -85,7 +86,7 @@ async def request_product_account(request: web.Request): task_suffix_name=f"{__name__}.request_product_account.send_account_request_email_to_support", fire_and_forget_tasks_collection=request.app[APP_FIRE_AND_FORGET_TASKS_KEY], ) - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) class _AuthenticatedContext(BaseModel): diff --git a/services/web/server/src/simcore_service_webserver/projects/_comments_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_comments_handlers.py index 90aae8569fb..5325f389e9a 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_comments_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_comments_handlers.py @@ -16,6 +16,7 @@ ) from models_library.rest_pagination_utils import paginate_data from pydantic import BaseModel, Extra, Field, NonNegativeInt +from servicelib.aiohttp import status from servicelib.aiohttp.requests_validation import ( parse_request_body_as, parse_request_path_parameters_as, @@ -223,7 +224,7 @@ async def delete_project_comment(request: web.Request): request=request, comment_id=path_params.comment_id, ) - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) @routes.get( diff --git a/services/web/server/src/simcore_service_webserver/projects/_crud_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_crud_handlers.py index 6897dbd2954..11d1f701b32 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_crud_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_crud_handlers.py @@ -26,6 +26,7 @@ from models_library.utils.fastapi_encoders import jsonable_encoder from models_library.utils.json_serialization import json_dumps from pydantic import parse_obj_as +from servicelib.aiohttp import status from servicelib.aiohttp.long_running_tasks.server import start_long_running_task from servicelib.aiohttp.requests_validation import ( parse_request_body_as, @@ -576,7 +577,7 @@ async def patch_project(request: web.Request): product_name=req_ctx.product_name, ) - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) # @@ -661,7 +662,7 @@ async def delete_project(request: web.Request): except ProjectDeleteError as err: raise web.HTTPConflict(reason=f"{err}") from err - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) # diff --git a/services/web/server/src/simcore_service_webserver/projects/_folders_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_folders_handlers.py index c44cfd5c39e..0e22c5970b9 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_folders_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_folders_handlers.py @@ -6,9 +6,9 @@ from models_library.projects import ProjectID from models_library.utils.common_validators import null_or_none_str_to_none_validator from pydantic import BaseModel, Extra, validator +from servicelib.aiohttp import status from servicelib.aiohttp.requests_validation import parse_request_path_parameters_as from servicelib.aiohttp.typing_extension import Handler -from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON from .._meta import api_version_prefix as VTAG from ..login.decorators import login_required @@ -69,4 +69,4 @@ async def replace_project_folder(request: web.Request): folder_id=path_params.folder_id, product_name=req_ctx.product_name, ) - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) diff --git a/services/web/server/src/simcore_service_webserver/projects/_groups_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_groups_handlers.py index 93075e0754f..85c71d0d62d 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_groups_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_groups_handlers.py @@ -9,12 +9,12 @@ from models_library.projects import ProjectID from models_library.users import GroupID from pydantic import BaseModel, Extra +from servicelib.aiohttp import status from servicelib.aiohttp.requests_validation import ( parse_request_body_as, parse_request_path_parameters_as, ) from servicelib.aiohttp.typing_extension import Handler -from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON from .._meta import api_version_prefix as VTAG from ..login.decorators import login_required @@ -154,4 +154,4 @@ async def delete_project_group(request: web.Request): group_id=path_params.group_id, product_name=req_ctx.product_name, ) - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) diff --git a/services/web/server/src/simcore_service_webserver/projects/_nodes_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_nodes_handlers.py index 4fd7daf254f..fd7a21eaad6 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_nodes_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_nodes_handlers.py @@ -33,6 +33,7 @@ from models_library.utils.fastapi_encoders import jsonable_encoder from models_library.utils.json_serialization import json_dumps from pydantic import BaseModel, Field, parse_obj_as +from servicelib.aiohttp import status from servicelib.aiohttp.long_running_tasks.server import ( TaskProgress, start_long_running_task, @@ -238,7 +239,7 @@ async def patch_project_node(request: web.Request) -> web.Response: node_patch=node_patch, ) - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) @routes.delete(f"/{VTAG}/projects/{{project_id}}/nodes/{{node_id}}", name="delete_node") @@ -263,7 +264,7 @@ async def delete_node(request: web.Request) -> web.Response: req_ctx.product_name, ) - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) @routes.post( @@ -310,7 +311,7 @@ async def update_node_outputs(request: web.Request) -> web.Response: node_errors=None, ui_changed_keys=ui_changed_keys, ) - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) @routes.post( @@ -333,7 +334,7 @@ async def start_node(request: web.Request) -> web.Response: node_id=path_params.node_id, ) - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) async def _stop_dynamic_service_task( @@ -347,7 +348,7 @@ async def _stop_dynamic_service_task( await dynamic_scheduler_api.stop_dynamic_service( app, dynamic_service_stop=dynamic_service_stop ) - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) except (RPCServerError, ServiceWaitingForManualInterventionError) as exc: # in case there is an error reply as not found @@ -355,7 +356,7 @@ async def _stop_dynamic_service_task( except ServiceWasNotFoundError: # in case the service is not found reply as all OK - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) @routes.post( @@ -413,7 +414,7 @@ async def restart_node(request: web.Request) -> web.Response: await director_v2_api.restart_dynamic_service(request.app, f"{path_params.node_id}") - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) # diff --git a/services/web/server/src/simcore_service_webserver/projects/_states_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_states_handlers.py index 7e9fb932639..ca0725b37b9 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_states_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_states_handlers.py @@ -10,6 +10,7 @@ from aiohttp import web from models_library.projects_state import ProjectState from pydantic import BaseModel +from servicelib.aiohttp import status from servicelib.aiohttp.requests_validation import ( parse_request_path_parameters_as, parse_request_query_parameters_as, @@ -20,7 +21,6 @@ UNDEFINED_DEFAULT_SIMCORE_USER_AGENT_VALUE, X_SIMCORE_USER_AGENT, ) -from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON from simcore_postgres_database.models.users import UserRole from simcore_postgres_database.webserver_models import ProjectType @@ -222,7 +222,7 @@ async def close_project(request: web.Request) -> web.Response: ), ) await project_logs.unsubscribe(request.app, path_params.project_id) - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) # diff --git a/services/web/server/src/simcore_service_webserver/projects/_workspaces_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_workspaces_handlers.py index d00fd684666..6b553a6d3ba 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_workspaces_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_workspaces_handlers.py @@ -6,9 +6,9 @@ from models_library.utils.common_validators import null_or_none_str_to_none_validator from models_library.workspaces import WorkspaceID from pydantic import BaseModel, Extra, validator +from servicelib.aiohttp import status from servicelib.aiohttp.requests_validation import parse_request_path_parameters_as from servicelib.aiohttp.typing_extension import Handler -from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON from .._meta import api_version_prefix as VTAG from ..folders.errors import FolderAccessForbiddenError, FolderNotFoundError @@ -81,4 +81,4 @@ async def replace_project_workspace(request: web.Request): workspace_id=path_params.workspace_id, product_name=req_ctx.product_name, ) - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) diff --git a/services/web/server/src/simcore_service_webserver/publications/_handlers.py b/services/web/server/src/simcore_service_webserver/publications/_handlers.py index 8cc4f545702..2d7feef016f 100644 --- a/services/web/server/src/simcore_service_webserver/publications/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/publications/_handlers.py @@ -3,6 +3,7 @@ from aiohttp import MultipartReader, hdrs, web from json2html import json2html # type: ignore[import-untyped] from models_library.utils.json_serialization import json_dumps +from servicelib.aiohttp import status from servicelib.mimetype_constants import ( MIMETYPE_APPLICATION_JSON, MIMETYPE_APPLICATION_ZIP, @@ -93,4 +94,4 @@ async def service_submission(request: web.Request): _logger.exception("Error while sending the 'new service submission' mail.") raise web.HTTPServiceUnavailable from exc - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) diff --git a/services/web/server/src/simcore_service_webserver/tags/_handlers.py b/services/web/server/src/simcore_service_webserver/tags/_handlers.py index 03c5eb2eda3..8862f0320c1 100644 --- a/services/web/server/src/simcore_service_webserver/tags/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/tags/_handlers.py @@ -2,6 +2,7 @@ from aiohttp import web from pydantic import parse_obj_as +from servicelib.aiohttp import status from servicelib.aiohttp.requests_validation import ( parse_request_body_as, parse_request_path_parameters_as, @@ -107,7 +108,7 @@ async def delete_tag(request: web.Request): request.app, user_id=req_ctx.user_id, tag_id=path_params.tag_id ) - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) # diff --git a/services/web/server/src/simcore_service_webserver/users/_handlers.py b/services/web/server/src/simcore_service_webserver/users/_handlers.py index 87d96a8e5f1..3462602f74b 100644 --- a/services/web/server/src/simcore_service_webserver/users/_handlers.py +++ b/services/web/server/src/simcore_service_webserver/users/_handlers.py @@ -4,13 +4,13 @@ from aiohttp import web from models_library.users import UserID from pydantic import BaseModel, Field +from servicelib.aiohttp import status from servicelib.aiohttp.requests_validation import ( parse_request_body_as, parse_request_query_parameters_as, ) from servicelib.aiohttp.typing_extension import Handler from servicelib.logging_errors import create_troubleshotting_log_kwargs -from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON from servicelib.request_keys import RQT_USERID_KEY from servicelib.rest_constants import RESPONSE_MODEL_POLICY @@ -85,7 +85,7 @@ async def update_my_profile(request: web.Request) -> web.Response: await api.update_user_profile( request.app, req_ctx.user_id, profile_update, as_patch=False ) - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) class _SearchQueryParams(BaseModel): diff --git a/services/web/server/src/simcore_service_webserver/users/_notifications_handlers.py b/services/web/server/src/simcore_service_webserver/users/_notifications_handlers.py index 4b984f5f79e..3a9588d39a5 100644 --- a/services/web/server/src/simcore_service_webserver/users/_notifications_handlers.py +++ b/services/web/server/src/simcore_service_webserver/users/_notifications_handlers.py @@ -4,11 +4,11 @@ import redis.asyncio as aioredis from aiohttp import web from pydantic import BaseModel +from servicelib.aiohttp import status from servicelib.aiohttp.requests_validation import ( parse_request_body_as, parse_request_path_parameters_as, ) -from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON from servicelib.redis_utils import handle_redis_returns_union_types from .._meta import API_VTAG @@ -84,7 +84,7 @@ async def create_user_notification(request: web.Request) -> web.Response: pipe.ltrim(key, 0, MAX_NOTIFICATIONS_FOR_USER_TO_KEEP - 1) await pipe.execute() - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) class _NotificationPathParams(BaseModel): @@ -115,9 +115,9 @@ async def mark_notification_as_read(request: web.Request) -> web.Response: await handle_redis_returns_union_types( redis_client.lset(key, k, user_notification.json()) ) - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) @routes.get(f"/{API_VTAG}/me/permissions", name="list_user_permissions") diff --git a/services/web/server/src/simcore_service_webserver/users/_preferences_handlers.py b/services/web/server/src/simcore_service_webserver/users/_preferences_handlers.py index c32b0536570..0c537278f9c 100644 --- a/services/web/server/src/simcore_service_webserver/users/_preferences_handlers.py +++ b/services/web/server/src/simcore_service_webserver/users/_preferences_handlers.py @@ -8,12 +8,12 @@ from models_library.products import ProductName from models_library.users import UserID from pydantic import BaseModel, Field +from servicelib.aiohttp import status from servicelib.aiohttp.requests_validation import ( parse_request_body_as, parse_request_path_parameters_as, ) from servicelib.aiohttp.typing_extension import Handler -from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON from servicelib.request_keys import RQT_USERID_KEY from simcore_postgres_database.utils_user_preferences import ( CouldNotCreateOrUpdateUserPreferenceError, @@ -66,4 +66,4 @@ async def set_frontend_preference(request: web.Request) -> web.Response: frontend_preference_identifier=req_path_params.preference_id, value=req_body.value, ) - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) diff --git a/services/web/server/src/simcore_service_webserver/users/_tokens_handlers.py b/services/web/server/src/simcore_service_webserver/users/_tokens_handlers.py index 7adb8fc8ad3..40b884c4eb9 100644 --- a/services/web/server/src/simcore_service_webserver/users/_tokens_handlers.py +++ b/services/web/server/src/simcore_service_webserver/users/_tokens_handlers.py @@ -3,12 +3,12 @@ from aiohttp import web from pydantic import BaseModel +from servicelib.aiohttp import status from servicelib.aiohttp.requests_validation import ( parse_request_body_as, parse_request_path_parameters_as, ) from servicelib.aiohttp.typing_extension import Handler -from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON from .._meta import API_VTAG from ..login.decorators import login_required @@ -85,4 +85,4 @@ async def delete_token(request: web.Request) -> web.Response: req_ctx = UsersRequestContext.parse_obj(request) req_path_params = parse_request_path_parameters_as(_TokenPathParams, request) await _tokens.delete_token(request.app, req_ctx.user_id, req_path_params.service) - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) diff --git a/services/web/server/src/simcore_service_webserver/wallets/_groups_handlers.py b/services/web/server/src/simcore_service_webserver/wallets/_groups_handlers.py index bbc67b24a21..1115a239d62 100644 --- a/services/web/server/src/simcore_service_webserver/wallets/_groups_handlers.py +++ b/services/web/server/src/simcore_service_webserver/wallets/_groups_handlers.py @@ -9,12 +9,12 @@ from models_library.users import GroupID, UserID from models_library.wallets import WalletID from pydantic import BaseModel, Extra, Field +from servicelib.aiohttp import status from servicelib.aiohttp.requests_validation import ( parse_request_body_as, parse_request_path_parameters_as, ) from servicelib.aiohttp.typing_extension import Handler -from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON from servicelib.request_keys import RQT_USERID_KEY from .._constants import RQ_PRODUCT_KEY @@ -161,4 +161,4 @@ async def delete_wallet_group(request: web.Request): group_id=path_params.group_id, product_name=req_ctx.product_name, ) - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) diff --git a/services/web/server/src/simcore_service_webserver/wallets/_payments_handlers.py b/services/web/server/src/simcore_service_webserver/wallets/_payments_handlers.py index 27060372abd..9a03bc2d2a5 100644 --- a/services/web/server/src/simcore_service_webserver/wallets/_payments_handlers.py +++ b/services/web/server/src/simcore_service_webserver/wallets/_payments_handlers.py @@ -15,6 +15,7 @@ from models_library.products import CreditResultGet from models_library.rest_pagination import Page, PageQueryParameters from models_library.rest_pagination_utils import paginate_data +from servicelib.aiohttp import status from servicelib.aiohttp.application_keys import APP_FIRE_AND_FORGET_TASKS_KEY from servicelib.aiohttp.requests_validation import ( parse_request_body_as, @@ -22,7 +23,6 @@ parse_request_query_parameters_as, ) from servicelib.logging_utils import get_log_record_extra, log_context -from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON from servicelib.utils import fire_and_forget_task from .._meta import API_VTAG as VTAG @@ -185,7 +185,7 @@ async def _cancel_payment(request: web.Request): product_name=req_ctx.product_name, ) - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) # @@ -261,7 +261,7 @@ async def _cancel_creation_of_payment_method(request: web.Request): product_name=req_ctx.product_name, ) - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) @routes.get( @@ -323,7 +323,7 @@ async def _delete_payment_method(request: web.Request): payment_method_id=path_params.payment_method_id, product_name=req_ctx.product_name, ) - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) _TINY_WAIT_TO_TRIGGER_CONTEXT_SWITCH = 0.1 diff --git a/services/web/server/src/simcore_service_webserver/workspaces/_groups_handlers.py b/services/web/server/src/simcore_service_webserver/workspaces/_groups_handlers.py index 79c50ce3b91..d4ae7c4b74f 100644 --- a/services/web/server/src/simcore_service_webserver/workspaces/_groups_handlers.py +++ b/services/web/server/src/simcore_service_webserver/workspaces/_groups_handlers.py @@ -9,12 +9,12 @@ from models_library.users import GroupID, UserID from models_library.workspaces import WorkspaceID from pydantic import BaseModel, Extra, Field +from servicelib.aiohttp import status from servicelib.aiohttp.requests_validation import ( parse_request_body_as, parse_request_path_parameters_as, ) from servicelib.aiohttp.typing_extension import Handler -from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON from servicelib.request_keys import RQT_USERID_KEY from .._constants import RQ_PRODUCT_KEY @@ -162,4 +162,4 @@ async def delete_workspace_group(request: web.Request): group_id=path_params.group_id, product_name=req_ctx.product_name, ) - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) diff --git a/services/web/server/src/simcore_service_webserver/workspaces/_workspaces_handlers.py b/services/web/server/src/simcore_service_webserver/workspaces/_workspaces_handlers.py index 2d1657e9e53..fa9a2e4aa67 100644 --- a/services/web/server/src/simcore_service_webserver/workspaces/_workspaces_handlers.py +++ b/services/web/server/src/simcore_service_webserver/workspaces/_workspaces_handlers.py @@ -15,6 +15,7 @@ from models_library.users import UserID from models_library.workspaces import WorkspaceID from pydantic import Extra, Field, Json, parse_obj_as, validator +from servicelib.aiohttp import status from servicelib.aiohttp.requests_validation import ( RequestParams, StrictRequestParams, @@ -209,4 +210,4 @@ async def delete_workspace(request: web.Request): workspace_id=path_params.workspace_id, product_name=req_ctx.product_name, ) - return web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return web.json_response(status=status.HTTP_204_NO_CONTENT) From bc8418f291352e07ad777042a885bb5eb05e554f Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Mon, 21 Oct 2024 09:52:04 +0200 Subject: [PATCH 11/16] return json_response --- .../web/server/tests/unit/isolated/test_security_api.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/services/web/server/tests/unit/isolated/test_security_api.py b/services/web/server/tests/unit/isolated/test_security_api.py index f7e73620435..e60cab4985b 100644 --- a/services/web/server/tests/unit/isolated/test_security_api.py +++ b/services/web/server/tests/unit/isolated/test_security_api.py @@ -156,12 +156,12 @@ async def _init(request: web.Request): # get url and deliver product product_name = expected_product_name await _remember_product_name(request, product_name) - return web.HTTPOk() + return web.json_response(status=status.HTTP_200_OK) @routes.post("/v0/hack/{product_name}") async def _set_other_product(request: web.Request): await _remember_product_name(request, request.match_info["product_name"]) - return web.HTTPOk() + return web.json_response(status=status.HTTP_200_OK) @routes.post("/v0/login") async def _login(request: web.Request): @@ -183,7 +183,7 @@ async def _login(request: web.Request): @routes.post("/v0/public") async def _public(request: web.Request): assert await _get_product_name(request) == expected_product_name - return web.HTTPOk() + return web.json_response(status=status.HTTP_200_OK) @routes.post("/v0/admin") @login_required # NOTE: same as `await check_user_authorized(request)`` @@ -192,7 +192,7 @@ async def _public(request: web.Request): ) async def _admin_only(request: web.Request): assert await _get_product_name(request) == expected_product_name - return web.HTTPOk() + return web.json_response(status=status.HTTP_200_OK) @routes.post("/v0/logout") async def _logout(request: web.Request): From 937abce751a4d020303080934d52207189a89764 Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Mon, 21 Oct 2024 10:46:17 +0200 Subject: [PATCH 12/16] fixe mypy --- .../storage/src/simcore_service_storage/handlers_files.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/storage/src/simcore_service_storage/handlers_files.py b/services/storage/src/simcore_service_storage/handlers_files.py index 7f691305647..87cb60d5829 100644 --- a/services/storage/src/simcore_service_storage/handlers_files.py +++ b/services/storage/src/simcore_service_storage/handlers_files.py @@ -1,7 +1,7 @@ import asyncio import logging import urllib.parse -from typing import NoReturn, cast +from typing import cast from aiohttp import web from aiohttp.web import RouteTableDef @@ -373,7 +373,7 @@ async def is_completed_upload_file(request: web.Request) -> web.Response: @routes.delete( f"/{API_VTAG}/locations/{{location_id}}/files/{{file_id}}", name="delete_file" ) -async def delete_file(request: web.Request) -> NoReturn: +async def delete_file(request: web.Request) -> web.Response: query_params: StorageQueryParamsBase = parse_request_query_parameters_as( StorageQueryParamsBase, request ) From c84cad942ab9a11fc358bb0064a93042f9b3b933 Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Mon, 21 Oct 2024 10:53:30 +0200 Subject: [PATCH 13/16] minor --- .../servicelib/aiohttp/web_exceptions_extension.py | 14 ++++++++------ .../tests/aiohttp/test_status_utils.py | 5 ++++- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/service-library/src/servicelib/aiohttp/web_exceptions_extension.py b/packages/service-library/src/servicelib/aiohttp/web_exceptions_extension.py index 2ad696b534b..1226250c6ee 100644 --- a/packages/service-library/src/servicelib/aiohttp/web_exceptions_extension.py +++ b/packages/service-library/src/servicelib/aiohttp/web_exceptions_extension.py @@ -3,7 +3,7 @@ """ import inspect -from typing import Any +from typing import Any, TypeVar from aiohttp import web_exceptions from aiohttp.web_exceptions import ( @@ -40,15 +40,18 @@ class HTTPLoopDetectedError(HTTPServerError): status_code = status.HTTP_508_LOOP_DETECTED +E = TypeVar("E", bound="HTTPException") + + def get_all_aiohttp_http_exceptions( - exception_cls: type[HTTPException] = HTTPException, -) -> dict[int, type[HTTPException]]: + base_http_exception_cls: type[E], +) -> dict[int, type[E]]: # Inverse map from code to HTTPException classes def _pred(obj) -> bool: return ( inspect.isclass(obj) - and issubclass(obj, exception_cls) + and issubclass(obj, base_http_exception_cls) and getattr(obj, "status_code", 0) > 0 ) @@ -62,8 +65,7 @@ def _pred(obj) -> bool: HTTPLockedError, HTTPLoopDetectedError, ): - - status_to_http_exception_map[cls.status_code] = cls # type:ignore + status_to_http_exception_map[cls.status_code] = cls return status_to_http_exception_map diff --git a/packages/service-library/tests/aiohttp/test_status_utils.py b/packages/service-library/tests/aiohttp/test_status_utils.py index 7e6415860a5..5051a051c53 100644 --- a/packages/service-library/tests/aiohttp/test_status_utils.py +++ b/packages/service-library/tests/aiohttp/test_status_utils.py @@ -1,6 +1,7 @@ from http import HTTPStatus import pytest +from fastapi import HTTPException from servicelib.aiohttp import status from servicelib.aiohttp.web_exceptions_extension import ( STATUS_CODES_WITHOUT_AIOHTTP_EXCEPTION_CLASS, @@ -68,7 +69,9 @@ def test_predicates_with_status(): ] -AIOHTTP_EXCEPTION_CLASSES_MAP = get_all_aiohttp_http_exceptions() +AIOHTTP_EXCEPTION_CLASSES_MAP: dict[ + int, type[HTTPException] +] = get_all_aiohttp_http_exceptions(HTTPException) @pytest.mark.parametrize("status_code", get_http_status_codes(status)) From ab82df891c2598631e71d0e91ef82d864042de87 Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Mon, 21 Oct 2024 11:09:18 +0200 Subject: [PATCH 14/16] mypy --- .../src/servicelib/aiohttp/web_exceptions_extension.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/service-library/src/servicelib/aiohttp/web_exceptions_extension.py b/packages/service-library/src/servicelib/aiohttp/web_exceptions_extension.py index 1226250c6ee..eaf0cfc42a3 100644 --- a/packages/service-library/src/servicelib/aiohttp/web_exceptions_extension.py +++ b/packages/service-library/src/servicelib/aiohttp/web_exceptions_extension.py @@ -15,6 +15,8 @@ from . import status +assert issubclass(HTTPError, HTTPException) # nsoec + # NOTE: these are the status codes that DO NOT have an aiohttp.HTTPException associated STATUS_CODES_WITHOUT_AIOHTTP_EXCEPTION_CLASS = ( status.HTTP_100_CONTINUE, From 39243afd1859f6333218bf2a5ccc59572fd6b97d Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Mon, 21 Oct 2024 11:15:02 +0200 Subject: [PATCH 15/16] pylint and bad import --- packages/service-library/tests/aiohttp/test_status_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/service-library/tests/aiohttp/test_status_utils.py b/packages/service-library/tests/aiohttp/test_status_utils.py index 5051a051c53..c5a8785a3b1 100644 --- a/packages/service-library/tests/aiohttp/test_status_utils.py +++ b/packages/service-library/tests/aiohttp/test_status_utils.py @@ -1,10 +1,10 @@ from http import HTTPStatus import pytest -from fastapi import HTTPException from servicelib.aiohttp import status from servicelib.aiohttp.web_exceptions_extension import ( STATUS_CODES_WITHOUT_AIOHTTP_EXCEPTION_CLASS, + HTTPException, get_all_aiohttp_http_exceptions, ) from servicelib.status_codes_utils import ( From 2bdcd5be17cef67b60144a54ed91205ba73246e2 Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Mon, 21 Oct 2024 16:12:11 +0200 Subject: [PATCH 16/16] rm test --- .../tests/aiohttp/test__models_examples.py | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 packages/service-library/tests/aiohttp/test__models_examples.py diff --git a/packages/service-library/tests/aiohttp/test__models_examples.py b/packages/service-library/tests/aiohttp/test__models_examples.py deleted file mode 100644 index c72bdfffe7f..00000000000 --- a/packages/service-library/tests/aiohttp/test__models_examples.py +++ /dev/null @@ -1,19 +0,0 @@ -import json -from typing import Any - -import pytest -import servicelib.aiohttp -from pydantic import BaseModel -from pytest_simcore.pydantic_models import walk_model_examples_in_package - - -@pytest.mark.parametrize( - "model_cls, example_name, example_data", - walk_model_examples_in_package(servicelib.aiohttp), -) -def test_all_models_config_examples_in_servicelib_aiohttp_package( - model_cls: type[BaseModel], example_name: int, example_data: Any -): - assert model_cls.parse_obj( - example_data - ), f"Failed {example_name} : {json.dumps(example_data)}"