Skip to content

Commit 5c9959d

Browse files
authored
Merge branch 'master' into is7395/test-settings
2 parents ff81b2e + f6efa8e commit 5c9959d

File tree

17 files changed

+196
-106
lines changed

17 files changed

+196
-106
lines changed

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

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,6 @@
88
from uuid import UUID
99

1010
from common_library.basic_types import DEFAULT_FACTORY
11-
from models_library.basic_types import ConstrainedStr
12-
from models_library.folders import FolderID
13-
from models_library.workspaces import WorkspaceID
1411
from pydantic import (
1512
BaseModel,
1613
ConfigDict,
@@ -21,8 +18,11 @@
2118
)
2219

2320
from .basic_regex import DATE_RE, UUID_RE_BASE
21+
from .basic_types import ConstrainedStr
2422
from .emails import LowerCaseEmailStr
23+
from .folders import FolderID
2524
from .groups import GroupID
25+
from .products import ProductName
2626
from .projects_access import AccessRights, GroupIDStr
2727
from .projects_nodes import Node
2828
from .projects_nodes_io import NodeIDStr
@@ -33,6 +33,7 @@
3333
none_to_empty_str_pre_validator,
3434
)
3535
from .utils.enums import StrAutoEnum
36+
from .workspaces import WorkspaceID
3637

3738
ProjectID: TypeAlias = UUID
3839
CommitID: TypeAlias = int
@@ -147,6 +148,25 @@ def _convert_sql_alchemy_enum(cls, v):
147148
)
148149

149150

151+
class ProjectListAtDB(BaseProjectModel):
152+
id: int
153+
type: ProjectType
154+
template_type: ProjectTemplateType | None
155+
prj_owner: int | None
156+
ui: dict[str, Any] | None
157+
classifiers: list[ClassifierID] | None
158+
dev: dict[str, Any] | None
159+
quality: dict[str, Any]
160+
published: bool | None
161+
hidden: bool
162+
workspace_id: WorkspaceID | None
163+
trashed: datetime | None
164+
trashed_by: UserID | None
165+
trashed_explicitly: bool
166+
product_name: ProductName
167+
folder_id: FolderID | None
168+
169+
150170
class Project(BaseProjectModel):
151171
# NOTE: This is the pydantic pendant of project-v0.0.1.json used in the API of the webserver/webclient
152172
# NOT for usage with DB!!

packages/models-library/src/models_library/projects_nodes.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ class Node(BaseModel):
168168
),
169169
] = None
170170

