Skip to content

Commit f393d7c

Browse files
Merge branch 'master' into is1905/add-new-fuctions-rest-api-endpoints
2 parents 57010e5 + f1c60a8 commit f393d7c

File tree

93 files changed

+1287
-851
lines changed

Some content is hidden

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

93 files changed

+1287
-851
lines changed

.github/workflows/ci-arm-build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ jobs:
3838
- name: show system environs
3939
run: ./ci/helpers/show_system_versions.bash
4040
- name: login to Dockerhub
41-
uses: docker/login-action@v2
41+
uses: docker/login-action@v3
4242
with:
4343
username: ${{ secrets.DOCKER_USERNAME }}
4444
password: ${{ secrets.DOCKER_PASSWORD }}

.github/workflows/ci-multi-architecture-fusing.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ jobs:
3939
- name: show system environs
4040
run: ./ci/helpers/show_system_versions.bash
4141
- name: login to Dockerhub
42-
uses: docker/login-action@v2
42+
uses: docker/login-action@v3
4343
with:
4444
username: ${{ secrets.DOCKER_USERNAME }}
4545
password: ${{ secrets.DOCKER_PASSWORD }}

api/specs/web-server/_users.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@
1010
from models_library.api_schemas_webserver.users import (
1111
MyFunctionPermissionsGet,
1212
MyPermissionGet,
13-
MyPhoneConfirm,
14-
MyPhoneRegister,
1513
MyProfileRestGet,
1614
MyProfileRestPatch,
1715
MyTokenCreate,
@@ -32,6 +30,10 @@
3230
UserNotificationCreate,
3331
UserNotificationPatch,
3432
)
33+
from simcore_service_webserver.users._controller.rest._rest_schemas import (
34+
MyPhoneConfirm,
35+
MyPhoneRegister,
36+
)
3537

3638
router = APIRouter(prefix=f"/{API_VTAG}", tags=["users"])
3739

api/specs/web-server/_users_admin.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from models_library.generics import Envelope
2020
from models_library.rest_pagination import Page
2121
from simcore_service_webserver._meta import API_VTAG
22-
from simcore_service_webserver.users.schemas import PreRegisteredUserGet
22+
from simcore_service_webserver.users.schemas import UserAccountRestPreRegister
2323

2424
router = APIRouter(prefix=f"/{API_VTAG}", tags=["users"])
2525

@@ -69,4 +69,4 @@ async def search_user_accounts(
6969
response_model=Envelope[UserAccountGet],
7070
tags=_extra_tags,
7171
)
72-
async def pre_register_user_account(_body: PreRegisteredUserGet): ...
72+
async def pre_register_user_account(_body: UserAccountRestPreRegister): ...

packages/models-library/src/models_library/api_schemas_long_running_tasks/tasks.py

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
from datetime import datetime
33
from typing import Any
44

5-
from pydantic import BaseModel, field_validator
5+
from common_library.exclude import Unset
6+
from pydantic import BaseModel, ConfigDict, model_validator
67

78
from .base import TaskId, TaskProgress
89

@@ -20,15 +21,30 @@ class TaskResult(BaseModel):
2021

2122
class TaskBase(BaseModel):
2223
task_id: TaskId
23-
task_name: str
24+
task_name: str | Unset = Unset.VALUE
25+
26+
@model_validator(mode="after")
27+
def try_populate_task_name_from_task_id(self) -> "TaskBase":
28+
# NOTE: currently this model is used to validate tasks coming from
29+
# the celery backend and form long_running_tasks
30+
# 1. if a task comes from Celery, it will keep it's given name
31+
# 2. if a task comes from long_running_tasks, it will extract it form
32+
# the task_id, which looks like "{PREFIX}.{TASK_NAME}.UNIQUE|{UUID}"
33+
34+
if self.task_id and self.task_name == Unset.VALUE:
35+
parts = self.task_id.split(".")
36+
if len(parts) > 1:
37+
self.task_name = urllib.parse.unquote(parts[1])
38+
39+
if self.task_name == Unset.VALUE:
40+
self.task_name = self.task_id
41+
42+
return self
43+
44+
model_config = ConfigDict(arbitrary_types_allowed=True)
2445

2546

2647
class TaskGet(TaskBase):
2748
status_href: str
2849
result_href: str
2950
abort_href: str
30-
31-
@field_validator("task_name")
32-
@classmethod
33-
def unquote_str(cls, v) -> str:
34-
return urllib.parse.unquote(v)

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

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from models_library.rest_base import RequestParameters
66
from pydantic import AliasGenerator, ConfigDict, Field, HttpUrl, SecretStr
77
from pydantic.alias_generators import to_camel
8+
from pydantic.config import JsonDict
89

910
from ..emails import LowerCaseEmailStr
1011
from ._base import InputSchema, OutputSchema
@@ -14,33 +15,39 @@ class AccountRequestInfo(InputSchema):
1415
form: dict[str, Any]
1516
captcha: str
1617

