Merge pull request 'add chat title' (#5) from feature/chat-title into master

Reviewed-on: #5
This commit is contained in:
Ghasemi 2026-07-25 08:48:44 -04:00
commit f0f7454797
6 changed files with 62 additions and 3 deletions

View file

@ -0,0 +1,18 @@
# Generated by Django 5.2.13 on 2026-07-25 09:12
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('chat', '0004_conversationreport'),
]
operations = [
migrations.AddField(
model_name='conversation',
name='title',
field=models.CharField(blank=True, max_length=255, null=True),
),
]

View file

@ -27,6 +27,7 @@ class Conversation(models.Model):
choices=ConversationStatus.choices, choices=ConversationStatus.choices,
default=ConversationStatus.OPEN, default=ConversationStatus.OPEN,
) )
title = models.CharField(max_length=255, null=True, blank=True)
# Set only while status is CLOSED; identifies who is allowed to reopen it. # Set only while status is CLOSED; identifies who is allowed to reopen it.
closed_by_uuid = models.UUIDField(null=True, blank=True) closed_by_uuid = models.UUIDField(null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True) created_at = models.DateTimeField(auto_now_add=True)

View file

@ -7,6 +7,9 @@ from apps.chat.models import Conversation, ConversationParticipant
class CreateConversationSerializer(serializers.Serializer): class CreateConversationSerializer(serializers.Serializer):
user_1_uuid = serializers.UUIDField() user_1_uuid = serializers.UUIDField()
user_2_uuid = serializers.UUIDField() user_2_uuid = serializers.UUIDField()
title = serializers.CharField(
max_length=255, required=False, allow_null=True, allow_blank=True
)
class ConversationUserActionSerializer(serializers.Serializer): class ConversationUserActionSerializer(serializers.Serializer):
@ -18,7 +21,15 @@ class ConversationSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Conversation model = Conversation
fields = ["uuid", "type", "status", "closed_by_uuid", "created_at", "participants"] fields = [
"uuid",
"type",
"status",
"title",
"closed_by_uuid",
"created_at",
"participants",
]
@extend_schema_field(serializers.ListField(child=serializers.UUIDField())) @extend_schema_field(serializers.ListField(child=serializers.UUIDField()))
def get_participants(self, obj): def get_participants(self, obj):
@ -33,6 +44,7 @@ class ConversationListSerializer(serializers.Serializer):
uuid = serializers.UUIDField() uuid = serializers.UUIDField()
type = serializers.CharField() type = serializers.CharField()
status = serializers.CharField() status = serializers.CharField()
title = serializers.CharField(allow_null=True)
closed_by_uuid = serializers.UUIDField(allow_null=True) closed_by_uuid = serializers.UUIDField(allow_null=True)
created_at = serializers.DateTimeField() created_at = serializers.DateTimeField()
participants = serializers.ListField(child=serializers.UUIDField()) participants = serializers.ListField(child=serializers.UUIDField())

View file

@ -21,7 +21,9 @@ class ConversationService:
self._account = account_service or AccountService() self._account = account_service or AccountService()
self._mm = mattermost_client or MattermostClient() self._mm = mattermost_client or MattermostClient()
def create(self, user_1_uuid: UUID, user_2_uuid: UUID) -> Conversation: def create(
self, user_1_uuid: UUID, user_2_uuid: UUID, title: str | None = None
) -> Conversation:
if not self._account.validate_user(user_1_uuid): if not self._account.validate_user(user_1_uuid):
raise ValueError(f"User {user_1_uuid} is not valid") raise ValueError(f"User {user_1_uuid} is not valid")
if not self._account.validate_user(user_2_uuid): if not self._account.validate_user(user_2_uuid):
@ -42,7 +44,9 @@ class ConversationService:
channel_id = self._mm.create_private_channel([mm_user_1, mm_user_2]) channel_id = self._mm.create_private_channel([mm_user_1, mm_user_2])
with transaction.atomic(): with transaction.atomic():
conversation = Conversation.objects.create(mattermost_channel_id=channel_id) conversation = Conversation.objects.create(
mattermost_channel_id=channel_id, title=title
)
ConversationParticipant.objects.create( ConversationParticipant.objects.create(
conversation=conversation, user_uuid=user_1_uuid conversation=conversation, user_uuid=user_1_uuid
) )

View file

@ -35,6 +35,28 @@ def test_create_conversation_success():
assert user_2 in participant_uuids assert user_2 in participant_uuids
@pytest.mark.django_db
def test_create_conversation_stores_title():
user_1 = uuid.uuid4()
user_2 = uuid.uuid4()
svc = ConversationService(mattermost_client=_make_mm_client())
conv = svc.create(user_1, user_2, title="Support request")
assert Conversation.objects.get(uuid=conv.uuid).title == "Support request"
@pytest.mark.django_db
def test_create_conversation_without_title_defaults_to_none():
user_1 = uuid.uuid4()
user_2 = uuid.uuid4()
svc = ConversationService(mattermost_client=_make_mm_client())
conv = svc.create(user_1, user_2)
assert Conversation.objects.get(uuid=conv.uuid).title is None
@pytest.mark.django_db @pytest.mark.django_db
def test_create_conversation_creates_mattermost_mapping(): def test_create_conversation_creates_mattermost_mapping():
user_1 = uuid.uuid4() user_1 = uuid.uuid4()

View file

@ -32,6 +32,7 @@ class ConversationCreateView(APIView):
conversation = ConversationService().create( conversation = ConversationService().create(
serializer.validated_data["user_1_uuid"], serializer.validated_data["user_1_uuid"],
serializer.validated_data["user_2_uuid"], serializer.validated_data["user_2_uuid"],
title=serializer.validated_data.get("title"),
) )
return Response( return Response(
@ -86,6 +87,7 @@ class UserConversationListView(APIView):
"uuid": conv.uuid, "uuid": conv.uuid,
"type": conv.type, "type": conv.type,
"status": conv.status, "status": conv.status,
"title": conv.title,
"closed_by_uuid": conv.closed_by_uuid, "closed_by_uuid": conv.closed_by_uuid,
"created_at": conv.created_at, "created_at": conv.created_at,
"participants": participants, "participants": participants,