Skip to content

Commit 56a2c79

Browse files
Adds FogBugz case creation for support requests
Integrates automatic case creation in FogBugz when a user submits the first message in a support conversation, if configured. Falls back to email-based support request if FogBugz is not set up, and logs actions for clarity. Updates data types and test coverage to reflect new workflow.
1 parent 8768457 commit 56a2c79

File tree

4 files changed

+86
-12
lines changed

4 files changed

+86
-12
lines changed

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

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import functools
2+
import json
23
import logging
34
from typing import Any
45

@@ -22,20 +23,24 @@
2223
from models_library.utils.fastapi_encoders import jsonable_encoder
2324
from pydantic import BaseModel, ConfigDict
2425
from servicelib.aiohttp import status
26+
from servicelib.aiohttp.application_keys import APP_SETTINGS_KEY
2527
from servicelib.aiohttp.requests_validation import (
2628
parse_request_body_as,
2729
parse_request_path_parameters_as,
2830
parse_request_query_parameters_as,
2931
)
3032
from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON
3133
from servicelib.rest_constants import RESPONSE_MODEL_POLICY
32-
from simcore_service_webserver.users import users_service
3334

3435
from ..._meta import API_VTAG as VTAG
3536
from ...email import email_service
37+
from ...fogbugz import get_fogbugz_rest_client
38+
from ...fogbugz._client import FogbugzCaseCreate
39+
from ...fogbugz.settings import FogbugzSettings
3640
from ...login.decorators import login_required
3741
from ...models import AuthenticatedRequestContext
3842
from ...products import products_web
43+
from ...users import users_service
3944
from ...utils_aiohttp import envelope_json_response
4045
from .. import _conversation_message_service, _conversation_service
4146
from ._common import ConversationPathParams, raise_unsupported_type
@@ -107,10 +112,60 @@ async def create_conversation_message(request: web.Request):
107112
)
108113

109114
# NOTE: This is done here in the Controller layer, as the interface around email currently needs request
110-
if is_first_message:
115+
product = products_web.get_current_product(request)
116+
fogbugz_settings_or_none: FogbugzSettings | None = request.app[
117+
APP_SETTINGS_KEY
118+
].WEBSERVER_FOGBUGZ
119+
if (
120+
product.support_standard_group_id
121+
and fogbugz_settings_or_none is not None
122+
and is_first_message
123+
):
124+
_logger.debug(
125+
"Support settings available and FogBugz client configured, creating FogBugz case."
126+
)
127+
assert product.support_assigned_fogbugz_project_id # nosec
128+
129+
try:
130+
user = await users_service.get_user(request.app, req_ctx.user_id)
131+
_url = request.url
132+
_conversation_url = f"{_url.scheme}://{_url.host}/#/conversation/{path_params.conversation_id}"
133+
134+
_description = f"""
135+
Dear Support Team,
136+
137+
We have received a support request from {user["first_name"]} {user["last_name"]} ({user["email"]}) on {request.host}.
138+
139+
All communication should take place in the Platform Support Center at the following link: {_conversation_url}
140+
141+
First message content: {message.content}
142+
143+
Extra content: {json.dumps(_conversation.extra_context)}
144+
"""
145+
146+
_fogbugz_client = get_fogbugz_rest_client(request.app)
147+
_fogbugz_case_data = FogbugzCaseCreate(
148+
fogbugz_project_id=product.support_assigned_fogbugz_project_id,
149+
title=f"Request for Support on {request.host}",
150+
description=_description,
151+
)
152+
await _fogbugz_client.create_case(_fogbugz_case_data)
153+
except Exception: # pylint: disable=broad-except
154+
_logger.exception(
155+
"Failed to create support request FogBugz case for conversation %s.",
156+
_conversation.conversation_id,
157+
)
158+
159+
elif (
160+
product.support_standard_group_id
161+
and fogbugz_settings_or_none is None
162+
and is_first_message
163+
):
164+
_logger.debug(
165+
"Support settings available, but no FogBugz client configured, sending email instead to create FogBugz case."
166+
)
111167
try:
112168
user = await users_service.get_user(request.app, req_ctx.user_id)
113-
product = products_web.get_current_product(request)
114169
template_name = "request_support.jinja2"
115170
destination_email = product.support_email
116171
email_template_path = await products_web.get_product_template_path(
@@ -141,6 +196,8 @@ async def create_conversation_message(request: web.Request):
141196
template_name,
142197
destination_email,
143198
)
199+
else:
200+
_logger.debug("No support settings available, skipping FogBugz case creation.")
144201

145202
data = ConversationMessageRestGet.from_domain_model(message)
146203
return envelope_json_response(data, web.HTTPCreated)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from ._client import FogbugzCaseCreate, FogbugzRestClient, get_fogbugz_rest_client
2+
3+
assert get_fogbugz_rest_client # nosec
4+
assert FogbugzCaseCreate # nosec
5+
assert FogbugzRestClient # nosec
6+
7+
__all__ = [
8+
"get_fogbugz_rest_client",
9+
"FogbugzCaseCreate",
10+
"FogbugzRestClient",
11+
]

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/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)