18+
@staticmethod
19+
def _update_json_schema_extra(schema: JsonDict) -> None:
20+
schema.update(
21+
{
22+
"example": {
23+
"form": {
24+
"firstName": "James",
25+
"lastName": "Maxwel",
26+
"email": "[email protected]",
27+
"phone": "+41 44 245 96 96",
28+
"company": "EM Com",
29+
"address": "Infinite Loop",
30+
"city": "Washington",
31+
"postalCode": "98001",
32+
"country": "Switzerland",
33+
"application": "Antenna_Design",
34+
"description": "Description of something",
35+
"hear": "Search_Engine",
36+
"privacyPolicy": True,
37+
"eula": True,
38+
},
39+
"captcha": "A12B34",
40+
}
41+
}
42+
)
43+
1744
model_config = ConfigDict(
1845
str_strip_whitespace=True,
1946
str_max_length=200,
2047
# NOTE: this is just informative. The format of the form is defined
2148
# currently in the front-end and it might change
2249
# SEE image in https://github.com/ITISFoundation/osparc-simcore/pull/5378
23-
json_schema_extra={
24-
"example": {
25-
"form": {
26-
"firstName": "James",
27-
"lastName": "Maxwel",
28-
"email": "[email protected]",
29-
"phone": "+1 123456789",
30-
"company": "EM Com",
31-
"address": "Infinite Loop",
32-
"city": "Washington",
33-
"postalCode": "98001",
34-
"country": "USA",
35-
"application": "Antenna_Design",
36-
"description": "Description of something",
37-
"hear": "Search_Engine",
38-
"privacyPolicy": True,
39-
"eula": True,
40-
},
41-
"captcha": "A12B34",
42-
}
43-
},
50+
json_schema_extra=_update_json_schema_extra,
4451
)
4552

4653

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

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -207,27 +207,6 @@ def _validate_user_name(cls, value: str):
207207
return value
208208

209209

210-
#
211-
# PHONE REGISTRATION
212-
#
213-
214-
215-
class MyPhoneRegister(InputSchema):
216-
phone: Annotated[
217-
str,
218-
StringConstraints(strip_whitespace=True, min_length=1),
219-
Field(description="Phone number to register"),
220-
]
221-
222-
223-
class MyPhoneConfirm(InputSchema):
224-
code: Annotated[
225-
str,
226-
StringConstraints(strip_whitespace=True, pattern=r"^[A-Za-z0-9]+$"),
227-
Field(description="Alphanumeric confirmation code"),
228-
]
229-
230-
231210
#
232211
# USER
233212
#

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

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,21 @@
1+
import os
12
from copy import deepcopy
2-
from typing import Any, TypeVar
3+
from typing import Annotated, Any, Final, TypeVar
34

45
from common_library.errors_classes import OsparcErrorMixin
56
from models_library.basic_types import ConstrainedStr
6-
7-
from pydantic import BaseModel
7+
from pydantic import BaseModel, Discriminator, PositiveInt, Tag
88

99
from .utils.string_substitution import OSPARC_IDENTIFIER_PREFIX
10+
from .utils.types import get_types_from_annotated_union
1011

1112
T = TypeVar("T")
1213

1314

14-
class OsparcVariableIdentifier(ConstrainedStr):
15+
class _BaseOsparcVariableIdentifier(ConstrainedStr):
1516
# NOTE: To allow parametrized value, set the type to Union[OsparcVariableIdentifier, ...]
1617
# NOTE: When dealing with str types, to avoid unexpected behavior, the following
1718
# order is suggested `OsparcVariableIdentifier | str`
18-
# NOTE: in below regex `{`` and `}` are respectively escaped with `{{` and `}}`
19-
pattern = (
20-
rf"^\${{1,2}}(?:\{{)?{OSPARC_IDENTIFIER_PREFIX}[A-Za-z0-9_]+(?:\}})?(:-.+)?$"
21-
)
2219

2320
def _get_without_template_markers(self) -> str:
2421
# $VAR
@@ -42,6 +39,40 @@ def default_value(self) -> str | None:
4239
parts = self._get_without_template_markers().split(":-")
4340
return parts[1] if len(parts) > 1 else None
4441

42+
@staticmethod
43+
def get_pattern(max_dollars: PositiveInt) -> str:
44+
# NOTE: in below regex `{`` and `}` are respectively escaped with `{{` and `}}`
45+
return rf"^\${{1,{max_dollars}}}(?:\{{)?{OSPARC_IDENTIFIER_PREFIX}[A-Za-z0-9_]+(?:\}})?(:-.+)?$"
46+
47+
48+
class PlatformOsparcVariableIdentifier(_BaseOsparcVariableIdentifier):
49+
pattern = _BaseOsparcVariableIdentifier.get_pattern(max_dollars=2)
50+
51+
52+
class OoilOsparcVariableIdentifier(_BaseOsparcVariableIdentifier):
53+
pattern = _BaseOsparcVariableIdentifier.get_pattern(max_dollars=4)
54+
55+
56+
_PLATFORM: Final[str] = "platform"
57+
_OOIL_VERSION: Final[str] = "ooil-version"
58+
59+
60+
def _get_discriminator_value(v: Any) -> str:
61+
_ = v
62+
if os.environ.get("ENABLE_OOIL_OSPARC_VARIABLE_IDENTIFIER", None):
63+
return _OOIL_VERSION
64+
65+
return _PLATFORM
66+
67+
68+
OsparcVariableIdentifier = Annotated[
69+
(
70+
Annotated[PlatformOsparcVariableIdentifier, Tag(_PLATFORM)]
71+
| Annotated[OoilOsparcVariableIdentifier, Tag(_OOIL_VERSION)]
72+
),
73+
Discriminator(_get_discriminator_value),
74+
]
75+
4576

