Skip to content

Commit df44672

Browse files
authored
Merge branch 'master' into fix_duplicate_map_inputs
2 parents 849e0e4 + 7aa3ac8 commit df44672

File tree

23 files changed

+843
-465
lines changed

23 files changed

+843
-465
lines changed

packages/models-library/src/models_library/api_schemas_webserver/conversations.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,13 @@ class ConversationRestGet(OutputSchema):
2626
project_uuid: ProjectID | None
2727
user_group_id: GroupID
2828
type: ConversationType
29+
fogbugz_case_id: str | None
2930
created: datetime
3031
modified: datetime
3132
extra_context: dict[str, str]
33+
is_read_by_user: bool
34+
is_read_by_support: bool
35+
last_message_created_at: datetime
3236

3337
@classmethod
3438
def from_domain_model(cls, domain: ConversationGetDB) -> Self:
@@ -39,15 +43,21 @@ def from_domain_model(cls, domain: ConversationGetDB) -> Self:
3943
project_uuid=domain.project_uuid,
4044
user_group_id=domain.user_group_id,
4145
type=domain.type,
46+
fogbugz_case_id=domain.fogbugz_case_id,
4247
created=domain.created,
4348
modified=domain.modified,
4449
extra_context=domain.extra_context,
50+
is_read_by_user=domain.is_read_by_user,
51+
is_read_by_support=domain.is_read_by_support,
52+
last_message_created_at=domain.last_message_created_at,
4553
)
4654

4755

4856
class ConversationPatch(InputSchema):
4957
name: str | None = None
5058
extra_context: dict[str, Any] | None = None
59+
is_read_by_user: bool | None = None
60+
is_read_by_support: bool | None = None
5161

5262

5363
### CONVERSATION MESSAGES ---------------------------------------------------------------

packages/models-library/src/models_library/batch_operations.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,28 @@
1+
"""
2+
3+
# Batch Operations Rationale:
4+
5+
Please preserve the following behaviors when implementing batch operations:
6+
7+
| Case | Behavior | Justification |
8+
| -------------- | ------------------------------------------ | --------------------------- |
9+
| Empty `names` | `400 Bad Request` | Invalid input |
10+
| Some missing | `200 OK`, with `missing` field | Partial success |
11+
| Duplicates | Silently deduplicate | Idempotent, client-friendly |
12+
| Response order | Preserve request order (excluding missing) | Deterministic, ergonomic |
13+
14+
15+
- `BatchGet` is semantically distinct from `List`.
16+
- `List` means “give me everything you have, maybe filtered.”
17+
- `BatchGet` means “give me these specific known resources.”
18+
- Passing an empty list means you’re not actually identifying anything to fetch — so it’s a client error (bad request), not a legitimate “empty result.”
19+
- This aligns with the principle: If the request parameters are syntactically valid but semantically meaningless, return 400 Bad Request.
20+
21+
# References:
22+
- https://google.aip.dev/130
23+
- https://google.aip.dev/231
24+
"""
25+
126
from typing import Annotated, Generic, TypeVar
227

328
from common_library.basic_types import DEFAULT_FACTORY

packages/models-library/src/models_library/conversations.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ class ConversationMessageType(StrAutoEnum):
3838
#
3939

4040

41+
IsSupportUser: TypeAlias = bool
42+
43+
4144
class ConversationGetDB(BaseModel):
4245
conversation_id: ConversationID
4346
product_name: ProductName
@@ -46,10 +49,14 @@ class ConversationGetDB(BaseModel):
4649
user_group_id: GroupID
4750
type: ConversationType
4851
extra_context: dict[str, Any]
52+
fogbugz_case_id: str | None
53+
is_read_by_user: bool
54+
is_read_by_support: bool
4955

5056
# states
5157
created: datetime
5258
modified: datetime
59+
last_message_created_at: datetime
5360

5461
model_config = ConfigDict(from_attributes=True)
5562

@@ -71,6 +78,10 @@ class ConversationMessageGetDB(BaseModel):
7178
class ConversationPatchDB(BaseModel):
7279
name: ConversationName | None = None
7380
extra_context: dict[str, Any] | None = None
81+
fogbugz_case_id: str | None = None
82+
is_read_by_user: bool | None = None
83+
is_read_by_support: bool | None = None
84+
last_message_created_at: datetime | None = None
7485

7586

