Skip to content

Commit 1aa663e

Browse files
committed
Merge branch 'master' into 7348-add-dummy-functions-api
2 parents a27ae21 + 653b60a commit 1aa663e

File tree

81 files changed

+1464
-1228
lines changed

Some content is hidden

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

81 files changed

+1464
-1228
lines changed

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

packages/pytest-simcore/src/pytest_simcore/helpers/playwright.py

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from typing import Any, Final
1212

1313
from playwright._impl._sync_base import EventContextManager
14-
from playwright.sync_api import APIRequestContext, FrameLocator, Page, Request
14+
from playwright.sync_api import APIRequestContext, FrameLocator, Locator, Page, Request
1515
from playwright.sync_api import TimeoutError as PlaywrightTimeoutError
1616
from playwright.sync_api import WebSocket
1717
from pydantic import AnyUrl
@@ -137,9 +137,7 @@ def on_framereceived(payload: str | bytes) -> None:
137137
ctx.logger.debug("⬆️ Frame received: %s", payload)
138138

139139
def on_close(_: WebSocket) -> None:
140-
ctx.logger.warning(
141-
"⚠️ WebSocket closed. Attempting to reconnect..."
142-
)
140+
ctx.logger.warning("⚠️ WebSocket closed. Attempting to reconnect...")
143141
self._attempt_reconnect(ctx.logger)
144142

145143
def on_socketerror(error_msg: str) -> None:
@@ -320,9 +318,9 @@ def __call__(self, message: str) -> bool:
320318
new_progress
321319
!= self._current_progress[node_progress_event.progress_type]
322320
):
323-
self._current_progress[
324-
node_progress_event.progress_type
325-
] = new_progress
321+
self._current_progress[node_progress_event.progress_type] = (
322+
new_progress
323+
)
326324

327325
self.logger.info(
328326
"Current startup progress [expected number of node-progress-types=%d]: %s",
@@ -343,29 +341,30 @@ def __call__(self, message: str) -> bool:
343341
url = (
344342
f"https://{self.node_id}.services.{self.get_partial_product_url()}"
345343
)
346-
response = self.api_request_context.get(url, timeout=1000)
347-
level = logging.DEBUG
348-
if (response.status >= 400) and (response.status not in (502, 503)):
349-
level = logging.ERROR
350-
self.logger.log(
351-
level,
352-
"Querying service endpoint in case we missed some websocket messages. Url: %s Response: '%s' TIP: %s",
353-
url,
354-
f"{response.status}: {response.text()}",
355-
(
356-
"We are emulating the frontend; a 5XX response is acceptable if the service is not yet ready."
357-
),
358-
)
344+
with contextlib.suppress(PlaywrightTimeoutError):
345+
response = self.api_request_context.get(url, timeout=1000)
346+
level = logging.DEBUG
347+
if (response.status >= 400) and (response.status not in (502, 503)):
348+
level = logging.ERROR
349+
self.logger.log(
350+
level,
351+
"Querying service endpoint in case we missed some websocket messages. Url: %s Response: '%s' TIP: %s",
352+
url,
353+
f"{response.status}: {response.text()}",
354+
(
355+
"We are emulating the frontend; a 5XX response is acceptable if the service is not yet ready."
356+
),
357+
)
359358

360-
if response.status <= 400:
361-
# NOTE: If the response status is less than 400, it means that the backend is ready (There are some services that respond with a 3XX)
362-
if self.got_expected_node_progress_types():
363-
self.logger.warning(
364-
"⚠️ Progress bar didn't receive 100 percent but service is already running: %s. TIP: we missed some websocket messages! ⚠️", # https://github.com/ITISFoundation/osparc-simcore/issues/6449
365-
self.get_current_progress(),
366-
)
367-
return True
368-
self._last_poll_timestamp = datetime.now(UTC)
359+
if response.status <= 400:
360+
# NOTE: If the response status is less than 400, it means that the backend is ready (There are some services that respond with a 3XX)
361+
if self.got_expected_node_progress_types():
362+
self.logger.warning(
363+
"⚠️ Progress bar didn't receive 100 percent but service is already running: %s. TIP: we missed some websocket messages! ⚠️", # https://github.com/ITISFoundation/osparc-simcore/issues/6449
364+
self.get_current_progress(),
365+
)
366+
return True
367+
self._last_poll_timestamp = datetime.now(UTC)
369368

370369
return False
371370

@@ -508,3 +507,16 @@ def app_mode_trigger_next_app(page: Page) -> None:
508507
):
509508
# Move to next step (this auto starts the next service)
510509
page.get_by_test_id("AppMode_NextBtn").click()
510+
511+
512+
def wait_for_label_text(
513+
page: Page, locator: str, substring: str, timeout: int = 10000
514+
) -> Locator:
515+
page.locator(locator).wait_for(state="visible", timeout=timeout)
516+
517+
page.wait_for_function(
518+
f"() => document.querySelector('{locator}').innerText.includes('{substring}')",
519+
timeout=timeout,
520+
)
521+
522+
return page.locator(locator)

