Skip to content

Commit 5fcf1be

Browse files
Merge branch 'master' into 8506-batch-db-requests-in-map-endpoint
2 parents ee7bb37 + d6b73db commit 5fcf1be

File tree

95 files changed

+1963
-437
lines changed

Some content is hidden

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

95 files changed

+1963
-437
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1810,7 +1810,7 @@ jobs:
18101810
with:
18111811
python-version: ${{ matrix.python }}
18121812
cache-dependency-glob: "**/e2e/requirements/requirements.txt"
1813-
- uses: actions/setup-node@v5.0.0
1813+
- uses: actions/setup-node@v6.0.0
18141814
with:
18151815
node-version: ${{ matrix.node }}
18161816
cache: "npm"

.github/workflows/codeql-analysis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@ jobs:
2929
uses: actions/checkout@v5
3030

3131
- name: Initialize CodeQL tools for scanning
32-
uses: github/codeql-action/init@v3
32+
uses: github/codeql-action/init@v4
3333
with:
3434
languages: ${{ matrix.language }}
3535
config-file: ./.github/codeql/codeql-config.yml
3636

3737
- name: Perform CodeQL Analysis
38-
uses: github/codeql-action/analyze@v3
38+
uses: github/codeql-action/analyze@v4

packages/models-library/VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.2.0
1+
0.3.0

packages/models-library/setup.cfg

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[bumpversion]
2-
current_version = 0.2.0
2+
current_version = 0.3.0
33
commit = True
44
message = packages/models-library version: {current_version} → {new_version}
55
tag = False
@@ -16,10 +16,10 @@ test = pytest
1616
[tool:pytest]
1717
asyncio_mode = auto
1818
asyncio_default_fixture_loop_scope = function
19-
markers =
19+
markers =
2020
diagnostics: "can be used to run diagnostics against deployed data (e.g. database, registry etc)"
2121
testit: "marks test to run during development"
2222

2323
[mypy]
24-
plugins =
24+
plugins =
2525
pydantic.mypy

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
from datetime import datetime
22
from typing import Annotated, Self
33

4+
from models_library.string_types import DisplaySafeStr
45
from pydantic import ConfigDict, Field, field_validator
56

67
from ..access_rights import AccessRights
7-
from ..basic_types import IDStr
88
from ..folders import FolderDB, FolderID
99
from ..groups import GroupID
1010
from ..utils.common_validators import null_or_none_str_to_none_validator
@@ -53,7 +53,7 @@ def from_domain_model(
5353

5454

5555
class FolderCreateBodyParams(InputSchema):
56-
name: IDStr
56+
name: DisplaySafeStr
5757
parent_folder_id: FolderID | None = None
5858
workspace_id: WorkspaceID | None = None
5959
model_config = ConfigDict(extra="forbid")
@@ -68,7 +68,7 @@ class FolderCreateBodyParams(InputSchema):
6868

6969

7070
class FolderReplaceBodyParams(InputSchema):
71-
name: IDStr
71+
name: DisplaySafeStr
7272
parent_folder_id: FolderID | None = None
7373
model_config = ConfigDict(extra="forbid")
7474

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

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
from common_library.basic_types import DEFAULT_FACTORY
55
from common_library.dict_tools import remap_keys
6+
from models_library.string_types import DescriptionSafeStr, NameSafeStr
67
from pydantic import (
78
AnyHttpUrl,
89
AnyUrl,
@@ -27,7 +28,7 @@
2728
StandardGroupCreate,
2829
StandardGroupUpdate,
2930
)
30-
from ..users import UserID, UserNameID
31+
from ..users import UserID, UserNameID, UserNameSafeID
3132
from ..utils.common_validators import create__check_only_one_is_set__root_validator
3233
from ._base import InputSchema, OutputSchema, OutputSchemaWithoutCamelCase
3334

@@ -155,8 +156,8 @@ def _update_json_schema_extra(schema: JsonDict) -> None:
155156

156157

157158
class GroupCreate(InputSchema):
158-
label: str
159-
description: str
159+
label: NameSafeStr
160+
description: DescriptionSafeStr
160161
thumbnail: AnyUrl | None = None
161162

162163
def to_domain_model(self) -> StandardGroupCreate:
@@ -173,8 +174,8 @@ def to_domain_model(self) -> StandardGroupCreate:
173174

174175

175176
class GroupUpdate(InputSchema):
176-
label: str | None = None
177-
description: str | None = None
177+
label: NameSafeStr | None = None
178+
description: DescriptionSafeStr | None = None
178179
thumbnail: AnyUrl | None = None
179180

180181
def to_domain_model(self) -> StandardGroupUpdate:
@@ -201,6 +202,12 @@ class MyGroupsGet(OutputSchema):
201202
description="Group ID of the app support team or None if no support is defined for this product"
202203
),
203204
] = None
205+
chatbot: Annotated[
206+
GroupGetBase | None,
207+
Field(
208+
description="Group ID of the support chatbot user or None if no chatbot is defined for this product"
209+
),
210+
] = None
204211

