Skip to content

✨ allowing to log back into a study when the tab was closed #2383

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from 55 commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
8b3b54a
nicer output
sanderegg Jun 11, 2021
93142ec
remove outdated todo
sanderegg Jun 11, 2021
0a42c05
allow logging in again when same user closed all tabs pointing to a s…
sanderegg Jun 11, 2021
cb88e8c
typo
sanderegg Jun 14, 2021
c246305
removed signal observer since import are already there
sanderegg Jun 14, 2021
30abfd5
updated schema for project state
sanderegg Jun 14, 2021
bd2b8e0
lock project when closing
sanderegg Jun 14, 2021
07f5288
set up project status
sanderegg Jun 14, 2021
527b7d2
some missing entries for the tests
sanderegg Jun 14, 2021
3861165
define project key
sanderegg Jun 14, 2021
7bb5f86
create a second client to access lock keys
sanderegg Jun 14, 2021
acef1c4
create constant for redis lock keys
sanderegg Jun 14, 2021
7ee32ef
constant for redis client for lock db
sanderegg Jun 14, 2021
6b6070c
notification when closing
sanderegg Jun 14, 2021
e88ae0e
adding the OPENING state
sanderegg Jun 14, 2021
1bd0c09
have some feedback at least
sanderegg Jun 14, 2021
8d3e8a4
steal project when opening from a disconnected same user
sanderegg Jun 14, 2021
d72eb26
simplify
sanderegg Jun 14, 2021
f0e5e58
missing status in project locked structure
sanderegg Jun 14, 2021
dc4afc8
fix unit test: missign ;
sanderegg Jun 14, 2021
d76abc8
fix unit test: missing required status value
sanderegg Jun 14, 2021
018deab
partly fixed
sanderegg Jun 14, 2021
6152c9a
fix unit test: missing semicolon
sanderegg Jun 15, 2021
b610754
pass client session id in state endpoint
sanderegg Jun 15, 2021
1f4c204
return OPENED, it's too complex to notify all clients with something …
sanderegg Jun 15, 2021
d2018a5
disallow opening the same project from 2 different tabs
sanderegg Jun 15, 2021
618518f
disallow opening 2 tabs on same project
sanderegg Jun 15, 2021
ab1942e
ensure redis database is properly cleaned
sanderegg Jun 16, 2021
ca67216
removed coveralls
sanderegg Jun 16, 2021
31006c9
ensure redis database is cleaned
sanderegg Jun 16, 2021
74676a3
added migration validator
sanderegg Jun 16, 2021
235b3ef
nicer
sanderegg Jun 16, 2021
b8ce571
removed coveralls
sanderegg Jun 16, 2021
203b7f6
use constant
sanderegg Jun 16, 2021
ff3f8ba
removed need of client session id in project/state endpoint
sanderegg Jun 16, 2021
e7655ae
fully test project locked
sanderegg Jun 16, 2021
ef00b4b
linter
sanderegg Jun 16, 2021
9bd89f7
making calls simpler?
sanderegg Jun 16, 2021
16cb365
linter
sanderegg Jun 16, 2021
83fe02c
missing the non called coroutine
sanderegg Jun 16, 2021
25a73b2
removed unnecesary try catch
sanderegg Jun 16, 2021
d5d8925
use relative imports
sanderegg Jun 16, 2021
4839dca
relative imports
sanderegg Jun 16, 2021
f602edb
one function for notifications
sanderegg Jun 16, 2021
c8270aa
added lock for cloning and exporting
sanderegg Jun 16, 2021
eda183c
ensure the state is set back to normal
sanderegg Jun 16, 2021
6914124
make notification optional when deleting the project
sanderegg Jun 16, 2021
db111af
needed to mock the notification subsystem there
sanderegg Jun 16, 2021
b70892e
@pcrespov @gitHK reviews: added tests to prevent different patterns b…
sanderegg Jun 18, 2021
dbfc512
@pcrespov review: added more auto validation
sanderegg Jun 18, 2021
5085659
@GitHK review: move _MIGRATION_FLAGS where it belongs
sanderegg Jun 18, 2021
db8069a
@pcrespov review: naming
sanderegg Jun 18, 2021
8ef3f23
@pcrespov review: user parse_obj
sanderegg Jun 18, 2021
4b8ac7b
@pcrespov review: add more docs
sanderegg Jun 18, 2021
792d4a7
@GitHK review: prepare context manager for easier usage once py3.8 is in
sanderegg Jun 18, 2021
3ed4b33
@pcrespov review: use namedtuple
sanderegg Jun 18, 2021
260612c
so that the GC does not delete guest users we call it manually
sanderegg Jun 18, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -189,11 +189,24 @@ endif

