Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 19 additions & 4 deletions app/api/chat_messages_api.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from fastapi import APIRouter, Depends
from fastapi import APIRouter, Depends, Path

from app.core.enum.sender import SenderEnum
from app.core.response import ResponseMessage
from app.core.status import CommonCode
from app.schemas.chat_message.request_model import ChatMessagesReqeust
from app.schemas.chat_message.response_model import ChatMessagesResponse
from app.schemas.chat_message.response_model import ALLChatMessagesResponseByTab, ChatMessagesResponse
from app.services.chat_message_service import ChatMessageService, chat_message_service

chat_message_service_dependency = Depends(lambda: chat_message_service)
Expand All @@ -25,8 +25,6 @@ async def create_chat_message(
"""
new_messages = await service.create_chat_message(request)

print(ChatMessagesResponse.model_json_schema())

response_data = ChatMessagesResponse(
id=new_messages.id,
chat_tab_id=new_messages.chat_tab_id,
Expand All @@ -37,3 +35,20 @@ async def create_chat_message(
)

return ResponseMessage.success(value=response_data, code=CommonCode.SUCCESS_CREATE_CHAT_MESSAGES)


@router.get(
"/find/{tabId}",
response_model=ResponseMessage[ALLChatMessagesResponseByTab],
summary="특정 탭의 메시지 전체 조회",
)
def get_chat_messages_by_tabId(
tabId: str = Path(..., description="채팅 탭 고유 ID"),
service: ChatMessageService = chat_message_service_dependency,
) -> ResponseMessage[ALLChatMessagesResponseByTab]:
"""tabId를 기준으로 해당 chat_tab의 전체 메시지를 가져옵니다."""

# 탭 정보와 메시지를 함께 조회
response_data = service.get_chat_tab_and_messages_by_id(tabId)

return ResponseMessage.success(value=response_data, code=CommonCode.SUCCESS_GET_CHAT_MESSAGES)
26 changes: 1 addition & 25 deletions app/api/chat_tab_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from app.core.response import ResponseMessage
from app.core.status import CommonCode
from app.schemas.chat_tab.base_model import ChatTabBase
from app.schemas.chat_tab.response_model import ChatMessagesResponse, ChatTabResponse
from app.schemas.chat_tab.response_model import ChatTabResponse
from app.schemas.chat_tab.update_model import ChatTabUpdate
from app.services.chat_tab_service import ChatTabService, chat_tab_service

Expand Down Expand Up @@ -61,30 +61,6 @@ def get_all_chat_tab(
return ResponseMessage.success(value=response_data, code=CommonCode.SUCCESS_GET_CHAT_TAB)


@router.get(
"/find/{tabId}",
response_model=ResponseMessage[ChatMessagesResponse],
summary="특정 탭의 메시지 전체 조회",
)
def get_chat_messages_by_tabId(
tabId: str = Path(..., description="채팅 탭 고유 ID"), service: ChatTabService = chat_tab_service_dependency
) -> ResponseMessage[list[ChatMessagesResponse]]:
"""tabId를 기준으로 해당 chat_tab의 전체 메시지를 가져옵니다."""
chat_tab = service.get_chat_tab_by_tabId(tabId)

chat_messages = service.get_chat_messages_by_tabId(tabId)

response_data = ChatMessagesResponse(
id=chat_tab.id,
name=chat_tab.name,
created_at=chat_tab.created_at,
updated_at=chat_tab.updated_at,
messages=chat_messages,
)

return ResponseMessage.success(value=response_data, code=CommonCode.SUCCESS_GET_CHAT_MESSAGES)


@router.put(
"/modify/{tabId}",
response_model=ResponseMessage[ChatTabResponse],
Expand Down
77 changes: 66 additions & 11 deletions app/repository/chat_message_repository.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import sqlite3

from app.core.enum.sender import SenderEnum
from app.core.exceptions import APIException
from app.core.status import CommonCode
from app.core.utils import get_db_path
from app.schemas.chat_message.db_model import ChatMessageInDB
from app.schemas.chat_message.response_model import ALLChatMessagesResponseByTab, ChatMessagesResponse


class ChatMessageRepository:
def create_chat_message(self, new_id: str, sender: str, chat_tab_id: str, message: str) -> ChatMessageInDB:
"""
새로운 채팅을 데이터베이스에 저장하고, 저장된 객체를 반환합니다.
"""
"""새로운 채팅을 데이터베이스에 저장하고, 저장된 객체를 반환합니다."""

db_path = get_db_path()
conn = None
Expand Down Expand Up @@ -43,7 +45,7 @@ def create_chat_message(self, new_id: str, sender: str, chat_tab_id: str, messag
if conn:
conn.close()

def get_chat_messages_by_tabId(self, id: str) -> list[ChatMessageInDB]:
def get_chat_tab_and_messages_by_id(self, tabId: str) -> ALLChatMessagesResponseByTab:
"""주어진 chat_tab_id에 해당하는 모든 메시지를 가져옵니다."""
db_path = get_db_path()
conn = None
Expand All @@ -52,17 +54,70 @@ def get_chat_messages_by_tabId(self, id: str) -> list[ChatMessageInDB]:
conn.row_factory = sqlite3.Row
cursor = conn.cursor()

# chat_message 테이블에서 chat_tab_id에 해당하는 모든 메시지를 조회합니다.'
# 메시지가 없을 경우, 빈 리스트를 반환합니다.
# 1. 채팅 탭 정보 조회
cursor.execute(
"SELECT * FROM chat_message WHERE chat_tab_id = ? ORDER BY created_at ASC",
(id,),
"SELECT id, name, created_at, updated_at FROM chat_tab WHERE id = ?",
(tabId,),
)
rows = cursor.fetchall()
tab_row = cursor.fetchone()

# 조회된 모든 행을 ChatMessageInDB 객체 리스트로 변환
return [ChatMessageInDB.model_validate(dict(row)) for row in rows]
if not tab_row:
raise APIException(CommonCode.NO_CHAT_TAB_DATA)

# 2. 해당 탭의 메시지들 조회
cursor.execute(
"""
SELECT id, chat_tab_id, sender, message, created_at, updated_at
FROM chat_message
WHERE chat_tab_id = ?
ORDER BY created_at ASC
""",
(tabId,),
)
message_rows = cursor.fetchall()

# 3. 메시지들을 ChatMessagesResponse로 변환
messages = [
ChatMessagesResponse(
id=row["id"],
chat_tab_id=row["chat_tab_id"],
sender=SenderEnum(row["sender"]),
message=row["message"],
created_at=row["created_at"],
updated_at=row["updated_at"],
)
for row in message_rows
]

# 4. ALLChatMessagesResponseByTab 객체 생성
return ALLChatMessagesResponseByTab(
id=tab_row["id"],
name=tab_row["name"],
created_at=tab_row["created_at"],
updated_at=tab_row["updated_at"],
messages=messages,
)
except sqlite3.Error as e:
raise e
finally:
if conn:
conn.close()

def get_chat_tab_by_id(self, tabId: str) -> None:
"""데이터베이스에 저장된 특정 Chat Tab ID를 조회합니다."""
db_path = get_db_path()
conn = None
try:
conn = sqlite3.connect(str(db_path), timeout=10)
conn.row_factory = sqlite3.Row
cursor = conn.cursor()

cursor.execute(
"SELECT id FROM chat_tab WHERE id = ?",
(tabId,),
)

return None
finally:
if conn:
conn.close()
Expand Down
23 changes: 1 addition & 22 deletions app/repository/chat_tab_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ def delete_chat_tab(self, id: str) -> bool:
conn.close()

def get_all_chat_tab(self) -> list[ChatTabInDB]:
"""데이터베이스에 저장된 모든 API Key를 조회합니다."""
"""데이터베이스에 저장된 모든 Chat Tab ID를 조회합니다."""
db_path = get_db_path()
conn = None
try:
Expand All @@ -116,26 +116,5 @@ def get_all_chat_tab(self) -> list[ChatTabInDB]:
if conn:
conn.close()

def get_chat_tab_by_id(self, id: str | None) -> ChatTabInDB | None:
"""ID에 해당하는 채팅 탭 정보를 가져옵니다."""
db_path = get_db_path()
conn = None
try:
conn = sqlite3.connect(str(db_path), timeout=10)
conn.row_factory = sqlite3.Row
cursor = conn.cursor()

cursor.execute("SELECT * FROM chat_tab WHERE id = ?", (id,))
row = cursor.fetchone()

if not row:
return None

return ChatTabInDB.model_validate(dict(row))

finally:
if conn:
conn.close()


chat_tab_repository = ChatTabRepository()
18 changes: 13 additions & 5 deletions app/schemas/chat_message/base_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@
from app.core.status import CommonCode


def validate_chat_tab_id_format(tab_id: str) -> None:
"""채팅 탭 ID 형식 유효성 검사"""
required_prefix = DBSaveIdEnum.chat_tab.value + "-"
if not tab_id.startswith(required_prefix):
raise APIException(CommonCode.INVALID_CHAT_TAB_ID_FORMAT)


class ChatMessagesBase(BaseModel):
id: str = Field(..., description="고유 ID")
created_at: datetime = Field(..., description="생성 시각")
Expand All @@ -16,9 +23,10 @@ class ChatMessagesBase(BaseModel):
class RequestBase(BaseModel):
"""요청 스키마의 기본 모델"""

def validate_chat_tab_id(self) -> None:
"""채팅 탭 ID에 대한 유효성 검증 로직을 수행합니다."""
def validate_chat_tab_id(self, field_value: str) -> None:
validate_chat_tab_id_format(field_value)

required_prefix = DBSaveIdEnum.chat_tab.value + "-"
if not self.chat_tab_id.startswith(required_prefix):
raise APIException(CommonCode.INVALID_CHAT_TAB_ID_FORMAT)
def validate_message(self, field_value: str) -> None:
"""메시지 유효성 검사"""
if not field_value or field_value.strip() == "":
raise APIException(CommonCode.INVALID_ANNOTATION_REQUEST)
3 changes: 2 additions & 1 deletion app/schemas/chat_message/request_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ class ChatMessagesReqeust(RequestBase):
message: str = Field(..., description="메시지 내용")

def validate(self):
self.validate_chat_tab_id()
self.validate_chat_tab_id(self.chat_tab_id)
self.validate_message(self.message)
16 changes: 15 additions & 1 deletion app/schemas/chat_message/response_model.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from pydantic import Field
from datetime import datetime

from pydantic import BaseModel, Field

from app.core.enum.sender import SenderEnum
from app.schemas.chat_message.base_model import ChatMessagesBase
Expand All @@ -11,3 +13,15 @@ class ChatMessagesResponse(ChatMessagesBase):

class Config:
use_enum_values = True


class ALLChatMessagesResponseByTab(BaseModel):
"""채팅 탭의 메타데이터와 전체 메시지 목록을 담는 응답 스키마"""

id: str = Field(..., description="채팅 탭의 고유 ID")
name: str = Field(..., description="채팅 탭의 이름")
created_at: datetime = Field(..., description="생성 시각")
updated_at: datetime = Field(..., description="마지막 수정 시각")
messages: list[ChatMessagesResponse] = Field(
default_factory=list, description="해당 채팅 탭에 속한 모든 메시지 목록"
)
9 changes: 0 additions & 9 deletions app/schemas/chat_tab/base_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

from pydantic import BaseModel, Field

from app.core.enum.db_key_prefix_name import DBSaveIdEnum
from app.core.exceptions import APIException
from app.core.status import CommonCode

Expand Down Expand Up @@ -36,11 +35,3 @@ def validate_chat_tab_name(self) -> None:
# 특정 특수문자를 검사하는 예시
if re.search(r"[;\"'`<>]", self.name):
raise APIException(CommonCode.INVALID_CHAT_TAB_NAME_CONTENT)

def validate_chat_tab_id(self) -> None:
"""채팅 탭 ID에 대한 유효성 검증 로직을 수행합니다."""

# 1. 'CHAT-TAB-' 접두사 검증
required_prefix = DBSaveIdEnum.chat_tab.value + "-"
if not self.id.startswith(required_prefix):
raise APIException(CommonCode.INVALID_CHAT_TAB_ID_FORMAT)
13 changes: 0 additions & 13 deletions app/schemas/chat_tab/db_model.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
from datetime import datetime

from pydantic import Field

from app.schemas.chat_tab.base_model import ChatTabBase


Expand All @@ -12,14 +10,3 @@ class ChatTabInDB(ChatTabBase):
name: str
created_at: datetime
updated_at: datetime


class ChatMessageInDB(ChatTabBase):
"""데이터베이스에 저장된 형태의 메시지 스키마 (내부용)"""

id: str = Field(..., description="메시지의 고유 ID (서버에서 생성)")
chat_tab_id: str = Field(..., description="해당 메시지가 속한 채팅 탭의 ID")
sender: str = Field(..., description="메시지 발신자 ('AI' 또는 'User')")
message: str = Field(..., description="메시지 내용")
created_at: datetime
updated_at: datetime
8 changes: 0 additions & 8 deletions app/schemas/chat_tab/response_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from pydantic import Field

from app.schemas.chat_tab.base_model import ChatTabBase
from app.schemas.chat_tab.db_model import ChatMessageInDB


class ChatTabResponse(ChatTabBase):
Expand All @@ -13,10 +12,3 @@ class ChatTabResponse(ChatTabBase):
name: str = Field(..., description="채팅 탭의 이름")
created_at: datetime
updated_at: datetime


class ChatMessagesResponse(ChatTabResponse):
"""AI 채팅 탭의 메타데이터와 전체 메시지 목록을 담는 API 응답 스키마"""

# 해당 탭의 모든 메시지를 리스트로 담습니다.
messages: list[ChatMessageInDB] = Field(..., description="해당 채팅 탭에 속한 모든 메시지 목록")
Loading