171-
thumbnail: Annotated[
171+
thumbnail: Annotated[ # <-- (DEPRECATED) Can be removed
172172
str | HttpUrl | None,
173173
Field(
174174
description="url of the latest screenshot of the node",
@@ -232,18 +232,18 @@ class Node(BaseModel):
232232
] = DEFAULT_FACTORY
233233

234234
output_node: Annotated[bool | None, Field(deprecated=True, alias="outputNode")] = (
235-
None
235+
None # <-- (DEPRECATED) Can be removed
236236
)
237237

238-
output_nodes: Annotated[
238+
output_nodes: Annotated[ # <-- (DEPRECATED) Can be removed
239239
list[NodeID] | None,
240240
Field(
241241
description="Used in group-nodes. Node IDs of those connected to the output",
242242
alias="outputNodes",
243243
),
244244
] = None
245245

246-
parent: Annotated[
246+
parent: Annotated[ # <-- (DEPRECATED) Can be removed
247247
NodeID | None,
248248
Field(
249249
description="Parent's (group-nodes') node ID s. Used to group",

packages/service-library/src/servicelib/fastapi/long_running_tasks/_client.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ def __init__(self, app: FastAPI, async_client: AsyncClient, base_url: str):
120120
"""
121121
self.app = app
122122
self._async_client = async_client
123-
self._base_url = base_url
123+
self.base_url = base_url
124124

125125
@property
126126
def _client_configuration(self) -> ClientConfiguration:
@@ -129,7 +129,7 @@ def _client_configuration(self) -> ClientConfiguration:
129129

130130
def _get_url(self, path: str) -> str:
131131
url_path = f"{self._client_configuration.router_prefix}{path}".lstrip("/")
132-
url = TypeAdapter(AnyHttpUrl).validate_python(f"{self._base_url}{url_path}")
132+
url = TypeAdapter(AnyHttpUrl).validate_python(f"{self.base_url}{url_path}")
133133
return f"{url}"
134134

135135
@retry_on_http_errors

packages/service-library/src/servicelib/fastapi/long_running_tasks/_context_manager.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import asyncio
2-
from asyncio.log import logger
2+
import logging
33
from collections.abc import AsyncIterator
44
from contextlib import asynccontextmanager
55
from typing import Any, Final
66

77
from pydantic import PositiveFloat
8+
from servicelib.logging_errors import create_troubleshotting_log_message
89

9-
from ...long_running_tasks.errors import TaskClientTimeoutError
10+
from ...long_running_tasks.errors import TaskClientTimeoutError, TaskExceptionError
1011
from ...long_running_tasks.models import (
1112
ProgressCallback,
1213
ProgressMessage,
@@ -16,6 +17,8 @@
1617
)
1718
from ._client import Client
1819

20+
_logger = logging.getLogger(__name__)
21+
1922
# NOTE: very short running requests are involved
2023
MAX_CONCURRENCY: Final[int] = 10
2124

@@ -96,7 +99,7 @@ async def periodic_task_result(
9699

97100
async def _status_update() -> TaskStatus:
98101
task_status: TaskStatus = await client.get_task_status(task_id)
99-
logger.debug("Task status %s", task_status.model_dump_json())
102+
_logger.debug("Task status %s", task_status.model_dump_json())
100103
await progress_manager.update(
101104
task_id=task_id,
102105
message=task_status.task_progress.message,
@@ -114,7 +117,7 @@ async def _wait_for_task_result() -> Any:
114117

115118
try:
116119
result = await asyncio.wait_for(_wait_for_task_result(), timeout=task_timeout)
117-
logger.debug("%s, %s", f"{task_id=}", f"{result=}")
120+
_logger.debug("%s, %s", f"{task_id=}", f"{result=}")
118121

119122
yield result
120123
except TimeoutError as e:
@@ -124,3 +127,13 @@ async def _wait_for_task_result() -> Any:
124127
timeout=task_timeout,
125128
exception=e,
126129
) from e
130+
except Exception as e:
131+
error = TaskExceptionError(task_id=task_id, exception=e, traceback="")
132+
_logger.warning(
133+
create_troubleshotting_log_message(
134+
user_error_msg=f"{task_id=} raised an exception",
135+
error=e,
136+
tip=f"Check the logs of the service responding to '{client.base_url}'",
137+
)
138+
)
139+
raise error from e

packages/service-library/src/servicelib/long_running_tasks/http_endpoint_responses.py

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
1+
import logging
12
from typing import Any
23

4+
from common_library.error_codes import create_error_code
5+
from servicelib.logging_errors import create_troubleshotting_log_kwargs
6+
7+
from .errors import TaskNotCompletedError, TaskNotFoundError
38
from .models import TaskBase, TaskId, TaskStatus
49
from .task import TaskContext, TasksManager, TrackedTask
510

11+
_logger = logging.getLogger(__name__)
12+
613

714
def list_tasks(
815
tasks_manager: TasksManager, task_context: TaskContext | None
@@ -25,14 +32,29 @@ async def get_task_result(
2532
tasks_manager: TasksManager, task_context: TaskContext | None, task_id: TaskId
2633
) -> Any:
2734
try:
28-
return tasks_manager.get_task_result(
29-
task_id=task_id, with_task_context=task_context
35+
task_result = tasks_manager.get_task_result(
36+
task_id, with_task_context=task_context
37+
)
38+
await tasks_manager.remove_task(
39+
task_id, with_task_context=task_context, reraise_errors=False
40+
)
41+
return task_result
42+
except (TaskNotFoundError, TaskNotCompletedError):
43+
raise
44+
except Exception as exc:
45+
_logger.exception(
46+
**create_troubleshotting_log_kwargs(
47+
user_error_msg=f"{task_id=} raised an exception",
48+
error=exc,
49+
error_code=create_error_code(exc),
50+
error_context={"task_context": task_context, "task_id": task_id},
51+
),
3052
)
31-
finally:
32-
# the task is always removed even if an error occurs
53+
# the task shall be removed in this case
3354
await tasks_manager.remove_task(
3455
task_id, with_task_context=task_context, reraise_errors=False
3556
)
57+
raise
3658

3759

3860
async def remove_task(

packages/service-library/tests/fastapi/long_running_tasks/test_long_running_tasks_context_manager.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from servicelib.fastapi.long_running_tasks.server import setup as setup_server
1919
from servicelib.long_running_tasks.errors import (
2020
TaskClientTimeoutError,
21+
TaskExceptionError,
2122
)
2223
from servicelib.long_running_tasks.models import (
2324
ProgressMessage,
@@ -148,7 +149,7 @@ async def test_task_result_task_result_is_an_error(
148149

149150
url = TypeAdapter(AnyHttpUrl).validate_python("http://backgroud.testserver.io/")
150151
client = Client(app=bg_task_app, async_client=async_client, base_url=url)
151-
with pytest.raises(RuntimeError, match="I am failing as requested"):
152+
with pytest.raises(TaskExceptionError, match="I am failing as requested"):
152153
async with periodic_task_result(
153154
client,
154155
task_id,

services/director-v2/src/simcore_service_director_v2/modules/dynamic_sidecar/scheduler/_core/_events_utils.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from models_library.users import UserID
2222
from servicelib.fastapi.http_client_thin import BaseHttpClientError
2323
from servicelib.logging_utils import log_context
24+
from servicelib.long_running_tasks.errors import TaskExceptionError
2425
from servicelib.long_running_tasks.models import ProgressCallback, TaskProgress
2526
from servicelib.rabbitmq import RabbitMQClient
2627
from servicelib.rabbitmq._client_rpc import RabbitMQRPCClient
@@ -134,7 +135,7 @@ async def service_remove_containers(
134135
await sidecars_client.stop_service(
135136
scheduler_data.endpoint, progress_callback=progress_callback
136137
)
137-
except BaseHttpClientError as e:
138+
except (BaseHttpClientError, TaskExceptionError) as e:
138139
_logger.info(
139140
(
140141
"Could not remove service containers for %s. "
@@ -151,7 +152,7 @@ async def service_free_reserved_disk_space(
151152
scheduler_data: SchedulerData = _get_scheduler_data(app, node_id)
152153
try:
153154
await sidecars_client.free_reserved_disk_space(scheduler_data.endpoint)
154-
except BaseHttpClientError as e:
155+
except (BaseHttpClientError, TaskExceptionError) as e:
155156
_logger.info(
156157
(
157158
"Could not remove service containers for %s. "
@@ -369,7 +370,7 @@ async def attempt_pod_removal_and_data_saving(
369370
scheduler_data.dynamic_sidecar.were_state_and_outputs_saved = True
370371

371372
_logger.info("dynamic-sidecar saved: state and output ports")
372-
except BaseHttpClientError as e:
373+
except (BaseHttpClientError, TaskExceptionError) as e:
373374
_logger.error( # noqa: TRY400
374375
(
375376
"Could not contact dynamic-sidecar to save service "

services/dynamic-sidecar/tests/unit/test_api_rest_containers_long_running_tasks.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
from pytest_simcore.helpers.monkeypatch_envs import EnvVarsDict
3131
from servicelib.fastapi.long_running_tasks.client import Client, periodic_task_result
3232
from servicelib.fastapi.long_running_tasks.client import setup as client_setup
33+
from servicelib.long_running_tasks.errors import TaskExceptionError
3334
from servicelib.long_running_tasks.models import TaskId
3435
from simcore_sdk.node_ports_common.exceptions import NodeNotFound
3536
from simcore_service_dynamic_sidecar._meta import API_VTAG
@@ -42,10 +43,7 @@
4243
from simcore_service_dynamic_sidecar.models.shared_store import SharedStore
4344
from simcore_service_dynamic_sidecar.modules.inputs import enable_inputs_pulling
4445
from simcore_service_dynamic_sidecar.modules.outputs._context import OutputsContext
45-
from simcore_service_dynamic_sidecar.modules.outputs._manager import (
46-
OutputsManager,
47-
UploadPortsFailedError,
48-
)
46+
from simcore_service_dynamic_sidecar.modules.outputs._manager import OutputsManager
4947

5048
FAST_STATUS_POLL: Final[float] = 0.1
5149
CREATE_SERVICE_CONTAINERS_TIMEOUT: Final[float] = 60
@@ -681,7 +679,7 @@ async def _test_code() -> None:
681679
if not mock_port_keys:
682680
await _test_code()
683681
else:
684-
with pytest.raises(UploadPortsFailedError) as exec_info:
682+
with pytest.raises(TaskExceptionError) as exec_info:
685683
await _test_code()
686684
assert f"the node id {missing_node_uuid} was not found" in f"{exec_info.value}"
687685

services/dynamic-sidecar/tests/unit/test_api_rest_workflow_service_metrics.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
from pytest_simcore.helpers.monkeypatch_envs import EnvVarsDict, setenvs_from_dict
3535
from servicelib.fastapi.long_running_tasks.client import Client, periodic_task_result
3636
from servicelib.fastapi.long_running_tasks.client import setup as client_setup
37+
from servicelib.long_running_tasks.errors import TaskExceptionError
3738
from servicelib.long_running_tasks.models import TaskId
3839
from simcore_service_dynamic_sidecar._meta import API_VTAG
3940
from simcore_service_dynamic_sidecar.core.docker_utils import get_container_states
@@ -258,7 +259,7 @@ async def test_user_services_fail_to_start(
258259
with_compose_down: bool,
259260
mock_user_services_fail_to_start: None,
260261
):
261-
with pytest.raises(RuntimeError):
262+
with pytest.raises(TaskExceptionError):
262263
async with periodic_task_result(
263264
client=client,
264265
task_id=await _get_task_id_create_service_containers(
@@ -314,7 +315,7 @@ async def test_user_services_fail_to_stop_or_save_data(
314315
# in case of manual intervention multiple stops will be sent
315316
_EXPECTED_STOP_MESSAGES = 4
316317
for _ in range(_EXPECTED_STOP_MESSAGES):
317-
with pytest.raises(RuntimeError):
318+
with pytest.raises(TaskExceptionError):
318319
async with periodic_task_result(
319320
client=client,
320321
task_id=await _get_task_id_docker_compose_down(httpx_async_client),

services/static-webserver/client/source/class/osparc/auth/ui/ResetPassRequestView.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ qx.Class.define("osparc.auth.ui.ResetPassRequestView", {
5555
// buttons
5656
const grp = new qx.ui.container.Composite(new qx.ui.layout.HBox(10));
5757

58-
const submitBtn = this.__submitBtn = new qx.ui.form.Button(this.tr("Submit")).set({
58+
const submitBtn = this.__submitBtn = new osparc.ui.form.FetchButton(this.tr("Submit")).set({
5959
center: true,
6060
appearance: "form-button"
6161
});
@@ -83,17 +83,20 @@ qx.Class.define("osparc.auth.ui.ResetPassRequestView", {
8383
},
8484

8585
__submit: function(email) {
86-
const manager = osparc.auth.Manager.getInstance();
86+
this.__submitBtn.setFetching(true);
8787

8888
const successFun = log => {
89+
this.__submitBtn.setFetching(false);
8990
this.fireDataEvent("done", log.message);
9091
osparc.FlashMessenger.getInstance().log(log);
9192
};
9293

9394
const failFun = err => {
95+
this.__submitBtn.setFetching(false);
9496
osparc.FlashMessenger.logError(err, this.tr("Could not request password reset"));
9597
};
9698

99+
const manager = osparc.auth.Manager.getInstance();
97100
manager.resetPasswordRequest(email.getValue(), successFun, failFun, this);
98101
},
99102

0 commit comments

Comments
 (0)