define _show_endpoints
# The following endpoints are available
echo "http://$(if $(IS_WSL2),$(get_my_ip),127.0.0.1):9081 - oSparc platform"
echo "http://$(if $(IS_WSL2),$(get_my_ip),127.0.0.1):18080/?pgsql=postgres&username=scu&db=simcoredb&ns=public - Postgres DB"
echo "http://$(if $(IS_WSL2),$(get_my_ip),127.0.0.1):9000 - Portainer"
set -o allexport; \
source $(CURDIR)/.env; \
set +o allexport; \
separator=------------------------------------------;\
separator=$${separator}$${separator}$${separator};\
rows="%-80s| %22s| %12s| %12s\n";\
TableWidth=140;\
printf "%80s| %22s| %12s| %12s\n" Endpoint Name User Password;\
printf "%.$${TableWidth}s\n" "$$separator";\
printf "$$rows" 'http://$(if $(IS_WSL2),$(get_my_ip),127.0.0.1):9081' 'oSparc platform';\
printf "$$rows" 'http://$(if $(IS_WSL2),$(get_my_ip),127.0.0.1):18080/?pgsql=postgres&username=$${POSTGRES_USER}&db=$${POSTGRES_DB}&ns=public' 'Postgres DB' $${POSTGRES_USER} $${POSTGRES_PASSWORD};\
printf "$$rows" 'http://$(if $(IS_WSL2),$(get_my_ip),127.0.0.1):9000' Portainer admin adminadmin;\
printf "$$rows" 'http://$(if $(IS_WSL2),$(get_my_ip),127.0.0.1):18081' Redis
endef

show-endpoints:
@$(_show_endpoints)

