Skip to content

Commit 93260a3

Browse files
Merge branch 'master' into clean-pydantic-model-dump-warnings
2 parents fbcdb03 + 7dd5842 commit 93260a3

File tree

259 files changed

+9631
-3204
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

259 files changed

+9631
-3204
lines changed

.github/workflows/ci-testing-deploy.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -514,7 +514,7 @@ jobs:
514514
unit-test-storage:
515515
needs: changes
516516
if: ${{ needs.changes.outputs.storage == 'true' || github.event_name == 'push' }}
517-
timeout-minutes: 18 # if this timeout gets too small, then split the tests
517+
timeout-minutes: 25 # if this timeout gets too small, then split the tests
518518
name: "[unit] storage"
519519
runs-on: ${{ matrix.os }}
520520
strategy:

api/specs/web-server/_auth.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,29 +15,29 @@
1515
from models_library.rest_error import EnvelopedError, Log
1616
from pydantic import BaseModel, Field, confloat
1717
from simcore_service_webserver._meta import API_VTAG
18-
from simcore_service_webserver.login._2fa_handlers import Resend2faBody
19-
from simcore_service_webserver.login._auth_handlers import (
18+
from simcore_service_webserver.login._controller.rest.auth import (
2019
LoginBody,
2120
LoginNextPage,
2221
LoginTwoFactorAuthBody,
2322
LogoutBody,
2423
)
25-
from simcore_service_webserver.login.handlers_change import (
24+
from simcore_service_webserver.login._controller.rest.change import (
2625
ChangeEmailBody,
2726
ChangePasswordBody,
2827
ResetPasswordBody,
2928
)
30-
from simcore_service_webserver.login.handlers_confirmation import (
29+
from simcore_service_webserver.login._controller.rest.confirmation import (
3130
PhoneConfirmationBody,
3231
ResetPasswordConfirmation,
3332
)
34-
from simcore_service_webserver.login.handlers_registration import (
33+
from simcore_service_webserver.login._controller.rest.registration import (
3534
InvitationCheck,
3635
InvitationInfo,
3736
RegisterBody,
3837
RegisterPhoneBody,
3938
RegisterPhoneNextPage,
4039
)
40+
from simcore_service_webserver.login._controller.rest.twofa import Resend2faBody
4141

4242
router = APIRouter(prefix=f"/{API_VTAG}", tags=["auth"])
4343

api/specs/web-server/_storage.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
PresignedLink,
2323
)
2424
from models_library.api_schemas_webserver.storage import (
25+
BatchDeletePathsBodyParams,
2526
DataExportPost,
2627
ListPathsQueryParams,
2728
StorageLocationPathParams,
@@ -80,6 +81,19 @@ async def compute_path_size(_path: Annotated[StoragePathComputeSizeParams, Depen
8081
"""Compute the size of a path"""
8182

8283

84+
@router.post(
85+
"/storage/locations/{location_id}/-/paths:batchDelete",
86+
response_model=Envelope[TaskGet],
87+
status_code=status.HTTP_202_ACCEPTED,
88+
description="Deletes Paths",
89+
)
90+
async def batch_delete_paths(
91+
_path: Annotated[StorageLocationPathParams, Depends()],
92+
_body: Annotated[BatchDeletePathsBodyParams, Depends()],
93+
):
94+
"""deletes files/folders if user has the rights to"""
95+
96+
8397
@router.get(
8498
"/storage/locations/{location_id}/datasets",
8599
response_model=Envelope[list[DatasetMetaData]],

packages/aws-library/src/aws_library/s3/_client.py

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -85,21 +85,30 @@ async def create(
8585
cls, settings: S3Settings, s3_max_concurrency: int = _S3_MAX_CONCURRENCY_DEFAULT
8686
) -> "SimcoreS3API":
8787
session = aioboto3.Session()
88-
session_client = session.client( # type: ignore[call-overload]
89-
"s3",
90-
endpoint_url=f"{settings.S3_ENDPOINT}",
91-
aws_access_key_id=settings.S3_ACCESS_KEY,
92-
aws_secret_access_key=settings.S3_SECRET_KEY,
93-
region_name=settings.S3_REGION,
94-
config=Config(signature_version="s3v4"),
95-
)
96-
assert isinstance(session_client, ClientCreatorContext) # nosec
88+
session_client = None
9789
exit_stack = contextlib.AsyncExitStack()
98-
s3_client = cast(S3Client, await exit_stack.enter_async_context(session_client))
99-
# NOTE: this triggers a botocore.exception.ClientError in case the connection is not made to the S3 backend
100-
await s3_client.list_buckets()
90+
try:
91+
session_client = session.client( # type: ignore[call-overload]
92+
"s3",
93+
endpoint_url=f"{settings.S3_ENDPOINT}",
94+
aws_access_key_id=settings.S3_ACCESS_KEY,
95+
aws_secret_access_key=settings.S3_SECRET_KEY,
96+
region_name=settings.S3_REGION,
97+
config=Config(signature_version="s3v4"),
98+
)
99+
assert isinstance(session_client, ClientCreatorContext) # nosec
101100

102-
return cls(s3_client, session, exit_stack, s3_max_concurrency)
101+
s3_client = cast(
102+
S3Client, await exit_stack.enter_async_context(session_client)
103+
)
104+
# NOTE: this triggers a botocore.exception.ClientError in case the connection is not made to the S3 backend
105+
await s3_client.list_buckets()
106+
107+
return cls(s3_client, session, exit_stack, s3_max_concurrency)
108+
except Exception:
109+
await exit_stack.aclose()
110+
111+
raise
103112

104113
async def close(self) -> None:
105114
await self._exit_stack.aclose()

packages/models-library/src/models_library/api_schemas_catalog/services.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,11 +323,20 @@ def _update_json_schema_extra(schema: JsonDict) -> None:
323323
)
324324

325325

326-
PageRpcServicesGetV2: TypeAlias = PageRpc[
326+
PageRpcLatestServiceGet: TypeAlias = PageRpc[
327327
# WARNING: keep this definition in models_library and not in the RPC interface
328+
# otherwise the metaclass PageRpc[*] will create *different* classes in server/client side
329+
# and will fail to serialize/deserialize these parameters when transmitted/received
328330
LatestServiceGet
329331
]
330332

333+
PageRpcServiceRelease: TypeAlias = PageRpc[
334+
# WARNING: keep this definition in models_library and not in the RPC interface
335+
# otherwise the metaclass PageRpc[*] will create *different* classes in server/client side
336+
# and will fail to serialize/deserialize these parameters when transmitted/received
337+
ServiceRelease
338+
]
339+
331340
ServiceResourcesGet: TypeAlias = ServiceResourcesDict
332341

333342

@@ -365,3 +374,6 @@ class MyServiceGet(CatalogOutputSchema):
365374

366375
owner: GroupID | None
367376
my_access_rights: ServiceGroupAccessRightsV2
377+
378+
379+
__all__: tuple[str, ...] = ("ServiceRelease",)

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ class ListPathsQueryParams(InputSchema, CursorQueryParameters):
3636
] = DEFAULT_NUMBER_OF_PATHS_PER_PAGE
3737

3838

39+
class BatchDeletePathsBodyParams(InputSchema):
40+
paths: set[Path]
41+
42+
3943
class DataExportPost(InputSchema):
4044
paths: list[StorageFileID]
4145

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828

2929
PageOffsetInt: TypeAlias = NonNegativeInt
3030

31+
PageTotalCount: TypeAlias = NonNegativeInt
32+
3133
DEFAULT_NUMBER_OF_ITEMS_PER_PAGE: Final[PageLimitInt] = TypeAdapter(
3234
PageLimitInt
3335
).validate_python(20)
@@ -70,7 +72,7 @@ class PageQueryParameters(RequestParameters):
7072

7173
class PageMetaInfoLimitOffset(BaseModel):
7274
limit: PositiveInt = DEFAULT_NUMBER_OF_ITEMS_PER_PAGE
73-
total: NonNegativeInt
75+
total: PageTotalCount
7476
offset: NonNegativeInt = 0
7577
count: NonNegativeInt
7678

packages/notifications-library/src/notifications_library/_models.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,17 @@ class UserData:
1313
email: str
1414

1515

16+
@dataclass(frozen=True)
17+
class ProductUIData:
18+
logo_url: str
19+
strong_color: str
20+
21+
1622
@dataclass(frozen=True)
1723
class ProductData:
1824
product_name: ProductName
1925
display_name: str
2026
vendor_display_inline: str
2127
support_email: str
28+
homepage_url: str
29+
ui: ProductUIData
Lines changed: 97 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,110 @@
1+
{% set default_strong_color = "rgb(131, 0, 191)" %}
2+
{% set default_logo = "https://raw.githubusercontent.com/ITISFoundation/osparc-simcore/refs/heads/master/services/static-webserver/client/source/resource/osparc/osparc-black.svg" %}
3+
{% set default_homepage = "https://osparc.io/" %}
4+
15
<!DOCTYPE html>
26
<html lang="en">
37
<head>
48
<meta charset="UTF-8">
59
<meta name="viewport" content="width=device-width, initial-scale=1.0">
610
<title>{% block title %}{% endblock %}</title>
711
<style>
8-
body {
9-
font-family: Arial, sans-serif;
10-
margin: 0;
11-
padding: 20px;
12-
color: #333;
13-
}
14-
.container {
15-
background-color: #f9f9f9;
16-
padding: 20px;
17-
border-radius: 5px;
18-
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
19-
}
20-
a {
21-
color: #007bff;
22-
text-decoration: none;
23-
}
12+
body {
13+
font-family: Manrope, sans-serif;
14+
margin: 0;
15+
padding: 0;
16+
background-color: #f4f4f4;
17+
}
18+
.email-container {
19+
max-width: 600px;
20+
margin: 20px auto;
21+
background-color: #ffffff;
22+
padding: 20px;
23+
border-radius: 8px;
24+
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
25+
}
26+
.header {
27+
display: flex;
28+
align-items: center;
29+
border-bottom: 2px solid #ddd;
30+
padding-bottom: 10px;
31+
margin-bottom: 20px;
32+
}
33+
.logo {
34+
height: 40px;
35+
}
36+
.content {
37+
color: #333;
38+
font-size: 16px;
39+
line-height: 1.5;
40+
}
41+
a {
42+
color: #007bff;
43+
text-decoration: none;
44+
}
45+
.strong-button {
46+
cursor: pointer;
47+
background-color: {{ product.ui.strong_color | default(default_strong_color) }};
48+
color: white;
49+
padding: 10px;
50+
border: none;
51+
border-radius: 4px;
52+
overflow: hidden;
53+
white-space: nowrap;
54+
user-select: none;
55+
touch-action: none;
56+
outline: none;
57+
}
58+
.strong-button a {
59+
font-size: 16px;
60+
color: white;
61+
text-decoration: none;
62+
display: block;
63+
width: 100%;
64+
height: 100%;
65+
text-align: center;
66+
}
67+
.footer {
68+
margin-top: 20px;
69+
padding-top: 15px;
70+
border-top: 2px solid #ddd;
71+
font-size: 14px;
72+
text-align: center;
73+
color: #666;
74+
}
75+
.footer a {
76+
color: #007bff;
77+
text-decoration: none;
78+
}
79+
.github-logo {
80+
height: 20px;
81+
vertical-align: bottom;
82+
}
2483
</style>
2584
</head>
2685
<body>
27-
<div class="container">
28-
{% block content %}
29-
{% endblock %}
86+
<div class="email-container">
87+
<div class="header">
88+
<a href="{{ product.homepage_url | default(default_homepage) }}" target="_blank" rel="noopener noreferrer">
89+
<img src="{{ product.ui.logo_url | default(default_logo) }}" alt="Logo" class="logo">
90+
</a>
91+
</div>
92+
93+
<div class="content">
94+
{% block content %}
95+
{% endblock %}
96+
</div>
97+
98+
<div class="footer">
99+
<p>
100+
Visit the <a href="{{ product.homepage_url | default(default_homepage) }}" target="_blank" rel="noopener noreferrer">Platform</a> |
101+
Need help? <a href="mailto:{{ product.support_email }}">Support</a> |
102+
Powered by oSPARC
103+
<a href="https://github.com/ITISFoundation/osparc-simcore" target="_blank" rel="noopener noreferrer">
104+
<img src="https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png" alt="GitHub" class="github-logo">
105+
</a>
106+
</p>
107+
</div>
30108
</div>
31109
</body>
32110
</html>

packages/notifications-library/src/notifications_library/templates/on_change_email.email.content.html

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88
</p>
99

1010
<p>
11-
<a href="{{ link }}">{{ link }}</a>
11+
<!-- Many email clients strip or ignore certain CSS properties, including them inline -->
12+
<button class="strong-button" style="border-radius: 4px;">
13+
<a href="{{ link }}" style="color: white;">Change email</a>
14+
</button>
1215
</p>
1316

1417
<p>Please don't hesitate to contact us at {{ product.support_email }} if you need further help.

0 commit comments

Comments
 (0)