Skip to content

Commit 5fd5898

Browse files
authored
✨ FE various: support email on expired message, Enter key linked to validate button and blink Output in App Mode (ITISFoundation#3400)
1 parent 65a95cd commit 5fd5898

File tree

11 files changed

+45
-36
lines changed

11 files changed

+45
-36
lines changed

services/static-webserver/client/source/class/osparc/auth/ui/LoginSMSCodeView.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,15 @@ qx.Class.define("osparc.auth.ui.LoginSMSCodeView", {
112112

113113
const manager = osparc.auth.Manager.getInstance();
114114
manager.validateCodeLogin(this.getUserEmail(), this.__validateCodeTF.getValue(), loginFun, failFun, this);
115+
},
116+
117+
_onAppear: function() {
118+
const command = new qx.ui.command.Command("Enter");
119+
this.__validateCodeBtn.setCommand(command);
120+
},
121+
122+
_onDisappear: function() {
123+
this.__validateCodeBtn.setCommand(null);
115124
}
116125
}
117126
});

services/static-webserver/client/source/class/osparc/component/node/BaseNodeView.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -374,8 +374,12 @@ qx.Class.define("osparc.component.node.BaseNodeView", {
374374
});
375375
this._outputsBtn.addListener("changeLabel", () => {
376376
// make it "blink"
377-
this._outputsBtn.setTextColor("ready-green");
378-
setTimeout(() => this._outputsBtn.setTextColor("text"), 1000);
377+
this._outputsBtn.getChildControl("label").setTextColor("ready-green");
378+
this._outputsBtn.getChildControl("icon").setTextColor("ready-green");
379+
setTimeout(() => {
380+
this._outputsBtn.getChildControl("label").setTextColor("text");
381+
this._outputsBtn.getChildControl("icon").setTextColor("text");
382+
}, 1000);
379383
});
380384

381385
this._addLogger();