4677
class UnresolvedOsparcVariableIdentifierError(OsparcErrorMixin, TypeError):
4778
msg_template = "Provided argument is unresolved: value={value}"
@@ -59,9 +90,9 @@ def example_func(par: OsparcVariableIdentifier | int) -> None:
5990
Raises:
6091
TypeError: if the the OsparcVariableIdentifier was unresolved
6192
"""
62-
if isinstance(var, OsparcVariableIdentifier):
93+
if isinstance(var, get_types_from_annotated_union(OsparcVariableIdentifier)):
6394
raise UnresolvedOsparcVariableIdentifierError(value=var)
64-
return var
95+
return var # type: ignore[return-value]
6596

6697

6798
def replace_osparc_variable_identifier( # noqa: C901
@@ -86,11 +117,11 @@ def replace_osparc_variable_identifier( # noqa: C901
86117
```
87118
"""
88119

89-
if isinstance(obj, OsparcVariableIdentifier):
90-
if obj.name in osparc_variables:
91-
return deepcopy(osparc_variables[obj.name]) # type: ignore
92-
if obj.default_value is not None:
93-
return deepcopy(obj.default_value) # type: ignore
120+
if isinstance(obj, get_types_from_annotated_union(OsparcVariableIdentifier)):
121+
if obj.name in osparc_variables: # type: ignore[attr-defined]
122+
return deepcopy(osparc_variables[obj.name]) # type: ignore[no-any-return,attr-defined]
123+
if obj.default_value is not None: # type: ignore[attr-defined]
124+
return deepcopy(obj.default_value) # type: ignore[no-any-return,attr-defined]
94125
elif isinstance(obj, dict):
95126
for key, value in obj.items():
96127
obj[key] = replace_osparc_variable_identifier(value, osparc_variables)
@@ -124,7 +155,7 @@ def raise_if_unresolved_osparc_variable_identifier_found(obj: Any) -> None:
124155
UnresolvedOsparcVariableIdentifierError: if not all instances of
125156
`OsparcVariableIdentifier` were replaced
126157
"""
127-
if isinstance(obj, OsparcVariableIdentifier):
158+
if isinstance(obj, get_types_from_annotated_union(OsparcVariableIdentifier)):
128159
raise_if_unresolved(obj)
129160
elif isinstance(obj, dict):
130161
for key, value in obj.items():

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ def _update_json_schema_extra(schema: JsonDict) -> None:
126126

127127

128128
class EnvelopedError(Envelope[None]):
129+
# SEE https://github.com/ITISFoundation/osparc-simcore/issues/443
129130
error: ErrorGet
130131

131132
model_config = ConfigDict(

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

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,21 @@
11
from collections.abc import Generator
22
from typing import Final
33

4-
from pydantic import BaseModel, ConfigDict, Field, TypeAdapter, ValidationInfo, field_validator
4+
from pydantic import (
5+
BaseModel,
6+
ConfigDict,
7+
Field,
8+
TypeAdapter,
9+
ValidationInfo,
10+
field_validator,
11+
)
512

613
from .basic_types import PortInt
7-
from .osparc_variable_identifier import OsparcVariableIdentifier, raise_if_unresolved
14+
from .osparc_variable_identifier import (
15+
OsparcVariableIdentifier,
16+
raise_if_unresolved,
17+
)
18+
from .utils.types import get_types_from_annotated_union
819

920
# Cloudflare DNS server address
1021
DEFAULT_DNS_SERVER_ADDRESS: Final[str] = "1.1.1.1" # NOSONAR
@@ -20,13 +31,15 @@ class _PortRange(BaseModel):
2031
@field_validator("upper")
2132
@classmethod
2233
def lower_less_than_upper(cls, v, info: ValidationInfo) -> PortInt:
23-
if isinstance(v, OsparcVariableIdentifier):
34+
if isinstance(v, get_types_from_annotated_union(OsparcVariableIdentifier)):
2435
return v # type: ignore # bypass validation if unresolved
2536

2637
upper = v
2738
lower: PortInt | OsparcVariableIdentifier | None = info.data.get("lower")
2839

29-
if lower and isinstance(lower, OsparcVariableIdentifier):
40+
if lower and isinstance(
41+
lower, get_types_from_annotated_union(OsparcVariableIdentifier)
42+
):
3043
return v # type: ignore # bypass validation if unresolved
3144

3245
if lower is None or lower >= upper:

0 commit comments

Comments
 (0)