Skip to content

Commit 0038982

Browse files
committed
Merge branch 'feature/sc-request-call' of github.com:odeimaiz/osparc-simcore into feature/sc-request-call
2 parents b36e7d9 + 12c3aec commit 0038982

File tree

8 files changed

+129
-14
lines changed

8 files changed

+129
-14
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ qx.Class.define("osparc.ui.message.FlashMessageOEC", {
141141
if (confirmationWindow.getConfirmed()) {
142142
const extraContext = extraContextTA.getValue()
143143
const friendlyContext = this.__getSupportFriendlyContext();
144-
const text = "Dear Support team,\n" + extraContext + "\n" + friendlyContext;
144+
const text = "Dear Support Team,\n" + extraContext + "\n" + friendlyContext;
145145
textToAddMessageField(text);
146146
// This should be an automatic response in the chat
147147
const msg = this.tr("Thanks, your report has been sent.<br>Our support team will get back to you.");

services/web/server/src/simcore_service_webserver/conversations/_controller/_conversations_messages_rest.py

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,22 @@
2222
from models_library.utils.fastapi_encoders import jsonable_encoder
2323
from pydantic import BaseModel, ConfigDict
2424
from servicelib.aiohttp import status
25+
from servicelib.aiohttp.application_keys import APP_SETTINGS_KEY
2526
from servicelib.aiohttp.requests_validation import (
2627
parse_request_body_as,
2728
parse_request_path_parameters_as,
2829
parse_request_query_parameters_as,
2930
)
3031
from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON
3132
from servicelib.rest_constants import RESPONSE_MODEL_POLICY
32-
from simcore_service_webserver.users import users_service
3333

3434
from ..._meta import API_VTAG as VTAG
3535
from ...email import email_service
36+
from ...fogbugz.settings import FogbugzSettings
3637
from ...login.decorators import login_required
3738
from ...models import AuthenticatedRequestContext
3839
from ...products import products_web
40+
from ...users import users_service
3941
from ...utils_aiohttp import envelope_json_response
4042
from .. import _conversation_message_service, _conversation_service
4143
from ._common import ConversationPathParams, raise_unsupported_type
@@ -107,10 +109,50 @@ async def create_conversation_message(request: web.Request):
107109
)
108110

109111
# NOTE: This is done here in the Controller layer, as the interface around email currently needs request
110-
if is_first_message:
112+
product = products_web.get_current_product(request)
113+
fogbugz_settings_or_none: FogbugzSettings | None = request.app[
114+
APP_SETTINGS_KEY
115+
].WEBSERVER_FOGBUGZ
116+
if (
117+
product.support_standard_group_id
118+
and fogbugz_settings_or_none is not None
119+
and is_first_message
120+
):
121+
_logger.debug(
122+
"Support settings available and FogBugz client configured, creating FogBugz case."
123+
)
124+
assert product.support_assigned_fogbugz_project_id # nosec
125+
126+
try:
127+
_url = request.url
128+
_conversation_url = f"{_url.scheme}://{_url.host}/#/conversation/{path_params.conversation_id}"
129+
130+
await _conversation_service.create_fogbugz_case_for_support_conversation(
131+
request.app,
132+
conversation=_conversation,
133+
user_id=req_ctx.user_id,
134+
message_content=message.content,
135+
conversation_url=_conversation_url,
136+
host=request.host,
137+
product_support_assigned_fogbugz_project_id=product.support_assigned_fogbugz_project_id,
138+
fogbugz_url=str(fogbugz_settings_or_none.FOGBUGZ_URL),
139+
)
140+
except Exception: # pylint: disable=broad-except
141+
_logger.exception(
142+
"Failed to create support request FogBugz case for conversation %s.",
143+
_conversation.conversation_id,
144+
)
145+
146+
elif (
147+
product.support_standard_group_id
148+
and fogbugz_settings_or_none is None
149+
and is_first_message
150+
):
151+
_logger.debug(
152+
"Support settings available, but no FogBugz client configured, sending email instead to create FogBugz case."
153+
)
111154
try:
112155
user = await users_service.get_user(request.app, req_ctx.user_id)
113-
product = products_web.get_current_product(request)
114156
template_name = "request_support.jinja2"
115157
destination_email = product.support_email
116158
email_template_path = await products_web.get_product_template_path(
@@ -141,6 +183,8 @@ async def create_conversation_message(request: web.Request):
141183
template_name,
142184
destination_email,
143185
)
186+
else:
187+
_logger.debug("No support settings available, skipping FogBugz case creation.")
144188

145189
data = ConversationMessageRestGet.from_domain_model(message)
146190
return envelope_json_response(data, web.HTTPCreated)

services/web/server/src/simcore_service_webserver/conversations/_conversation_repository.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,9 @@ async def update(
279279
**updates.model_dump(exclude_unset=True),
280280
conversations.c.modified.name: func.now(),
281281
}
282+
_name = _updates.get("name", "Default")
283+
if _name is None:
284+
_updates["name"] = "no name"
282285

283286
async with transaction_context(get_asyncpg_engine(app), connection) as conn:
284287
result = await conn.execute(

services/web/server/src/simcore_service_webserver/conversations/_conversation_service.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
# pylint: disable=unused-argument
22

3+
import json
34
import logging
45
from typing import Any
6+
from urllib.parse import urljoin
57

68
from aiohttp import web
79
from models_library.basic_types import IDStr
@@ -22,6 +24,7 @@
2224
notify_conversation_deleted,
2325
notify_conversation_updated,
2426
)
27+
from ..fogbugz import FogbugzCaseCreate, get_fogbugz_rest_client
2528
from ..groups.api import list_user_groups_ids_with_read_access
2629
from ..products import products_service
2730
from ..projects._groups_repository import list_project_groups
@@ -240,3 +243,54 @@ async def list_support_conversations_for_user(
240243
limit=limit,
241244
order_by=OrderBy(field=IDStr("conversation_id"), direction=OrderDirection.DESC),
242245
)
246+
247+
248+
async def create_fogbugz_case_for_support_conversation(
249+
app: web.Application,
250+
*,
251+
conversation: ConversationGetDB,
252+
user_id: UserID,
253+
message_content: str,
254+
conversation_url: str,
255+
host: str,
256+
product_support_assigned_fogbugz_project_id: int,
257+
fogbugz_url: str,
258+
) -> None:
259+
"""Creates a FogBugz case for a support conversation and updates the conversation with the case URL."""
260+
user = await users_service.get_user(app, user_id)
261+
262+
description = f"""
263+
Dear Support Team,
264+
265+
We have received a support request from {user["first_name"]} {user["last_name"]} ({user["email"]}) on {host}.
266+
267+
All communication should take place in the Platform Support Center at the following link: {conversation_url}
268+
269+
First message content: {message_content}
270+
271+
Extra content: {json.dumps(conversation.extra_context)}
272+
"""
273+
274+
fogbugz_client = get_fogbugz_rest_client(app)
275+
fogbugz_case_data = FogbugzCaseCreate(
276+
fogbugz_project_id=product_support_assigned_fogbugz_project_id,
277+
title=f"Request for Support on {host}",
278+
description=description,
279+
)
280+
case_id = await fogbugz_client.create_case(fogbugz_case_data)
281+
282+
# Update conversation with FogBugz case URL
283+
await update_conversation(
284+
app,
285+
project_id=None,
286+
conversation_id=conversation.conversation_id,
287+
updates=ConversationPatchDB(
288+
extra_context=conversation.extra_context
289+
| {
290+
"fogbugz_case_url": urljoin(
291+
f"{fogbugz_url}",
292+
f"f/cases/{case_id}",
293+
)
294+
},
295+
),
296+
)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# mypy: disable-error-code=truthy-function
2+
from ._client import FogbugzCaseCreate, FogbugzRestClient, get_fogbugz_rest_client
3+
4+
__all__ = [
5+
"get_fogbugz_rest_client",
6+
"FogbugzCaseCreate",
7+
"FogbugzRestClient",
8+
]

services/web/server/src/simcore_service_webserver/fogbugz/_client.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,9 @@
2121
_JSON_CONTENT_TYPE = "application/json"
2222
_UNKNOWN_ERROR_MESSAGE = "Unknown error occurred"
2323

24-
_FOGBUGZ_TIMEOUT: float = Field(
25-
default=45.0, description="API request timeout in seconds"
26-
)
27-
2824

2925
class FogbugzCaseCreate(BaseModel):
30-
fogbugz_project_id: str = Field(description="Project ID in Fogbugz")
26+
fogbugz_project_id: int = Field(description="Project ID in Fogbugz")
3127
title: str = Field(description="Case title")
3228
description: str = Field(description="Case description/first comment")
3329

@@ -36,7 +32,7 @@ class FogbugzRestClient:
3632
"""REST client for Fogbugz API"""
3733

3834
def __init__(self, api_token: SecretStr, base_url: AnyUrl) -> None:
39-
self._client = httpx.AsyncClient(timeout=_FOGBUGZ_TIMEOUT)
35+
self._client = httpx.AsyncClient()
4036
self._api_token = api_token
4137
self._base_url = base_url
4238

@@ -57,7 +53,7 @@ async def create_case(self, data: FogbugzCaseCreate) -> str:
5753
json_payload = {
5854
"cmd": "new",
5955
"token": self._api_token.get_secret_value(),
60-
"ixProject": data.fogbugz_project_id,
56+
"ixProject": f"{data.fogbugz_project_id}",
6157
"sTitle": data.title,
6258
"sEvent": data.description,
6359
}

services/web/server/tests/unit/with_dbs/04/conversations/test_conversations_messages_rest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,7 @@ async def test_conversation_messages_with_database(
461461
mock_send_email = mocker.patch(
462462
"simcore_service_webserver.email.email_service.send_email_from_template"
463463
)
464-
464+
mocker.patch("simcore_service_webserver.products.products_web.get_current_product")
465465
assert client.app
466466

467467
# Create a conversation directly via API (no mocks)

services/web/server/tests/unit/with_dbs/04/test_fogbugz_client.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
# pylint: disable=too-many-arguments
55
# pylint: disable=too-many-statements
66

7+
import json
78
from collections.abc import Iterator
89

910
import httpx
@@ -94,11 +95,20 @@ async def test_fogubugz_client(
9495
fogbugz_client = get_fogbugz_rest_client(client.app)
9596
assert fogbugz_client
9697

98+
_json = {"first_key": "test", "second_key": "test2"}
99+
_description = f"""
100+
Dear Support Team,
101+
102+
We have received a support request.
103+
104+
Extra content: {json.dumps(_json)}
105+
"""
106+
97107
case_id = await fogbugz_client.create_case(
98108
data=FogbugzCaseCreate(
99-
fogbugz_project_id="45",
109+
fogbugz_project_id=45,
100110
title="Matus Test Automatic Creation of Fogbugz Case",
101-
description="This is a test case",
111+
description=_description,
102112
)
103113
)
104114
assert case_id == _IXBUG_DUMMY

0 commit comments

Comments
 (0)