services/static-webserver/client/source/class/osparc/utils/Clusters.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ qx.Class.define("osparc.utils.Clusters", {
2727

2828
construct: function() {
2929
this.base(arguments);
30+
3031
this.__clusterIds = [];
31-
this.__fetchDetailsTimers = [];
3232
},
3333

3434
statics: {
@@ -90,7 +90,6 @@ qx.Class.define("osparc.utils.Clusters", {
9090

9191
members: {
9292
__clusterIds: null,
93-
__fetchDetailsTimers: null,
9493

9594
__fetchDetails: function(cid) {
9695
const params = {

services/web/server/src/simcore_service_webserver/login/handlers.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,17 +58,19 @@ def _get_user_name(email: str) -> str:
5858
return username
5959

6060

61-
def _validate_user_status(user: dict, cfg):
61+
def _validate_user_status(user: dict, cfg, support_email: str):
6262
user_status: str = user["status"]
6363

6464
if user_status == BANNED or user["role"] == ANONYMOUS:
6565
raise web.HTTPUnauthorized(
66-
reason=cfg.MSG_USER_BANNED, content_type=MIMETYPE_APPLICATION_JSON
66+
reason=cfg.MSG_USER_BANNED.format(support_email=support_email),
67+
content_type=MIMETYPE_APPLICATION_JSON
6768
) # 401
6869

6970
if user_status == EXPIRED:
7071
raise web.HTTPUnauthorized(
71-
reason=cfg.MSG_USER_EXPIRED, content_type=MIMETYPE_APPLICATION_JSON
72+
reason=cfg.MSG_USER_EXPIRED.format(support_email=support_email),
73+
content_type=MIMETYPE_APPLICATION_JSON
7274
) # 401
7375

7476
if user_status == CONFIRMATION_PENDING:
@@ -300,6 +302,7 @@ async def login(request: web.Request):
300302
settings: LoginSettings = get_plugin_settings(request.app)
301303
db: AsyncpgStorage = get_plugin_storage(request.app)
302304
cfg: LoginOptions = get_plugin_options(request.app)
305+
product: Product = get_current_product(request)
303306

304307
email = body.email
305308
password = body.password
@@ -311,7 +314,7 @@ async def login(request: web.Request):
311314
reason=cfg.MSG_UNKNOWN_EMAIL, content_type=MIMETYPE_APPLICATION_JSON
312315
)
313316

314-
_validate_user_status(user, cfg)
317+
_validate_user_status(user, cfg, product.support_email)
315318

316319
if not check_password(password, user["password_hash"]):
317320
raise web.HTTPUnauthorized(
@@ -322,8 +325,6 @@ async def login(request: web.Request):
322325
assert user["email"] == email, "db corrupted. Invalid email" # nosec
323326

324327
if settings.LOGIN_2FA_REQUIRED and UserRole(user["role"]) <= UserRole.USER:
325-
product: Product = get_current_product(request)
326-
327328
if not user["phone"]:
328329
rsp = envelope_response(
329330
{
@@ -457,6 +458,7 @@ async def reset_password(request: web.Request):
457458

458459
db: AsyncpgStorage = get_plugin_storage(request.app)
459460
cfg: LoginOptions = get_plugin_options(request.app)
461+
product: Product = get_current_product(request)
460462

461463
email = body.email
462464

@@ -467,7 +469,7 @@ async def reset_password(request: web.Request):
467469
reason=cfg.MSG_UNKNOWN_EMAIL, content_type=MIMETYPE_APPLICATION_JSON
468470
) # 422
469471

470-
_validate_user_status(user, cfg)
472+
_validate_user_status(user, cfg, product.support_email)
471473

472474
assert user["status"] == ACTIVE # nosec
473475
assert user["email"] == email # nosec

services/web/server/src/simcore_service_webserver/login/plugin.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from ..email import setup_email
1313
from ..email_settings import SMTPSettings
1414
from ..email_settings import get_plugin_settings as get_email_plugin_settings
15+
from ..products import setup_products
1516
from ..redis import setup_redis
1617
from ..rest import setup_rest
1718
from .routes import create_routes
@@ -74,6 +75,7 @@ def setup_login(app: web.Application):
7475

7576
setup_db(app)
7677
setup_redis(app)
78+
setup_products(app)
7779
setup_rest(app)
7880
setup_email(app)
7981

services/web/server/src/simcore_service_webserver/login/settings.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,8 @@ def get_confirmation_lifetime(
9797
MSG_UNKNOWN_EMAIL: str = "This email is not registered"
9898
MSG_WRONG_PASSWORD: str = "Wrong password"
9999
MSG_PASSWORD_MISMATCH: str = "Password and confirmation do not match"
100-
MSG_USER_BANNED: str = "This user does not have anymore access"
101-
MSG_USER_EXPIRED: str = "This account has expired and does not have anymore access. Please contact support for further details."
100+
MSG_USER_BANNED: str = "This user does not have anymore access. Please contact support for further details: {support_email}"
101+
MSG_USER_EXPIRED: str = "This account has expired and does not have anymore access. Please contact support for further details: {support_email}"
102102
MSG_ACTIVATION_REQUIRED: str = (
103103
"You have to activate your account via email, before you can login"
104104
)

services/web/server/tests/unit/with_dbs/01/test_login_login.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@ async def test_login_blocked_user(
9595

9696
assert r.status == web.HTTPUnauthorized.status_code, str(payload)
9797
assert r.url.path == url.path
98-
assert expected_msg in payload["error"]["errors"][0]["message"]
98+
# expected_msg contains {support_email} at the end of the string
99+
assert expected_msg[:-20] in payload["error"]["errors"][0]["message"]
99100

100101

101102
async def test_login_inactive_user(client: TestClient, login_options: LoginOptions):

services/web/server/tests/unit/with_dbs/03/test_login_reset_password.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ async def test_blocked_user(
8484
assert client.app
8585
reset_url = client.app.router["auth_reset_password"].url_for()
8686

87-
expected_msg = getattr(cfg, f"MSG_USER_{user_status.name.upper()}")
87+
expected_msg: str = getattr(cfg, f"MSG_USER_{user_status.name.upper()}")
8888

8989
async with NewUser({"status": user_status.name}, app=client.app) as user:
9090
rp = await client.post(
@@ -98,7 +98,8 @@ async def test_blocked_user(
9898
await assert_status(rp, web.HTTPOk, cfg.MSG_EMAIL_SENT.format(**user))
9999

100100
out, _ = capsys.readouterr()
101-
assert parse_test_marks(out)["reason"] == expected_msg
101+
# expected_msg contains {support_email} at the end of the string
102+
assert expected_msg[:-20] in parse_test_marks(out)["reason"]
102103

103104

104105
async def test_inactive_user(client: TestClient, cfg: LoginOptions, capsys):

tests/e2e/tutorials/jupyterlabs.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ async function runTutorial() {
3636
for (let j = 1; j < 3; j++) {
3737
// open JLab
3838
await tutorial.openNode(j);
39-
await tutorial.waitFor(35000);
39+
await tutorial.waitFor(10000);
4040

4141
// Run the jlab nbook
4242
const jLabIframe = await tutorial.getIframe(workbenchData["nodeIds"][j]);
@@ -63,9 +63,10 @@ async function runTutorial() {
6363
await tutorial.takeScreenshot("after_run_all_menu");
6464

6565
if (j === 2) {
66-
await tutorial.waitFor(40000); // we are solving an em problem
67-
} else {
68-
await tutorial.waitFor(5000); // we are not solving an em problem
66+
await tutorial.waitFor(30000); // we are solving an em problem
67+
}
68+
else {
69+
await tutorial.waitFor(5000); // we are NOT solving an em problem
6970
}
7071

7172
const outFiles = [

tests/e2e/tutorials/sim4life.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ async function runTutorial() {
3434
false
3535
);
3636

37-
await tutorial.waitFor(35000, 'Wait for some time');
37+
await tutorial.waitFor(15000, 'Wait for some time');
3838
}
3939
catch (err) {
4040
await tutorial.setTutorialFailed(true);

tests/e2e/utils/auto.js

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -267,25 +267,15 @@ async function deleteFirstStudy(page, studyName) {
267267
}
268268

269269
await page.waitForSelector('[osparc-test-id="studiesList"]')
270-
const children = await utils.getVisibleChildrenIDs(page, '[osparc-test-id="studiesList"]');
271-
272-
// filter out the cards that are not studies
273-
[
274-
"newStudyBtn",
275-
"newPlanButton",
276-
"studiesLoading"
277-
].forEach(notAStudy => {
278-
const idx = children.indexOf(notAStudy);
279-
if (idx > -1) {
280-
children.splice(idx, 1);
281-
}
282-
});
283-
if (children.length === 0) {
270+
const childrenIDs = await utils.getVisibleChildrenIDs(page, '[osparc-test-id="studiesList"]');
271+
272+
const studyIDs = childrenIDs.filter(childId => childId.includes("studyBrowserListItem_"));
273+
if (studyIDs.length === 0) {
284274
console.log("Deleting first Study: no study found");
285275
return false;
286276
}
287277

288-
const studyCardId = children[0];
278+
const studyCardId = studyIDs[0];
289279
const firstChildId = '[osparc-test-id="' + studyCardId + '"]';
290280
const studyCardStyle = await utils.getStyle(page, firstChildId);
291281
if (studyCardStyle.cursor === "not-allowed") {

0 commit comments

Comments
 (0)