205212
model_config = ConfigDict(
206213
json_schema_extra={
@@ -245,6 +252,12 @@ class MyGroupsGet(OutputSchema):
245252
"description": "The support team of the application",
246253
"thumbnail": "https://placekitten.com/15/15",
247254
},
255+
"chatbot": {
256+
"gid": "6",
257+
"label": "Chatbot User",
258+
"description": "The chatbot user of the application",
259+
"thumbnail": "https://placekitten.com/15/15",
260+
},
248261
}
249262
}
250263
)
@@ -255,6 +268,7 @@ def from_domain_model(
255268
groups_by_type: GroupsByTypeTuple,
256269
my_product_group: tuple[Group, AccessRightsDict] | None,
257270
product_support_group: Group | None,
271+
product_chatbot_primary_group: Group | None,
258272
) -> Self:
259273
assert groups_by_type.primary # nosec
260274
assert groups_by_type.everyone # nosec
@@ -277,6 +291,13 @@ def from_domain_model(
277291
if product_support_group
278292
else None
279293
),
294+
chatbot=(
295+
GroupGetBase.model_validate(
296+
GroupGetBase.dump_basic_group_data(product_chatbot_primary_group)
297+
)
298+
if product_chatbot_primary_group
299+
else None
300+
),
280301
)
281302

282303