7687
class ConversationMessagePatchDB(BaseModel):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""add last_message_created_at column
2+
3+
Revision ID: a6289977e057
4+
Revises: dfdd4f8d4870
5+
Create Date: 2025-10-13 08:39:24.912539+00:00
6+
7+
"""
8+
9+
import sqlalchemy as sa
10+
from alembic import op
11+
12+
# revision identifiers, used by Alembic.
13+
revision = "a6289977e057"
14+
down_revision = "dfdd4f8d4870"
15+
branch_labels = None
16+
depends_on = None
17+
18+
19+
def upgrade():
20+
# ### commands auto generated by Alembic - please adjust! ###
21+
op.add_column(
22+
"conversations",
23+
sa.Column(
24+
"last_message_created_at",
25+
sa.DateTime(timezone=True),
26+
server_default=sa.text("now()"),
27+
nullable=False,
28+
),
29+
)
30+
# ### end Alembic commands ###
31+
32+
# Data migration: populate last_message_created_at with modified column values
33+
op.execute("UPDATE conversations SET last_message_created_at = modified")
34+
35+
36+
def downgrade():
37+
# ### commands auto generated by Alembic - please adjust! ###
38+
op.drop_column("conversations", "last_message_created_at")
39+
# ### end Alembic commands ###
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
"""add read by user/support columns
2+
3+
Revision ID: dfdd4f8d4870
4+
Revises: 9dddb16914a4
5+
Create Date: 2025-10-10 12:07:12.014847+00:00
6+
7+
"""
8+
9+
import sqlalchemy as sa
10+
from alembic import op
11+
12+
# revision identifiers, used by Alembic.
13+
revision = "dfdd4f8d4870"
14+
down_revision = "9dddb16914a4"
15+
branch_labels = None
16+
depends_on = None
17+
18+
19+
def upgrade():
20+
# ### commands auto generated by Alembic - please adjust! ###
21+
op.add_column(
22+
"conversations", sa.Column("fogbugz_case_id", sa.String(), nullable=True)
23+
)
24+
op.add_column(
25+
"conversations",
26+
sa.Column(
27+
"is_read_by_user",
28+
sa.Boolean(),
29+
server_default=sa.text("true"),
30+
nullable=False,
31+
),
32+
)
33+
op.add_column(
34+
"conversations",
35+
sa.Column(
36+
"is_read_by_support",
37+
sa.Boolean(),
38+
server_default=sa.text("true"),
39+
nullable=False,
40+
),
41+
)
42+
# ### end Alembic commands ###
43+
44+
45+
def downgrade():
46+
# ### commands auto generated by Alembic - please adjust! ###
47+
op.drop_column("conversations", "is_read_by_support")
48+
op.drop_column("conversations", "is_read_by_user")
49+
op.drop_column("conversations", "fogbugz_case_id")
50+
# ### end Alembic commands ###

packages/postgres-database/src/simcore_postgres_database/models/conversations.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,33 @@ class ConversationType(enum.Enum):
7878
server_default=sa.text("'{}'::jsonb"),
7979
doc="Free JSON to store extra context",
8080
),
81+
sa.Column(
82+
"fogbugz_case_id",
83+
sa.String,
84+
nullable=True,
85+
doc="Fogbugz case ID associated with the conversation",
86+
),
87+
sa.Column(
88+
"is_read_by_user",
89+
sa.Boolean,
90+
nullable=False,
91+
server_default=sa.text("true"),
92+
doc="Indicates if the message has been read by the user (true) or not (false)",
93+
),
94+
sa.Column(
95+
"is_read_by_support",
96+
sa.Boolean,
97+
nullable=False,
98+
server_default=sa.text("true"),
99+
doc="Indicates if the message has been read by the support user (true) or not (false)",
100+
),
101+
sa.Column(
102+
"last_message_created_at",
103+
sa.DateTime(timezone=True),
104+
nullable=False,
105+
server_default=sa.sql.func.now(),
106+
doc="Timestamp of the last message created in this conversation",
107+
),
81108
column_created_datetime(timezone=True),
82109
column_modified_datetime(timezone=True),
83110
)

services/catalog/src/simcore_service_catalog/service/catalog_services.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -703,6 +703,7 @@ async def batch_get_user_services(
703703
704704
Raises:
705705
CatalogItemNotFoundError: When no services are found at all
706+
ValidationError: if the ids are empty
706707
"""
707708
unique_service_identifiers = _BatchIdsValidator.validate_python(ids)
708709

0 commit comments

Comments
 (0)