Skip to content

Commit 728db70

Browse files
authored
🎨 [Frontend] Allow users copy OEC (ITISFoundation#7394)
1 parent 2e67b17 commit 728db70

File tree

12 files changed

+65
-65
lines changed

12 files changed

+65
-65
lines changed

‎packages/service-library/src/servicelib/aiohttp/rest_middlewares.py‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,9 @@
2424
from .typing_extension import Handler, Middleware
2525

2626
DEFAULT_API_VERSION = "v0"
27-
_FMSG_INTERNAL_ERROR_USER_FRIENDLY_WITH_OEC = (
27+
_FMSG_INTERNAL_ERROR_USER_FRIENDLY = (
2828
"We apologize for the inconvenience. "
29-
"Our team has recorded the issue [SupportID={error_code}]. "
30-
"If the issue persists please report it."
29+
"The issue has been recorded, please report it if it persists."
3130
)
3231

3332

@@ -52,7 +51,7 @@ def _process_and_raise_unexpected_error(request: web.BaseRequest, err: Exception
5251
"request.path": f"{request.path}",
5352
}
5453

55-
user_error_msg = _FMSG_INTERNAL_ERROR_USER_FRIENDLY_WITH_OEC.format(
54+
user_error_msg = _FMSG_INTERNAL_ERROR_USER_FRIENDLY.format(
5655
error_code=error_code
5756
)
5857
http_error = create_http_error(

‎packages/service-library/tests/aiohttp/test_rest_middlewares.py‎

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,9 @@
1313
import pytest
1414
from aiohttp import web
1515
from aiohttp.test_utils import TestClient
16-
from common_library.error_codes import parse_error_codes
1716
from common_library.json_serialization import json_dumps
1817
from servicelib.aiohttp import status
1918
from servicelib.aiohttp.rest_middlewares import (
20-
_FMSG_INTERNAL_ERROR_USER_FRIENDLY_WITH_OEC,
2119
envelope_middleware_factory,
2220
error_middleware_factory,
2321
)
@@ -234,14 +232,6 @@ async def test_raised_unhandled_exception(
234232
assert not data
235233
assert error
236234

237-
# user friendly message with OEC reference
238-
assert "OEC" in error["message"]
239-
parsed_oec = parse_error_codes(error["message"]).pop()
240-
assert (
241-
_FMSG_INTERNAL_ERROR_USER_FRIENDLY_WITH_OEC.format(error_code=parsed_oec)
242-
== error["message"]
243-
)
244-
245235
# avoids details
246236
assert not error.get("errors")
247237
assert not error.get("logs")

‎services/static-webserver/client/source/class/osparc/FlashMessenger.js‎

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,35 @@ qx.Class.define("osparc.FlashMessenger", {
8686
console.error(error);
8787
}
8888
const msg = this.extractMessage(error, defaultMessage);
89-
return this.getInstance().logAs(msg, "ERROR", duration);
89+
const flashMessage = this.getInstance().logAs(msg, "ERROR", duration);
90+
if (error && error["supportId"]) {
91+
flashMessage.addWidget(this.__createCopyOECWidget(msg, error["supportId"]));
92+
flashMessage.setDuration(flashMessage.getDuration()*2);
93+
}
94+
return flashMessage;
95+
},
96+
97+
__createCopyOECWidget: function(message, supportId) {
98+
const errorLabel = new qx.ui.basic.Atom().set({
99+
label: supportId,
100+
icon: "@FontAwesome5Solid/copy/10",
101+
iconPosition: "right",
102+
gap: 8,
103+
cursor: "pointer",
104+
alignX: "center",
105+
allowGrowX: false,
106+
});
107+
errorLabel.addListener("tap", () => {
108+
const dataToClipboard = {
109+
message,
110+
supportId,
111+
timestamp: new Date().toString(),
112+
url: window.location.href,
113+
studyId: osparc.store.Store.getInstance().getCurrentStudy() || "",
114+
}
115+
osparc.utils.Utils.copyTextToClipboard(JSON.stringify(dataToClipboard));
116+
});
117+
return errorLabel;
90118
},
91119
},
92120

@@ -143,14 +171,9 @@ qx.Class.define("osparc.FlashMessenger", {
143171
}
144172
this.__displayedMessagesCount++;
145173

146-
let duration = flashMessage.getDuration();
147-
if (duration === null) {
148-
const message = flashMessage.getMessage();
149-
const wordCount = message.split(" ").length;
150-
duration = Math.max(5500, wordCount*500); // An average reader takes 300ms to read a word
151-
}
174+
const duration = flashMessage.getDuration();
152175
if (duration !== 0) {
153-
qx.event.Timer.once(() => this.removeMessage(flashMessage), this, duration);
176+
flashMessage.timer = setTimeout(() => this.removeMessage(flashMessage), duration);
154177
}
155178
},
156179

‎services/static-webserver/client/source/class/osparc/ui/message/FlashMessage.js‎

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ qx.Class.define("osparc.ui.message.FlashMessage", {
3131
*/
3232
construct: function(message, level, duration) {
3333
this.base(arguments);
34-
this._setLayout(new qx.ui.layout.VBox(15));
34+
this._setLayout(new qx.ui.layout.VBox(10));
3535

3636
this.set({
3737
padding: 18,
@@ -48,14 +48,13 @@ qx.Class.define("osparc.ui.message.FlashMessage", {
4848
textColor: this.self().LOG_LEVEL_COLOR_MAP[level].color
4949
});
5050

51-
if (message) {
52-
this.setMessage(message);
53-
}
51+
this.setMessage(message);
5452

55-
// also support duration 0: the message won't be automatically removed
56-
if (duration != null) {
57-
this.setDuration(duration);
53+
if ([null, undefined].includes(duration)) {
54+
const wordCount = message.split(" ").length;
55+
duration = Math.max(5500, wordCount*500); // An average reader takes 300ms to read a word
5856
}
57+
this.setDuration(duration);
5958

6059
this.getChildControl("closebutton");
6160
},
@@ -68,13 +67,14 @@ qx.Class.define("osparc.ui.message.FlashMessage", {
6867

6968
message: {
7069
check: "String",
71-
nullable: true,
72-
apply: "__applyMessage"
70+
nullable: false,
71+
apply: "__applyMessage",
7372
},
7473

7574
duration: {
7675
check: "Number",
77-
nullable: true
76+
init: null,
77+
nullable: true,
7878
}
7979
},
8080

@@ -142,10 +142,7 @@ qx.Class.define("osparc.ui.message.FlashMessage", {
142142
},
143143

144144
__applyMessage: function(value) {
145-
const label = this.getChildControl("message");
146-
if (label) {
147-
label.setValue(value);
148-
}
145+
this.getChildControl("message").setValue(value);
149146
},
150147

151148
addWidget: function(widget) {

‎services/web/server/src/simcore_service_webserver/login/_constants.py‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
from typing import Final
22

33
MSG_2FA_CODE_SENT: Final[str] = "A code was sent by SMS to {phone_number}."
4-
MSG_2FA_UNAVAILABLE_OEC: Final[str] = (
5-
"Two-factor authentication is temporarily unavailable. Please try again later. ({error_code})"
6-
)
4+
MSG_2FA_UNAVAILABLE: Final[str] = "Two-factor authentication is temporarily unavailable"
75
MSG_ACTIVATED: Final[str] = "Your account has been activated."
86
MSG_ACTIVATION_REQUIRED: Final[str] = (
97
"Please activate your account via the email we sent before logging in."

‎services/web/server/src/simcore_service_webserver/login/_registration.py‎

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
""" Core functionality and tools for user's registration
1+
"""Core functionality and tools for user's registration
22
3-
- registration code
4-
- invitation code
3+
- registration code
4+
- invitation code
55
"""
66

77
import logging
@@ -214,9 +214,7 @@ def _invitations_request_context(invitation_code: str) -> Iterator[URL]:
214214

215215
except (ValidationError, InvalidInvitationError) as err:
216216
error_code = create_error_code(err)
217-
user_error_msg = (
218-
f"Invalid invitation. {MSG_INVITATIONS_CONTACT_SUFFIX} [{error_code}]"
219-
)
217+
user_error_msg = f"Invalid invitation. {MSG_INVITATIONS_CONTACT_SUFFIX}"
220218

221219
_logger.exception(
222220
**create_troubleshotting_log_kwargs(
@@ -233,7 +231,7 @@ def _invitations_request_context(invitation_code: str) -> Iterator[URL]:
233231

234232
except InvitationsServiceUnavailableError as err:
235233
error_code = create_error_code(err)
236-
user_error_msg = f"Unable to process your invitation since the invitations service is currently unavailable [{error_code}]"
234+
user_error_msg = "Unable to process your invitation since the invitations service is currently unavailable"
237235

238236
_logger.exception(
239237
**create_troubleshotting_log_kwargs(

‎services/web/server/src/simcore_service_webserver/login/errors.py‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,12 @@
77
from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON
88

99
from ..errors import WebServerBaseError
10-
from ._constants import MSG_2FA_UNAVAILABLE_OEC
10+
from ._constants import MSG_2FA_UNAVAILABLE
1111

1212
_logger = logging.getLogger(__name__)
1313

1414

15-
class LoginError(WebServerBaseError, ValueError):
16-
...
15+
class LoginError(WebServerBaseError, ValueError): ...
1716

1817

1918
class SendingVerificationSmsError(LoginError):
@@ -32,7 +31,7 @@ async def wrapper(request: web.Request) -> web.StreamResponse:
3231

3332
except (SendingVerificationSmsError, SendingVerificationEmailError) as exc:
3433
error_code = exc.error_code()
35-
front_end_msg = MSG_2FA_UNAVAILABLE_OEC.format(error_code=error_code)
34+
front_end_msg = MSG_2FA_UNAVAILABLE
3635
# in these cases I want to log the cause
3736
_logger.exception(
3837
**create_troubleshotting_log_kwargs(

‎services/web/server/src/simcore_service_webserver/login/handlers_confirmation.py‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,8 +187,7 @@ async def validate_confirmation_and_redirect(request: web.Request):
187187
error_code = create_error_code(err)
188188
user_error_msg = (
189189
f"Sorry, we cannot confirm your {action}."
190-
"Please try again in a few moments. "
191-
f"If the problem persist please contact support attaching this code ({error_code})"
190+
"Please try again in a few moments."
192191
)
193192

194193
_logger.exception(

‎services/web/server/src/simcore_service_webserver/login/handlers_registration.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ async def register(request: web.Request):
274274
)
275275
except Exception as err: # pylint: disable=broad-except
276276
error_code = create_error_code(err)
277-
user_error_msg = f"{MSG_CANT_SEND_MAIL} [{error_code}]"
277+
user_error_msg = MSG_CANT_SEND_MAIL
278278

279279
_logger.exception(
280280
**create_troubleshotting_log_kwargs(
@@ -416,7 +416,7 @@ async def register_phone(request: web.Request):
416416
except Exception as err: # pylint: disable=broad-except
417417
# Unhandled errors -> 503
418418
error_code = create_error_code(err)
419-
user_error_msg = f"Currently we cannot register phone numbers [{error_code}]"
419+
user_error_msg = "Currently we cannot register phone numbers"
420420

421421
_logger.exception(
422422
**create_troubleshotting_log_kwargs(

‎services/web/server/src/simcore_service_webserver/users/_users_rest.py‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,7 @@
5959
MissingGroupExtraPropertiesForProductError: HttpErrorInfo(
6060
status.HTTP_503_SERVICE_UNAVAILABLE,
6161
"The product is not ready for use until the configuration is fully completed. "
62-
"Please wait and try again. "
63-
"If this issue persists, contact support indicating this support code: {error_code}.",
62+
"Please wait and try again. ",
6463
),
6564
}
6665

0 commit comments

Comments
 (0)