packages/pytest-simcore/src/pytest_simcore/helpers/webserver_login.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@
1111
from simcore_service_webserver.db.models import UserRole, UserStatus
1212
from simcore_service_webserver.groups.api import auto_add_user_to_product_group
1313
from simcore_service_webserver.login._constants import MSG_LOGGED_IN
14-
from simcore_service_webserver.login._registration import create_invitation_token
15-
from simcore_service_webserver.login.storage import AsyncpgStorage, get_plugin_storage
14+
from simcore_service_webserver.login._invitations_service import create_invitation_token
15+
from simcore_service_webserver.login._login_repository_legacy import (
16+
AsyncpgStorage,
17+
get_plugin_storage,
18+
)
1619
from simcore_service_webserver.products.products_service import list_products
1720
from simcore_service_webserver.security.api import clean_auth_policy_cache
1821
from yarl import URL

packages/pytest-simcore/src/pytest_simcore/simcore_storage_data_models.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
from sqlalchemy.dialects.postgresql import insert as pg_insert
1919
from sqlalchemy.ext.asyncio import AsyncConnection, AsyncEngine
2020

21-
from .helpers.faker_factories import random_project, random_user
21+
from .helpers.faker_factories import DEFAULT_FAKER, random_project, random_user
2222

2323

2424
@asynccontextmanager
@@ -62,7 +62,7 @@ async def other_user_id(sqlalchemy_async_engine: AsyncEngine) -> AsyncIterator[U
6262
@pytest.fixture
6363
async def create_project(
6464
user_id: UserID, sqlalchemy_async_engine: AsyncEngine
65-
) -> AsyncIterator[Callable[[], Awaitable[dict[str, Any]]]]:
65+
) -> AsyncIterator[Callable[..., Awaitable[dict[str, Any]]]]:
6666
created_project_uuids = []
6767

6868
async def _creator(**kwargs) -> dict[str, Any]:
@@ -71,7 +71,7 @@ async def _creator(**kwargs) -> dict[str, Any]:
7171
async with sqlalchemy_async_engine.begin() as conn:
7272
result = await conn.execute(
7373
projects.insert()
74-
.values(**random_project(**prj_config))
74+
.values(**random_project(DEFAULT_FAKER, **prj_config))
7575
.returning(sa.literal_column("*"))
7676
)
7777
row = result.one()

packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/async_jobs/async_jobs.py

