Skip to content

Commit d829b01

Browse files
committed
Merge branch 'master' into 1973-add-celery-worker-to-api-server
2 parents 8c3b881 + 35e7048 commit d829b01

Some content is hidden

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

45 files changed

+856
-270
lines changed

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"""
44

55
from enum import Enum, unique
6-
from typing import Annotated, Self, TypeAlias
6+
from typing import Annotated, Final, Self, TypeAlias
77

88
from pydantic import (
99
BaseModel,
@@ -65,6 +65,13 @@ def is_running(self) -> bool:
6565
return self in self.list_running_states()
6666

6767

68+
RUNNING_STATE_COMPLETED_STATES: Final[tuple[RunningState, ...]] = (
69+
RunningState.ABORTED,
70+
RunningState.FAILED,
71+
RunningState.SUCCESS,
72+
)
73+
74+
6875
@unique
6976
class DataState(str, Enum):
7077
UP_TO_DATE = "UPTODATE"

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,4 +325,4 @@ class ComputationalPipelineStatusMessage(RabbitMessageBase, ProjectMessageBase):
325325
run_result: RunningState
326326

327327
def routing_key(self) -> str | None:
328-
return f"{self.project_id}"
328+
return f"{self.project_id}.all_nodes"

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

Lines changed: 33 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import docker
1616
import jsonschema
1717
import pytest
18+
import pytest_asyncio
1819
import tenacity
1920
from pytest_simcore.helpers.logging_tools import log_context
2021
from pytest_simcore.helpers.typing_env import EnvVarsDict
@@ -146,7 +147,7 @@ def wait_till_registry_is_responsive(url: str) -> bool:
146147
# ********************************************************* Services ***************************************
147148

148149

149-
def _pull_push_service(
150+
async def _pull_push_service(
150151
pull_key: str,
151152
tag: str,
152153
new_registry: str,
@@ -213,11 +214,13 @@ def _pull_push_service(
213214
assert image.tag(new_image_tag)
214215

215216
# push the image to the new location
216-
with log_context(
217-
logging.INFO,
218-
msg=f"Pushing {pull_key}:{tag} -> {new_image_tag} ...",
219-
):
220-
client.images.push(new_image_tag)
217+
async with aiodocker.Docker() as client:
218+
await client.images.push(new_image_tag)
219+
# with log_context(
220+
# logging.INFO,
221+
# msg=f"Pushing {pull_key}:{tag} -> {new_image_tag} ...",
222+
# ):
223+
# client.images.push(new_image_tag)
221224

222225
# return image io.simcore.* labels
223226
image_labels = dict(image.labels)
@@ -230,10 +233,10 @@ def _pull_push_service(
230233
}
231234

232235

233-
@pytest.fixture(scope="session")
236+
@pytest_asyncio.fixture(scope="session", loop_scope="session")
234237
def docker_registry_image_injector(
235238
docker_registry: str, node_meta_schema: dict
236-
) -> Callable[..., dict[str, Any]]:
239+
) -> Callable[[str, str, str | None], Awaitable[dict[str, Any]]]:
237240
def inject_image(
238241
source_image_repo: str, source_image_tag: str, owner_email: str | None = None
239242
):
@@ -249,82 +252,86 @@ def inject_image(
249252

250253

251254
@pytest.fixture
252-
def osparc_service(
255+
async def osparc_service(
253256
docker_registry: str, node_meta_schema: dict, service_repo: str, service_tag: str
254257
) -> dict[str, Any]:
255258
"""pulls the service from service_repo:service_tag and pushes to docker_registry using the oSparc node meta schema
256259
NOTE: 'service_repo' and 'service_tag' defined as parametrization
257260
"""
258-
return _pull_push_service(
261+
return await _pull_push_service(
259262
service_repo, service_tag, docker_registry, node_meta_schema
260263
)
261264

262265

263-
@pytest.fixture(scope="session")
264-
def sleeper_service(docker_registry: str, node_meta_schema: dict) -> dict[str, Any]:
266+
@pytest_asyncio.fixture(scope="session", loop_scope="session")
267+
async def sleeper_service(
268+
docker_registry: str, node_meta_schema: dict
269+
) -> dict[str, Any]:
265270
"""Adds a itisfoundation/sleeper in docker registry"""
266-
return _pull_push_service(
271+
return await _pull_push_service(
267272
"itisfoundation/sleeper", "1.0.0", docker_registry, node_meta_schema
268273
)
269274

270275

271-
@pytest.fixture(scope="session")
272-
def jupyter_service(docker_registry: str, node_meta_schema: dict) -> dict[str, Any]:
276+
@pytest_asyncio.fixture(scope="session", loop_scope="session")
277+
async def jupyter_service(
278+
docker_registry: str, node_meta_schema: dict
279+
) -> dict[str, Any]:
273280
"""Adds a itisfoundation/jupyter-base-notebook in docker registry"""
274-
return _pull_push_service(
281+
return await _pull_push_service(
275282
"itisfoundation/jupyter-base-notebook",
276283
"2.13.0",
277284
docker_registry,
278285
node_meta_schema,
279286
)
280287

281288

282-
@pytest.fixture(scope="session", params=["2.0.7"])
289+
@pytest_asyncio.fixture(scope="session", loop_scope="session", params=["2.0.7"])
283290
def dy_static_file_server_version(request: pytest.FixtureRequest):
284291
return request.param
285292

286293

287-
@pytest.fixture(scope="session")
288-
def dy_static_file_server_service(
294+
@pytest_asyncio.fixture(scope="session", loop_scope="session")
295+
async def dy_static_file_server_service(
289296
docker_registry: str, node_meta_schema: dict, dy_static_file_server_version: str
290297
) -> dict[str, Any]:
291298
"""
292299
Adds the below service in docker registry
293300
itisfoundation/dy-static-file-server
294301
"""
295-
return _pull_push_service(
302+
return await _pull_push_service(
296303
"itisfoundation/dy-static-file-server",
297304
dy_static_file_server_version,
298305
docker_registry,
299306
node_meta_schema,
300307
)
301308

302309

303-
@pytest.fixture(scope="session")
304-
def dy_static_file_server_dynamic_sidecar_service(
310+
@pytest_asyncio.fixture(scope="session", loop_scope="session")
311+
async def dy_static_file_server_dynamic_sidecar_service(
305312
docker_registry: str, node_meta_schema: dict, dy_static_file_server_version: str
306313
) -> dict[str, Any]:
307314
"""
308315
Adds the below service in docker registry
309316
itisfoundation/dy-static-file-server-dynamic-sidecar
310317
"""
311-
return _pull_push_service(
318+
return await _pull_push_service(
312319
"itisfoundation/dy-static-file-server-dynamic-sidecar",
313320
dy_static_file_server_version,
314321
docker_registry,
315322
node_meta_schema,
316323
)
317324

318325

319-
@pytest.fixture(scope="session")
320-
def dy_static_file_server_dynamic_sidecar_compose_spec_service(
326+
@pytest_asyncio.fixture(scope="session", loop_scope="session")
327+
async def dy_static_file_server_dynamic_sidecar_compose_spec_service(
321328
docker_registry: str, node_meta_schema: dict, dy_static_file_server_version: str
322329
) -> dict[str, Any]:
323330
"""
324331
Adds the below service in docker registry
325332
itisfoundation/dy-static-file-server-dynamic-sidecar-compose-spec
326333
"""
327-
return _pull_push_service(
334+
return await _pull_push_service(
328335
"itisfoundation/dy-static-file-server-dynamic-sidecar-compose-spec",
329336
dy_static_file_server_version,
330337
docker_registry,

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,29 @@ def __call__(self, message: str) -> bool:
302302
return False
303303

304304

305+
@dataclass
306+
class SocketIOWaitNodeForOutputs:
307+
logger: logging.Logger
308+
expected_number_of_outputs: int
309+
node_id: str
310+
311+
def __call__(self, message: str) -> bool:
312+
if message.startswith(SOCKETIO_MESSAGE_PREFIX):
313+
decoded_message = decode_socketio_42_message(message)
314+
if decoded_message.name == _OSparcMessages.NODE_UPDATED:
315+
assert "data" in decoded_message.obj
316+
assert "node_id" in decoded_message.obj
317+
if decoded_message.obj["node_id"] == self.node_id:
318+
assert "outputs" in decoded_message.obj["data"]
319+
320+
return (
321+
len(decoded_message.obj["data"]["outputs"])
322+
== self.expected_number_of_outputs
323+
)
324+
325+
return False
326+
327+
305328
@dataclass
306329
class SocketIOOsparcMessagePrinter:
307330
include_logger_messages: bool = False

services/director-v2/src/simcore_service_director_v2/utils/computations.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from typing import Any
44

55
import arrow
6-
from models_library.projects_state import RunningState
6+
from models_library.projects_state import RUNNING_STATE_COMPLETED_STATES, RunningState
77
from models_library.services import ServiceKeyVersion
88
from models_library.services_regex import SERVICE_KEY_RE
99
from models_library.users import UserID
@@ -15,7 +15,7 @@
1515

1616
_logger = logging.getLogger(__name__)
1717

18-
_COMPLETED_STATES = (RunningState.ABORTED, RunningState.FAILED, RunningState.SUCCESS)
18+
1919
_RUNNING_STATES = (RunningState.STARTED,)
2020
_TASK_TO_PIPELINE_CONVERSIONS = {
2121
# tasks are initially in NOT_STARTED state, then they transition to published
@@ -50,16 +50,16 @@
5050
RunningState.NOT_STARTED,
5151
): RunningState.NOT_STARTED,
5252
# if there are only completed states with FAILED --> FAILED
53-
(*_COMPLETED_STATES,): RunningState.FAILED,
53+
(*RUNNING_STATE_COMPLETED_STATES,): RunningState.FAILED,
5454
# if there are only completed states with FAILED and not started ones --> NOT_STARTED
5555
(
56-
*_COMPLETED_STATES,
56+
*RUNNING_STATE_COMPLETED_STATES,
5757
RunningState.NOT_STARTED,
5858
): RunningState.NOT_STARTED,
5959
# the generic case where we have a combination of completed states, running states,
6060
# or published/pending tasks, not_started is a started pipeline
6161
(
62-
*_COMPLETED_STATES,
62+
*RUNNING_STATE_COMPLETED_STATES,
6363
*_RUNNING_STATES,
6464
RunningState.PUBLISHED,
6565
RunningState.PENDING,

services/director-v2/tests/integration/02/test_dynamic_services_routes.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,11 +276,13 @@ async def _mocked_context_manger(*args, **kwargs) -> AsyncIterator[None]:
276276
async def key_version_expected(
277277
dy_static_file_server_dynamic_sidecar_service: dict,
278278
dy_static_file_server_service: dict,
279-
docker_registry_image_injector: Callable,
279+
docker_registry_image_injector: Callable[
280+
[str, str, str | None], Awaitable[dict[str, Any]]
281+
],
280282
) -> list[tuple[ServiceKeyVersion, bool]]:
281283
results: list[tuple[ServiceKeyVersion, bool]] = []
282284

283-
sleeper_service = docker_registry_image_injector(
285+
sleeper_service = await docker_registry_image_injector(
284286
"itisfoundation/sleeper", "2.1.1", "[email protected]"
285287
)
286288

services/static-webserver/client/source/class/osparc/dashboard/CardBase.js

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -148,10 +148,7 @@ qx.Class.define("osparc.dashboard.CardBase", {
148148
return false;
149149
}
150150
case "shared-with-everyone": {
151-
const everyoneGroupIds = [
152-
groupsStore.getEveryoneProductGroup().getGroupId(),
153-
groupsStore.getEveryoneGroup().getGroupId(),
154-
];
151+
const everyoneGroupIds = groupsStore.getEveryoneGroupIds();
155152
const found = Object.keys(checks).some(gId => everyoneGroupIds.includes(parseInt(gId)));
156153
// show those that are shared with "1" or product everyone's groupId
157154
return !found;
@@ -190,13 +187,12 @@ qx.Class.define("osparc.dashboard.CardBase", {
190187

191188
// Icon
192189
const groupsStore = osparc.store.Groups.getInstance();
193-
const groupEveryone = groupsStore.getEveryoneGroup();
194-
const groupProductEveryone = groupsStore.getEveryoneProductGroup();
190+
const everyoneGroupIds = groupsStore.getEveryoneGroupIds();
195191
const organizations = groupsStore.getOrganizations();
196192
const myGroupId = groupsStore.getMyGroupId();
197193

198194
const organizationIds = Object.keys(organizations).map(key => parseInt(key));
199-
if (gids.includes(groupEveryone.getGroupId()) || gids.includes(groupProductEveryone.getGroupId())) {
195+
if (gids.some(gid => everyoneGroupIds.includes(gid))) {
200196
shareIcon.setSource(osparc.dashboard.CardBase.SHARED_ALL);
201197
} else if (organizationIds.filter(value => gids.includes(value)).length) { // find intersection
202198
shareIcon.setSource(osparc.dashboard.CardBase.SHARED_ORGS);
@@ -230,14 +226,11 @@ qx.Class.define("osparc.dashboard.CardBase", {
230226

231227
addHintFromGids: function(icon, gids) {
232228
const groupsStore = osparc.store.Groups.getInstance();
233-
const groupEveryone = groupsStore.getEveryoneGroup();
234-
const groupProductEveryone = groupsStore.getEveryoneProductGroup();
229+
const everyoneGroups = groupsStore.getEveryoneGroups();
235230
const organizations = groupsStore.getOrganizations();
236231
const myGroupId = groupsStore.getMyGroupId();
237232

238-
const groups = [];
239-
groups.push(groupEveryone);
240-
groups.push(groupProductEveryone);
233+
const groups = everyoneGroups.slice();
241234
groups.push(...Object.values(organizations));
242235
const sharedGrps = [];
243236
groups.forEach(group => {
@@ -275,7 +268,7 @@ qx.Class.define("osparc.dashboard.CardBase", {
275268
break;
276269
}
277270
let sharedGrpLabel = sharedGrps[i].getLabel();
278-
if ([groupEveryone, groupProductEveryone].includes(sharedGrps[i])) {
271+
if (everyoneGroups.includes(sharedGrps[i])) {
279272
sharedGrpLabel = "Public";
280273
}
281274
if (!sharedGrpLabels.includes(sharedGrpLabel)) {

services/static-webserver/client/source/class/osparc/dashboard/GridButtonItem.js

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,8 @@ qx.Class.define("osparc.dashboard.GridButtonItem", {
250250
_applyOwner: function(value, old) {
251251
const label = this.getChildControl("subtitle-text");
252252
if (osparc.utils.Resources.isFunction(this.getResourceData())) {
253-
const canIWrite = Boolean(this.getResourceData()["accessRights"]["write"]);
253+
// Functions don't have 'owner'
254+
const canIWrite = osparc.data.model.Function.canIWrite(this.getResourceData()["accessRights"]);
254255
label.setValue(canIWrite ? "My Function" : "Read Only");
255256
} else {
256257
const user = this.__createOwner(value);
@@ -262,20 +263,15 @@ qx.Class.define("osparc.dashboard.GridButtonItem", {
262263
_applyAccessRights: function(value) {
263264
if (value && Object.keys(value).length) {
264265
const shareIcon = this.getChildControl("subtitle-icon");
265-
if (this.isResourceType("function")) {
266-
// in case of functions, the access rights are actually myAccessRights
267-
osparc.dashboard.CardBase.populateMyAccessRightsIcon(shareIcon, value);
268-
} else {
269-
shareIcon.addListener("tap", e => {
270-
e.stopPropagation();
271-
this.openAccessRights();
272-
}, this);
273-
shareIcon.addListener("pointerdown", e => e.stopPropagation());
274-
osparc.dashboard.CardBase.populateShareIcon(shareIcon, value);
266+
shareIcon.addListener("tap", e => {
267+
e.stopPropagation();
268+
this.openAccessRights();
269+
}, this);
270+
shareIcon.addListener("pointerdown", e => e.stopPropagation());
271+
osparc.dashboard.CardBase.populateShareIcon(shareIcon, value);
275272

276-
if (this.isResourceType("study")) {
277-
this._setStudyPermissions(value);
278-
}
273+
if (this.isResourceType("study")) {
274+
this._setStudyPermissions(value);
279275
}
280276
}
281277
},

services/static-webserver/client/source/class/osparc/dashboard/ListButtonItem.js

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,8 @@ qx.Class.define("osparc.dashboard.ListButtonItem", {
244244
_applyOwner: function(value, old) {
245245
const label = this.getChildControl("owner");
246246
if (osparc.utils.Resources.isFunction(this.getResourceData())) {
247-
const canIWrite = Boolean(this.getResourceData()["accessRights"]["write"]);
247+
// Functions don't have 'owner'
248+
const canIWrite = osparc.data.model.Function.canIWrite(this.getResourceData()["accessRights"]);
248249
label.setValue(canIWrite ? "My Function" : "Read Only");
249250
} else {
250251
const user = this.__createOwner(value);
@@ -257,20 +258,15 @@ qx.Class.define("osparc.dashboard.ListButtonItem", {
257258
_applyAccessRights: function(value) {
258259
if (value && Object.keys(value).length) {
259260
const shareIcon = this.getChildControl("shared-icon");
260-
if (this.isResourceType("function")) {
261-
// in case of functions, the access rights are actually myAccessRights
262-
osparc.dashboard.CardBase.populateMyAccessRightsIcon(shareIcon, value);
263-
} else {
264-
shareIcon.addListener("tap", e => {
265-
e.stopPropagation();
266-
this.openAccessRights();
267-
}, this);
268-
shareIcon.addListener("pointerdown", e => e.stopPropagation());
269-
osparc.dashboard.CardBase.populateShareIcon(shareIcon, value);
261+
shareIcon.addListener("tap", e => {
262+
e.stopPropagation();
263+
this.openAccessRights();
264+
}, this);
265+
shareIcon.addListener("pointerdown", e => e.stopPropagation());
266+
osparc.dashboard.CardBase.populateShareIcon(shareIcon, value);
270267

271-
if (this.isResourceType("study")) {
272-
this._setStudyPermissions(value);
273-
}
268+
if (this.isResourceType("study")) {
269+
this._setStudyPermissions(value);
274270
}
275271
}
276272
},

0 commit comments

Comments
 (0)