Skip to content

Commit 7ff8647

Browse files
Refactors support conversation handling and group user queries
Unifies conversation query parameter handling and refines support conversation endpoints to remove unnecessary type checks from the query string. Improves recipient notification logic for support conversations by leveraging product support groups and conversation creator identity. Introduces a permission-free method to list all users in a group for internal use, separating caller-checked user listing from internal queries. Simplifies frontend API calls by removing redundant type query parameters. Supports clearer support request notifications by streamlining email templates. Enhances maintainability and future extensibility of conversation and group logic.
1 parent 81315d3 commit 7ff8647

File tree

13 files changed

+206
-97
lines changed

13 files changed

+206
-97
lines changed

api/specs/web-server/_conversations.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
)
3333
from simcore_service_webserver.conversations._controller._conversations_rest import (
3434
_ConversationsCreateBodyParams,
35-
_GetConversationsQueryParams,
3635
_ListConversationsQueryParams,
3736
)
3837

@@ -56,7 +55,6 @@
5655
)
5756
async def create_conversation(
5857
_body: _ConversationsCreateBodyParams,
59-
_query: Annotated[_GetConversationsQueryParams, Depends()],
6058
): ...
6159

6260

@@ -76,7 +74,6 @@ async def list_conversations(
7674
async def update_conversation(
7775
_params: Annotated[ConversationPathParams, Depends()],
7876
_body: ConversationPatch,
79-
_query: Annotated[as_query(_GetConversationsQueryParams), Depends()],
8077
): ...
8178

8279

@@ -86,7 +83,6 @@ async def update_conversation(
8683
)
8784
async def delete_conversation(
8885
_params: Annotated[ConversationPathParams, Depends()],
89-
_query: Annotated[as_query(_GetConversationsQueryParams), Depends()],
9086
): ...
9187

9288

@@ -96,7 +92,6 @@ async def delete_conversation(
9692
)
9793
async def get_conversation(
9894
_params: Annotated[ConversationPathParams, Depends()],
99-
_query: Annotated[as_query(_GetConversationsQueryParams), Depends()],
10095
): ...
10196

10297

services/static-webserver/client/source/class/osparc/data/Resources.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1487,11 +1487,11 @@ qx.Class.define("osparc.data.Resources", {
14871487
},
14881488
getConversation: {
14891489
method: "GET",
1490-
url: statics.API + "/conversations/{conversationId}?type=SUPPORT"
1490+
url: statics.API + "/conversations/{conversationId}"
14911491
},
14921492
renameConversation: {
14931493
method: "PATCH",
1494-
url: statics.API + "/conversations/{conversationId}?type=SUPPORT"
1494+
url: statics.API + "/conversations/{conversationId}"
14951495
},
14961496
deleteConversation: {
14971497
method: "DELETE",

services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml

Lines changed: 5 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -516,12 +516,6 @@ paths:
516516
- conversations
517517
summary: Create Conversation
518518
operationId: create_conversation
519-
parameters:
520-
- name: type
521-
in: query
522-
required: true
523-
schema:
524-
$ref: '#/components/schemas/ConversationType'
525519
requestBody:
526520
required: true
527521
content:
@@ -541,11 +535,6 @@ paths:
541535
summary: List Conversations
542536
operationId: list_conversations
543537
parameters:
544-
- name: type
545-
in: query
546-
required: true
547-
schema:
548-
$ref: '#/components/schemas/ConversationType'
549538
- name: limit
550539
in: query
551540
required: false
@@ -563,6 +552,11 @@ paths:
563552
minimum: 0
564553
default: 0
565554
title: Offset
555+
- name: type
556+
in: query
557+
required: true
558+
schema:
559+
$ref: '#/components/schemas/ConversationType'
566560
responses:
567561
'200':
568562
description: Successful Response
@@ -584,11 +578,6 @@ paths:
584578
type: string
585579
format: uuid
586580
title: Conversation Id
587-
- name: type
588-
in: query
589-
required: true
590-
schema:
591-
$ref: '#/components/schemas/ConversationType'
592581
requestBody:
593582
required: true
594583
content:
@@ -615,11 +604,6 @@ paths:
615604
type: string
616605
format: uuid
617606
title: Conversation Id
618-
- name: type
619-
in: query
620-
required: true
621-
schema:
622-
$ref: '#/components/schemas/ConversationType'
623607
responses:
624608
'204':
625609
description: Successful Response
@@ -636,11 +620,6 @@ paths:
636620
type: string
637621
format: uuid
638622
title: Conversation Id
639-
- name: type
640-
in: query
641-
required: true
642-
schema:
643-
$ref: '#/components/schemas/ConversationType'
644623
responses:
645624
'200':
646625
description: Successful Response

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

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ async def create_conversation_message(request: web.Request):
9797
message, is_first_message = (
9898
await _conversation_message_service.create_support_message_with_first_check(
9999
app=request.app,
100+
product_name=req_ctx.product_name,
100101
user_id=req_ctx.user_id,
101102
project_id=None, # Support conversations don't use project_id
102103
conversation_id=path_params.conversation_id,
@@ -128,11 +129,6 @@ async def create_conversation_message(request: web.Request):
128129
template=email_template_path,
129130
context={
130131
"host": request.host,
131-
"product": product.model_dump(
132-
include={
133-
"display_name",
134-
}
135-
),
136132
"first_name": user["first_name"],
137133
"last_name": user["last_name"],
138134
"user_email": user["email"],
@@ -275,6 +271,7 @@ async def update_conversation_message(request: web.Request):
275271

276272
message = await _conversation_message_service.update_message(
277273
app=request.app,
274+
product_name=req_ctx.product_name,
278275
project_id=None, # Support conversations don't use project_id
279276
conversation_id=path_params.conversation_id,
280277
message_id=path_params.message_id,
@@ -314,6 +311,7 @@ async def delete_conversation_message(request: web.Request):
314311

315312
await _conversation_message_service.delete_message(
316313
app=request.app,
314+
product_name=req_ctx.product_name,
317315
user_id=req_ctx.user_id,
318316
project_id=None, # Support conversations don't use project_id
319317
conversation_id=path_params.conversation_id,

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

Lines changed: 43 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
PageQueryParameters,
1717
)
1818
from models_library.rest_pagination_utils import paginate_data
19-
from pydantic import BaseModel, ConfigDict, field_validator
19+
from pydantic import ConfigDict, field_validator
2020
from servicelib.aiohttp import status
2121
from servicelib.aiohttp.requests_validation import (
2222
parse_request_body_as,
@@ -25,16 +25,13 @@
2525
)
2626
from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON
2727
from servicelib.rest_constants import RESPONSE_MODEL_POLICY
28+
from simcore_service_webserver.users import users_service
2829

2930
from ..._meta import API_VTAG as VTAG
3031
from ...login.decorators import login_required
3132
from ...models import AuthenticatedRequestContext
3233
from ...utils_aiohttp import envelope_json_response
33-
from .. import conversations_service
34-
from .._conversation_service import (
35-
get_support_conversation_for_user,
36-
list_support_conversations_for_user,
37-
)
34+
from .. import _conversation_service, conversations_service
3835
from ._common import ConversationPathParams, raise_unsupported_type
3936
from ._rest_exceptions import _handle_exceptions
4037

@@ -43,7 +40,7 @@
4340
routes = web.RouteTableDef()
4441

4542

46-
class _GetConversationsQueryParams(BaseModel):
43+
class _ListConversationsQueryParams(PageQueryParameters):
4744
type: ConversationType
4845
model_config = ConfigDict(extra="forbid")
4946

@@ -56,11 +53,6 @@ def validate_type(cls, value):
5653
return value
5754

5855

59-
class _ListConversationsQueryParams(PageQueryParameters, _GetConversationsQueryParams):
60-
61-
model_config = ConfigDict(extra="forbid")
62-
63-
6456
class _ConversationsCreateBodyParams(InputSchema):
6557
name: str
6658
type: ConversationType
@@ -109,14 +101,17 @@ async def list_conversations(request: web.Request):
109101
query_params = parse_request_query_parameters_as(
110102
_ListConversationsQueryParams, request
111103
)
112-
assert query_params.type == ConversationType.SUPPORT # nosec
113-
114-
total, conversations = await list_support_conversations_for_user(
115-
app=request.app,
116-
user_id=req_ctx.user_id,
117-
product_name=req_ctx.product_name,
118-
offset=query_params.offset,
119-
limit=query_params.limit,
104+
if query_params.type != ConversationType.SUPPORT:
105+
raise_unsupported_type(query_params.type)
106+
107+
total, conversations = (
108+
await _conversation_service.list_support_conversations_for_user(
109+
app=request.app,
110+
user_id=req_ctx.user_id,
111+
product_name=req_ctx.product_name,
112+
offset=query_params.offset,
113+
limit=query_params.limit,
114+
)
120115
)
121116

122117
page = Page[ConversationRestGet].model_validate(
@@ -147,12 +142,14 @@ async def get_conversation(request: web.Request):
147142
"""Get a specific conversation"""
148143
req_ctx = AuthenticatedRequestContext.model_validate(request)
149144
path_params = parse_request_path_parameters_as(ConversationPathParams, request)
150-
query_params = parse_request_query_parameters_as(
151-
_GetConversationsQueryParams, request
145+
146+
conversation = await _conversation_service.get_conversation(
147+
request.app, conversation_id=path_params.conversation_id
152148
)
153-
assert query_params.type == ConversationType.SUPPORT # nosec
149+
if conversation.type != ConversationType.SUPPORT:
150+
raise_unsupported_type(conversation.type)
154151

155-
conversation = await get_support_conversation_for_user(
152+
conversation = await _conversation_service.get_support_conversation_for_user(
156153
app=request.app,
157154
user_id=req_ctx.user_id,
158155
product_name=req_ctx.product_name,
@@ -174,16 +171,21 @@ async def update_conversation(request: web.Request):
174171
req_ctx = AuthenticatedRequestContext.model_validate(request)
175172
path_params = parse_request_path_parameters_as(ConversationPathParams, request)
176173
body_params = await parse_request_body_as(ConversationPatch, request)
177-
query_params = parse_request_query_parameters_as(
178-
_GetConversationsQueryParams, request
174+
175+
conversation = await _conversation_service.get_conversation(
176+
request.app, conversation_id=path_params.conversation_id
179177
)
180-
assert query_params.type == ConversationType.SUPPORT # nosec
178+
if conversation.type != ConversationType.SUPPORT:
179+
raise_unsupported_type(conversation.type)
181180

182-
await get_support_conversation_for_user(
181+
# Only support conversation creator can update conversation
182+
_user_group_id = await users_service.get_user_primary_group_id(
183+
request.app, user_id=req_ctx.user_id
184+
)
185+
await _conversation_service.get_conversation_for_user(
183186
app=request.app,
184-
user_id=req_ctx.user_id,
185-
product_name=req_ctx.product_name,
186187
conversation_id=path_params.conversation_id,
188+
user_group_id=_user_group_id,
187189
)
188190

189191
conversation = await conversations_service.update_conversation(
@@ -207,16 +209,21 @@ async def delete_conversation(request: web.Request):
207209
"""Delete a conversation"""
208210
req_ctx = AuthenticatedRequestContext.model_validate(request)
209211
path_params = parse_request_path_parameters_as(ConversationPathParams, request)
210-
query_params = parse_request_query_parameters_as(
211-
_GetConversationsQueryParams, request
212+
213+
conversation = await _conversation_service.get_conversation(
214+
request.app, conversation_id=path_params.conversation_id
212215
)
213-
assert query_params.type == ConversationType.SUPPORT # nosec
216+
if conversation.type != ConversationType.SUPPORT:
217+
raise_unsupported_type(conversation.type)
214218

215-
await get_support_conversation_for_user(
219+
# Only support conversation creator can delete conversation
220+
_user_group_id = await users_service.get_user_primary_group_id(
221+
request.app, user_id=req_ctx.user_id
222+
)
223+
await _conversation_service.get_conversation_for_user(
216224
app=request.app,
217-
user_id=req_ctx.user_id,
218-
product_name=req_ctx.product_name,
219225
conversation_id=path_params.conversation_id,
226+
user_group_id=_user_group_id,
220227
)
221228

222229
await conversations_service.delete_conversation(

0 commit comments

Comments
 (0)