@@ -373,7 +394,7 @@ class GroupUserAdd(InputSchema):
373394
"""
374395

375396
uid: UserID | None = None
376-
user_name: Annotated[UserNameID | None, Field(alias="userName")] = None
397+
user_name: Annotated[UserNameSafeID | None, Field(alias="userName")] = None
377398
email: Annotated[
378399
LowerCaseEmailStr | None,
379400
Field(

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
from pydantic.config import JsonDict
2323

2424
from ..api_schemas_long_running_tasks.tasks import TaskGet
25-
from ..basic_types import LongTruncatedStr, ShortTruncatedStr
2625
from ..emails import LowerCaseEmailStr
2726
from ..folders import FolderID
2827
from ..groups import GroupID
@@ -41,6 +40,7 @@
4140
ProjectShareStatus,
4241
ProjectStateRunningState,
4342
)
43+
from ..string_types import LongTruncatedStr, ShortTruncatedStr
4444
from ..utils._original_fastapi_encoders import jsonable_encoder
4545
from ..utils.common_validators import (
4646
empty_str_to_none_pre_validator,

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

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import re
22
from datetime import date, datetime
33
from enum import Enum
4-
from typing import Annotated, Any, Literal, Self, TypeAlias
4+
from typing import Annotated, Any, Literal, Self
55

66
import annotated_types
77
from common_library.basic_types import DEFAULT_FACTORY
@@ -11,11 +11,11 @@
1111
from models_library.rest_filters import Filters
1212
from models_library.rest_pagination import PageQueryParameters
1313
from pydantic import (
14+
AfterValidator,
1415
BaseModel,
1516
ConfigDict,
1617
EmailStr,
1718
Field,
18-
StringConstraints,
1919
ValidationInfo,
2020
field_validator,
2121
model_validator,
@@ -27,12 +27,18 @@
2727
from ..groups import AccessRightsDict, Group, GroupID, GroupsByTypeTuple, PrimaryGroupID
2828
from ..products import ProductName
2929
from ..rest_base import RequestParameters
30+
from ..string_types import (
31+
GlobPatternSafeStr,
32+
SearchPatternSafeStr,
33+
validate_input_xss_safety,
34+
)
3035
from ..users import (
3136
FirstNameStr,
3237
LastNameStr,
3338
MyProfile,
3439
UserID,
3540
UserNameID,
41+
UserNameSafeID,
3642
UserPermission,
3743
UserThirdPartyToken,
3844
)
@@ -172,6 +178,7 @@ def from_domain_model(
172178
my_product_group: tuple[Group, AccessRightsDict] | None,
173179
my_preferences: AggregatedPreferences,
174180
my_support_group: Group | None,
181+
my_chatbot_user_group: Group | None,
175182
profile_contact: MyProfileAddressGet | None = None,
176183
) -> Self:
177184
profile_data = remap_keys(
@@ -194,17 +201,31 @@ def from_domain_model(
194201
return cls(
195202
**profile_data,
196203
groups=MyGroupsGet.from_domain_model(
197-
my_groups_by_type, my_product_group, my_support_group
204+
my_groups_by_type,
205+
my_product_group,
206+
my_support_group,
207+
my_chatbot_user_group,
198208
),
199209
preferences=my_preferences,
200210
contact=profile_contact,
201211
)
202212

203213

214+
FirstNameSafeStr = Annotated[
215+
FirstNameStr,
216+
AfterValidator(validate_input_xss_safety),
217+
]
218+
219+
LastNameSafeStr = Annotated[
220+
LastNameStr,
221+
AfterValidator(validate_input_xss_safety),
222+
]
223+
224+
204225
class MyProfileRestPatch(InputSchemaWithoutCamelCase):
205-
first_name: FirstNameStr | None = None
206-
last_name: LastNameStr | None = None
207-
user_name: Annotated[IDStr | None, Field(alias="userName", min_length=4)] = None
226+
first_name: FirstNameSafeStr | None = None
227+
last_name: LastNameSafeStr | None = None
228+
user_name: Annotated[UserNameSafeID | None, Field(alias="userName")] = None
208229
# NOTE: phone is updated via a dedicated endpoint!
209230

210231
privacy: MyProfilePrivacyPatch | None = None
@@ -262,8 +283,7 @@ class UsersGetParams(RequestParameters):
262283

263284
class UsersSearch(InputSchema):
264285
match_: Annotated[
265-
str,
266-
StringConstraints(strip_whitespace=True, min_length=1, max_length=80),
286+
SearchPatternSafeStr,
267287
Field(
268288
description="Search string to match with usernames and public profiles (e.g. emails, first/last name)",
269289
alias="match",
@@ -314,17 +334,9 @@ class UserAccountReject(InputSchema):
314334
email: EmailStr
315335

316336

317-
GlobString: TypeAlias = Annotated[
318-
str,
319-
StringConstraints(
320-
min_length=3, max_length=200, strip_whitespace=True, pattern=r"^[^%]*$"
321-
),
322-
]
323-
324-
325337
class UserAccountSearchQueryParams(RequestParameters):
326338
email: Annotated[
327-
GlobString | None,
339+
GlobPatternSafeStr | None,
328340
Field(
329341
description="complete or glob pattern for an email",
330342
),
@@ -336,7 +348,7 @@ class UserAccountSearchQueryParams(RequestParameters):
336348
),
337349
] = None
338350
user_name: Annotated[
339-
GlobString | None,
351+
GlobPatternSafeStr | None,
340352
Field(
341353
description="complete or glob pattern for a username",
342354
),

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

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
SIMPLE_VERSION_RE,
1515
UUID_RE,
1616
)
17-
from .utils.common_validators import trim_string_before
1817

1918
assert issubclass(LogLevel, Enum) # nosec
2019
assert issubclass(BootModeEnum, Enum) # nosec
@@ -151,38 +150,6 @@ def concatenate(*args: "IDStr", link_char: str = " ") -> "IDStr":
151150
return IDStr(result)
152151

153152

154-
_SHORT_TRUNCATED_STR_MAX_LENGTH: Final[int] = 600
155-
ShortTruncatedStr: TypeAlias = Annotated[
156-
str,
157-
StringConstraints(strip_whitespace=True),
158-
trim_string_before(max_length=_SHORT_TRUNCATED_STR_MAX_LENGTH),
159-
annotated_types.doc(
160-
"""
161-
A truncated string used to input e.g. titles or display names.
162-
Strips whitespaces and truncate strings that exceed the specified characters limit (curtail_length).
163-
Ensures that the **input** data length to the API is controlled and prevents exceeding large inputs silently,
164-
i.e. without raising errors.
165-
"""
166-
# SEE https://github.com/ITISFoundation/osparc-simcore/pull/5989#discussion_r1650506583
167-
),
168-
]
169-
170-
_LONG_TRUNCATED_STR_MAX_LENGTH: Final[int] = 65536 # same as github description
171-
LongTruncatedStr: TypeAlias = Annotated[
172-
str,
173-
StringConstraints(strip_whitespace=True),
174-
trim_string_before(max_length=_LONG_TRUNCATED_STR_MAX_LENGTH),
175-
annotated_types.doc(
176-
"""
177-
A truncated string used to input e.g. descriptions or summaries.
178-
Strips whitespaces and truncate strings that exceed the specified characters limit (curtail_length).
179-
Ensures that the **input** data length to the API is controlled and prevents exceeding large inputs silently,
180-
i.e. without raising errors.
181-
"""
182-
),
183-
]
184-
185-
186153
# auto-incremented primary-key IDs
187154
IdInt: TypeAlias = PositiveInt
188155
PrimaryKeyInt: TypeAlias = PositiveInt

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ class ConversationType(StrAutoEnum):
2424
auto() # Something like sticky note, can be located anywhere in the pipeline UI
2525
)
2626
SUPPORT = auto() # Support conversation
27+
SUPPORT_CALL = auto() # Support call conversation
28+
29+
def is_support_type(self) -> bool:
30+
return self in {ConversationType.SUPPORT, ConversationType.SUPPORT_CALL}
2731

2832

2933
class ConversationMessageType(StrAutoEnum):

0 commit comments

Comments
 (0)