Lines changed: 56 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
AsyncJobResult,
1313
AsyncJobStatus,
1414
)
15+
from models_library.api_schemas_rpc_async_jobs.exceptions import JobMissingError
1516
from models_library.rabbitmq_basic_types import RPCMethodName, RPCNamespace
1617
from pydantic import NonNegativeInt, TypeAdapter
1718
from tenacity import (
@@ -20,6 +21,7 @@
2021
before_sleep_log,
2122
retry,
2223
retry_if_exception_type,
24+
stop_after_attempt,
2325
stop_after_delay,
2426
wait_fixed,
2527
wait_random_exponential,
@@ -124,11 +126,11 @@ async def submit(
124126

125127

126128
_DEFAULT_RPC_RETRY_POLICY: dict[str, Any] = {
127-
"retry": retry_if_exception_type(RemoteMethodNotRegisteredError),
129+
"retry": retry_if_exception_type((RemoteMethodNotRegisteredError,)),
128130
"wait": wait_random_exponential(max=20),
129-
"stop": stop_after_delay(60),
131+
"stop": stop_after_attempt(30),
130132
"reraise": True,
131-
"before_sleep": before_sleep_log(_logger, logging.INFO),
133+
"before_sleep": before_sleep_log(_logger, logging.WARNING),
132134
}
133135

134136

@@ -146,7 +148,7 @@ async def _wait_for_completion(
146148
async for attempt in AsyncRetrying(
147149
stop=stop_after_delay(client_timeout.total_seconds()),
148150
reraise=True,
149-
retry=retry_if_exception_type(TryAgain),
151+
retry=retry_if_exception_type((TryAgain, JobMissingError)),
150152
before_sleep=before_sleep_log(_logger, logging.DEBUG),
151153
wait=wait_fixed(_DEFAULT_POLL_INTERVAL_S),
152154
):
@@ -184,45 +186,72 @@ async def result(self) -> Any:
184186
return await self._result
185187

186188

187-
async def submit_and_wait(
189+
async def wait_and_get_result(
188190
rabbitmq_rpc_client: RabbitMQRPCClient,
189191
*,
190192
rpc_namespace: RPCNamespace,
191193
method_name: str,
194+
job_id: AsyncJobId,
192195
job_id_data: AsyncJobNameData,
193196
client_timeout: datetime.timedelta,
194-
**kwargs,
195197
) -> AsyncGenerator[AsyncJobComposedResult, None]:
196-
async_job_rpc_get = None
198+
"""when a job is already submitted this will wait for its completion
199+
and return the composed result"""
197200
try:
198-
async_job_rpc_get = await submit(
199-
rabbitmq_rpc_client,
200-
rpc_namespace=rpc_namespace,
201-
method_name=method_name,
202-
job_id_data=job_id_data,
203-
**kwargs,
204-
)
205-
job_status: AsyncJobStatus | None = None
201+
job_status = None
206202
async for job_status in _wait_for_completion(
207203
rabbitmq_rpc_client,
208204
rpc_namespace=rpc_namespace,
209205
method_name=method_name,
210-
job_id=async_job_rpc_get.job_id,
206+
job_id=job_id,
211207
job_id_data=job_id_data,
212208
client_timeout=client_timeout,
213209
):
214210
assert job_status is not None # nosec
215211
yield AsyncJobComposedResult(job_status)
212+
213+
# return the result
216214
if job_status:
217215
yield AsyncJobComposedResult(
218216
job_status,
219217
result(
220218
rabbitmq_rpc_client,
221219
rpc_namespace=rpc_namespace,
222-
job_id=async_job_rpc_get.job_id,
220+
job_id=job_id,
223221
job_id_data=job_id_data,
224222
),
225223
)
224+
except (TimeoutError, CancelledError) as error:
225+
try:
226+
await cancel(
227+
rabbitmq_rpc_client,
228+
rpc_namespace=rpc_namespace,
229+
job_id=job_id,
230+
job_id_data=job_id_data,
231+
)
232+
except Exception as exc:
233+
raise exc from error # NOSONAR
234+
raise
235+
236+
237+
async def submit_and_wait(
238+
rabbitmq_rpc_client: RabbitMQRPCClient,
239+
*,
240+
rpc_namespace: RPCNamespace,
241+
method_name: str,
242+
job_id_data: AsyncJobNameData,
243+
client_timeout: datetime.timedelta,
244+
**kwargs,
245+
) -> AsyncGenerator[AsyncJobComposedResult, None]:
246+
async_job_rpc_get = None
247+
try:
248+
async_job_rpc_get = await submit(
249+
rabbitmq_rpc_client,
250+
rpc_namespace=rpc_namespace,
251+
method_name=method_name,
252+
job_id_data=job_id_data,
253+
**kwargs,
254+
)
226255
except (TimeoutError, CancelledError) as error:
227256
if async_job_rpc_get is not None:
228257
try:
@@ -235,3 +264,13 @@ async def submit_and_wait(
235264
except Exception as exc:
236265
raise exc from error
237266
raise
267+
268+
async for wait_and_ in wait_and_get_result(
269+
rabbitmq_rpc_client,
270+
rpc_namespace=rpc_namespace,
271+
method_name=method_name,
272+
job_id=async_job_rpc_get.job_id,
273+
job_id_data=job_id_data,
274+
client_timeout=client_timeout,
275+
):
276+
yield wait_and_

services/static-webserver/client/source/class/osparc/data/PollTask.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@
2222
qx.Class.define("osparc.data.PollTask", {
2323
extend: qx.core.Object,
2424

25-
construct: function(taskData, interval) {
25+
construct: function(taskData, interval = 1000) {
2626
this.base(arguments);
2727

28-
interval ? this.setPollInterval(interval) : this.initPollInterval();
28+
this.setPollInterval(interval);
2929

3030
if (taskData && "task_id" in taskData) {
3131
this.set({

services/static-webserver/client/source/class/osparc/data/Resources.js

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1241,26 +1241,6 @@ qx.Class.define("osparc.data.Resources", {
12411241
}
12421242
}
12431243
},
1244-
/*
1245-
* STORAGE ASYNC
1246-
*/
1247-
"storageAsyncJobs": {
1248-
useCache: false,
1249-
endpoints: {
1250-
jobStatus: {
1251-
method: "GET",
1252-
url: statics.API + "/storage/async-jobs/{jobId}/status"
1253-
},
1254-
jobResult: {
1255-
method: "GET",
1256-
url: statics.API + "/storage/async-jobs/{jobId}/result"
1257-
},
1258-
abortJob: {
1259-
method: "POST",
1260-
url: statics.API + "/storage/async-jobs/{jobId}/abort"
1261-
},
1262-
}
1263-
},
12641244
/*
12651245
* ACTIVITY
12661246
*/

0 commit comments

Comments
 (0)