up-devel: .stack-simcore-development.yml .init-swarm $(CLIENT_WEB_OUTPUT) ## Deploys local development stack, qx-compile+watch and ops stack (pass 'make ops_disabled=1 up-...' to disable)
# Start compile+watch front-end container [front-end]
@$(MAKE_C) services/web/client down compile-dev flags=--watch
Expand Down
5 changes: 1 addition & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@
[![Requires.io]](https://requires.io/github/ITISFoundation/osparc-simcore/requirements/?branch=master "State of third party python dependencies")
<!-- [![travis-ci]](https://travis-ci.org/ITISFoundation/osparc-simcore "State of CI: build, test and pushing images") Commented until #2029 is resolved-->
[![Github-CI Push/PR]](https://github.com/ITISFoundation/osparc-simcore/actions?query=workflow%3A%22Github-CI+Push%2FPR%22+branch%3Amaster)
[![coveralls.io]](https://coveralls.io/github/ITISFoundation/osparc-simcore?branch=master)
[![codecov.io]](https://codecov.io/gh/ITISFoundation/osparc-simcore)
[![codecov](https://codecov.io/gh/ITISFoundation/osparc-simcore/branch/master/graph/badge.svg?token=h1rOE8q7ic)](https://codecov.io/gh/ITISFoundation/osparc-simcore)
[![github.io]](https://itisfoundation.github.io/)
[![itis.dockerhub]](https://hub.docker.com/u/itisfoundation)
[![license]](./LICENSE)
Expand All @@ -23,8 +22,6 @@
[travis-ci]:https://travis-ci.org/ITISFoundation/osparc-simcore.svg?branch=master
[github.io]:https://img.shields.io/website-up-down-green-red/https/itisfoundation.github.io.svg?label=documentation
[itis.dockerhub]:https://img.shields.io/website/https/hub.docker.com/u/itisfoundation.svg?down_color=red&label=dockerhub%20repos&up_color=green
[coveralls.io]:https://coveralls.io/repos/github/ITISFoundation/osparc-simcore/badge.svg?branch=master
[codecov.io]:https://codecov.io/gh/ITISFoundation/osparc-simcore/branch/master/graph/badge.svg
[license]:https://img.shields.io/github/license/ITISFoundation/osparc-simcore
[Github-CI Push/PR]:https://github.com/ITISFoundation/osparc-simcore/workflows/Github-CI%20Push/PR/badge.svg
<!------------------------------------------------------>
Expand Down
2 changes: 1 addition & 1 deletion api/specs/common/schemas/node-meta-v0.0.1-converted.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ properties:
key:
type: string
description: distinctive name for the node based on the docker registry path
pattern: ^(simcore)/(services)/(comp|dynamic)(/[\w/-]+)+$
pattern: ^(simcore)/(services)/(comp|dynamic|frontend)(/[\w/-]+)+$
example: simcore/services/comp/itis/sleeper
integration-version:
type: string
Expand Down
2 changes: 1 addition & 1 deletion api/specs/common/schemas/node-meta-v0.0.1.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"key": {
"type": "string",
"description": "distinctive name for the node based on the docker registry path",
"pattern": "^(simcore)/(services)/(comp|dynamic)(/[^\\s/]+)+$",
"pattern": "^(simcore)/(services)/(comp|dynamic|frontend)(/[\\w/-]+)+$",
"examples": [
"simcore/services/comp/itis/sleeper",
"simcore/services/dynamic/3dviewer"
Expand Down
16 changes: 14 additions & 2 deletions api/specs/common/schemas/project-v0.0.1-converted.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -405,11 +405,11 @@ properties:
properties:
value:
title: Value
description: True if the project is locked by another user
description: True if the project is locked
type: boolean
owner:
title: Owner
description: The user that owns the lock
description: If locked, the user that owns the lock
allOf:
- title: Owner
type: object
Expand Down Expand Up @@ -439,8 +439,20 @@ properties:
- user_id
- first_name
- last_name
status:
title: Status
description: The status of the project
enum:
- CLOSED
- CLOSING
- CLONING
- OPENING
- EXPORTING
- OPENED
type: string
required:
- value
- status
state:
title: State
description: The project running state
Expand Down
22 changes: 18 additions & 4 deletions api/specs/common/schemas/project-v0.0.1.json
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@
"key": {
"type": "string",
"description": "distinctive name for the node based on the docker registry path",
"pattern": "^(simcore)/(services)/(comp|dynamic|frontend)(/[^\\s/]+)+$",
"pattern": "^(simcore)/(services)/(comp|dynamic|frontend)(/[\\w/-]+)+$",
"examples": [
"simcore/services/comp/sleeper",
"simcore/services/dynamic/3dviewer",
Expand Down Expand Up @@ -556,12 +556,12 @@
"properties": {
"value": {
"title": "Value",
"description": "True if the project is locked by another user",
"description": "True if the project is locked",
"type": "boolean"
},
"owner": {
"title": "Owner",
"description": "The user that owns the lock",
"description": "If locked, the user that owns the lock",
"allOf": [
{
"title": "Owner",
Expand Down Expand Up @@ -600,10 +600,24 @@
]
}
]
},
"status": {
"title": "Status",
"description": "The status of the project",
"enum": [
"CLOSED",
"CLOSING",
"CLONING",
"OPENING",
"EXPORTING",
"OPENED"
],
"type": "string"
}
},
"required": [
"value"
"value",
"status"
]
}
]
Expand Down
54 changes: 50 additions & 4 deletions packages/models-library/src/models_library/projects_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from enum import Enum, unique
from typing import Optional

from pydantic import BaseModel, Extra, Field
from pydantic import BaseModel, Extra, Field, validator

from .projects_access import Owner

Expand All @@ -29,14 +29,60 @@ class DataState(str, Enum):
OUTDATED = "OUTDATED"


@unique
class ProjectStatus(str, Enum):
CLOSED = "CLOSED"
CLOSING = "CLOSING"
CLONING = "CLONING"
EXPORTING = "EXPORTING"
OPENING = "OPENING"
OPENED = "OPENED"


class ProjectLocked(BaseModel):
value: bool = Field(
..., description="True if the project is locked by another user"
value: bool = Field(..., description="True if the project is locked")
owner: Optional[Owner] = Field(
None, description="If locked, the user that owns the lock"
)
owner: Optional[Owner] = Field(None, description="The user that owns the lock")
status: ProjectStatus = Field(..., description="The status of the project")

class Config:
extra = Extra.forbid
use_enum_values = True
schema_extra = {
"examples": [
{"value": False, "status": ProjectStatus.CLOSED},
{
"value": True,
"status": ProjectStatus.OPENED,
"owner": {
"user_id": 123,
"first_name": "Johnny",
"last_name": "Cash",
},
},
]
}

@validator("owner", pre=True, always=True)
@classmethod
def check_not_null(v, values):
if values["value"] is True and v is None:
raise ValueError("value cannot be None when project is locked")
return v

@validator("status", always=True)
@classmethod
def check_status_compatible(v, values):
if values["value"] is False and v not in ["CLOSED", "OPENED"]:
raise ValueError(
f"status is set to {v} and lock is set to {values['value']}!"
)
if values["value"] is True and v == "CLOSED":
raise ValueError(
f"status is set to {v} and lock is set to {values['value']}!"
)
return v


class ProjectRunningState(BaseModel):
Expand Down
35 changes: 35 additions & 0 deletions packages/models-library/tests/test_projects_state.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from pprint import pformat

import pytest
from models_library.projects_state import ProjectLocked, ProjectStatus


@pytest.mark.parametrize(
"model_cls",
(ProjectLocked,),
)
def test_projects_state_model_examples(model_cls, model_cls_examples):
for name, example in model_cls_examples.items():
print(name, ":", pformat(example))
model_instance = model_cls(**example)
assert model_instance, f"Failed with {name}"


def test_project_locked_with_missing_owner_raises():
with pytest.raises(ValueError):
ProjectLocked(**{"value": True, "status": ProjectStatus.OPENED})
ProjectLocked.parse_obj({"value": False, "status": ProjectStatus.OPENED})


@pytest.mark.parametrize(
"lock, status",
[
(False, x)
for x in ProjectStatus
if x not in [ProjectStatus.CLOSED, ProjectStatus.OPENED]
]
+ [(True, ProjectStatus.CLOSED)],
)
def test_project_locked_with_allowed_values(lock: bool, status: ProjectStatus):
with pytest.raises(ValueError):
ProjectLocked.parse_obj({"value": lock, "status": status})
39 changes: 36 additions & 3 deletions packages/models-library/tests/test_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@

import re
from pprint import pformat
from typing import Any, Dict
from typing import Any, Callable, Dict, List

import pytest
import yaml
from models_library.basic_regex import VERSION_RE
from models_library.services import (
SERVICE_KEY_RE,
ServiceAccessRightsAtDB,
Expand Down Expand Up @@ -103,7 +104,6 @@ def test_service_models_examples(model_cls, model_cls_examples):
assert model_instance, f"Failed with {name}"


@pytest.mark.skip(reason="dev")
@pytest.mark.parametrize(
"service_key",
[
Expand Down Expand Up @@ -159,7 +159,7 @@ def test_service_models_examples(model_cls, model_cls_examples):
[SERVICE_KEY_RE, r"^(simcore)/(services)/(comp|dynamic|frontend)(/[^\s/]+)+$"],
ids=["pattern_with_w", "pattern_with_s"],
)
def test_service_key_regex_patterns(service_key, regex_pattern):
def test_service_key_regex_patterns(service_key: str, regex_pattern: str):
match = re.match(regex_pattern, service_key)
assert match

Expand All @@ -178,3 +178,36 @@ def test_services_model_examples(model_cls, model_cls_examples):
print(name, ":", pformat(example))
model_instance = model_cls(**example)
assert model_instance, f"Failed with {name}"


@pytest.mark.parametrize(
"python_regex_pattern, json_schema_file_name, json_schema_entry_paths",
[
(SERVICE_KEY_RE, "project-v0.0.1.json", ["key"]),
(VERSION_RE, "project-v0.0.1.json", ["version"]),
(VERSION_RE, "node-meta-v0.0.1.json", ["version"]),
(SERVICE_KEY_RE, "node-meta-v0.0.1.json", ["key"]),
],
)
def test_regex_pattern_same_in_jsonschema_and_python(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

python_regex_pattern: str,
json_schema_file_name: str,
json_schema_entry_paths: List[str],
json_schema_dict: Callable,
):
# read file in
json_schema_config = json_schema_dict(json_schema_file_name)
# go to keys
def _find_pattern_entry(obj: Dict[str, Any], key: str) -> Any:
if key in obj:
return obj[key]["pattern"]
for v in obj.values():
if isinstance(v, dict):
item = _find_pattern_entry(v, key)
if item is not None:
return item
return None

for x_path in json_schema_entry_paths:
json_pattern = _find_pattern_entry(json_schema_config, x_path)
assert json_pattern == python_regex_pattern
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ async def redis_service(redis_config: RedisConfig, monkeypatch) -> RedisConfig:
return redis_config


@pytest.fixture(scope="module")
@pytest.fixture(scope="function")
async def redis_client(loop, redis_config: RedisConfig) -> Iterator[aioredis.Redis]:
client = await aioredis.create_redis_pool(redis_config.dsn, encoding="utf-8")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,13 @@ def test_create_job_from_project():
},
"quality": {},
"tags": [],
"state": {"locked": {"value": False}, "state": {"value": "SUCCESS"}},
"state": {
"locked": {
"value": False,
"status": "CLOSED",
},
"state": {"value": "SUCCESS"},
},
},
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ paths:
key:
type: string
description: distinctive name for the node based on the docker registry path
pattern: '^(simcore)/(services)/(comp|dynamic)(/[\w/-]+)+$'
pattern: '^(simcore)/(services)/(comp|dynamic|frontend)(/[\w/-]+)+$'
example: simcore/services/comp/itis/sleeper
integration-version:
type: string
Expand Down Expand Up @@ -552,7 +552,7 @@ paths:
key:
type: string
description: distinctive name for the node based on the docker registry path
pattern: '^(simcore)/(services)/(comp|dynamic)(/[\w/-]+)+$'
pattern: '^(simcore)/(services)/(comp|dynamic|frontend)(/[\w/-]+)+$'
example: simcore/services/comp/itis/sleeper
integration-version:
type: string
Expand Down Expand Up @@ -2199,7 +2199,7 @@ components:
key:
type: string
description: distinctive name for the node based on the docker registry path
pattern: '^(simcore)/(services)/(comp|dynamic)(/[\w/-]+)+$'
pattern: '^(simcore)/(services)/(comp|dynamic|frontend)(/[\w/-]+)+$'
example: simcore/services/comp/itis/sleeper
integration-version:
type: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"key": {
"type": "string",
"description": "distinctive name for the node based on the docker registry path",
"pattern": "^(simcore)/(services)/(comp|dynamic)(/[^\\s/]+)+$",
"pattern": "^(simcore)/(services)/(comp|dynamic|frontend)(/[\\w/-]+)+$",
"examples": [
"simcore/services/comp/itis/sleeper",
"simcore/services/dynamic/3dviewer"
Expand Down
Loading