Skip to content
59 changes: 59 additions & 0 deletions app/api/annotation_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
from fastapi import APIRouter, Depends

from app.core.response import ResponseMessage
from app.core.status import CommonCode
from app.schemas.annotation.request_model import AnnotationCreateRequest
from app.schemas.annotation.response_model import AnnotationDeleteResponse, FullAnnotationResponse
from app.services.annotation_service import AnnotationService, annotation_service

annotation_service_dependency = Depends(lambda: annotation_service)

router = APIRouter()


@router.post(
"/create",
response_model=ResponseMessage[FullAnnotationResponse],
summary="새로운 어노테이션 생성",
)
async def create_annotation(
request: AnnotationCreateRequest,
service: AnnotationService = annotation_service_dependency,
) -> ResponseMessage[FullAnnotationResponse]:
"""
`db_profile_id`를 받아 AI를 통해 DB 스키마를 분석하고 어노테이션을 생성하여 반환합니다.
"""
new_annotation = await service.create_annotation(request)
return ResponseMessage.success(value=new_annotation, code=CommonCode.SUCCESS_CREATE_ANNOTATION)


@router.get(
"/find/{annotation_id}",
response_model=ResponseMessage[FullAnnotationResponse],
summary="특정 어노테이션 상세 정보 조회",
)
def get_annotation(
annotation_id: str,
service: AnnotationService = annotation_service_dependency,
) -> ResponseMessage[FullAnnotationResponse]:
"""
`annotation_id`에 해당하는 어노테이션의 전체 상세 정보를 조회합니다.
"""
annotation = service.get_full_annotation(annotation_id)
return ResponseMessage.success(value=annotation, code=CommonCode.SUCCESS_FIND_ANNOTATION)


@router.delete(
"/remove/{annotation_id}",
response_model=ResponseMessage[AnnotationDeleteResponse],
summary="특정 어노테이션 삭제",
)
def delete_annotation(
annotation_id: str,
service: AnnotationService = annotation_service_dependency,
) -> ResponseMessage[AnnotationDeleteResponse]:
"""
`annotation_id`에 해당하는 어노테이션 및 하위 데이터를 모두 삭제합니다.
"""
result = service.delete_annotation(annotation_id)
return ResponseMessage.success(value=result, code=CommonCode.SUCCESS_DELETE_ANNOTATION)
2 changes: 1 addition & 1 deletion app/api/api_key_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@


@router.post(
"/actions",
"/create",
response_model=ResponseMessage[APIKeyResponse],
summary="API KEY 저장 (처음 한 번)",
description="외부 AI 서비스의 API Key를 암호화하여 로컬 데이터베이스에 저장합니다.",
Expand Down
3 changes: 2 additions & 1 deletion app/api/api_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from fastapi import APIRouter

from app.api import api_key_api, chat_tab_api, driver_api, test_api, user_db_api
from app.api import annotation_api, api_key_api, chat_tab_api, driver_api, test_api, user_db_api

api_router = APIRouter()

Expand All @@ -14,3 +14,4 @@
api_router.include_router(user_db_api.router, prefix="/user/db", tags=["UserDb"])
api_router.include_router(api_key_api.router, prefix="/keys", tags=["API Key"])
api_router.include_router(chat_tab_api.router, prefix="/chats", tags=["AI Chat"])
api_router.include_router(annotation_api.router, prefix="/annotations", tags=["Annotation"])
23 changes: 22 additions & 1 deletion app/api/user_db_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@

from app.core.exceptions import APIException
from app.core.response import ResponseMessage
from app.core.status import CommonCode
from app.schemas.user_db.db_profile_model import DBProfileInfo, UpdateOrCreateDBProfile
from app.schemas.user_db.result_model import ColumnInfo, DBProfile
from app.schemas.user_db.result_model import ColumnInfo, DBProfile, TableInfo
from app.services.user_db_service import UserDbService, user_db_service

user_db_service_dependency = Depends(lambda: user_db_service)
Expand Down Expand Up @@ -89,6 +90,7 @@ def delete_profile(
def find_all_profile(
service: UserDbService = user_db_service_dependency,
) -> ResponseMessage[list[DBProfile]]:

result = service.find_all_profile()

if not result.is_successful:
Expand All @@ -102,6 +104,7 @@ def find_all_profile(
summary="특정 DB의 전체 스키마 조회",
)
def find_schemas(profile_id: str, service: UserDbService = user_db_service_dependency) -> ResponseMessage[list[str]]:

db_info = service.find_profile(profile_id)
result = service.find_schemas(db_info)

Expand All @@ -118,6 +121,7 @@ def find_schemas(profile_id: str, service: UserDbService = user_db_service_depen
def find_tables(
profile_id: str, schema_name: str, service: UserDbService = user_db_service_dependency
) -> ResponseMessage[list[str]]:

db_info = service.find_profile(profile_id)
result = service.find_tables(db_info, schema_name)

Expand All @@ -134,9 +138,26 @@ def find_tables(
def find_columns(
profile_id: str, schema_name: str, table_name: str, service: UserDbService = user_db_service_dependency
) -> ResponseMessage[list[ColumnInfo]]:

db_info = service.find_profile(profile_id)
result = service.find_columns(db_info, schema_name, table_name)

if not result.is_successful:
raise APIException(result.code)
return ResponseMessage.success(value=result.columns, code=result.code)


@router.get(
"/find/all-schemas/{profile_id}",
response_model=ResponseMessage[list[TableInfo]],
summary="특정 DB의 전체 스키마의 상세 정보 조회",
description="테이블, 컬럼, 제약조건, 인덱스를 포함한 모든 스키마 정보를 반환합니다.",
)
def find_all_schema_info(
profile_id: str, service: UserDbService = user_db_service_dependency
) -> ResponseMessage[list[TableInfo]]:

db_info = service.find_profile(profile_id)
full_schema_info = service.get_full_schema_info(db_info)

return ResponseMessage.success(value=full_schema_info, code=CommonCode.SUCCESS)
16 changes: 16 additions & 0 deletions app/core/enum/constraint_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from enum import Enum


class ConstraintTypeEnum(str, Enum):
"""
데이터베이스 제약 조건의 유형을 정의하는 Enum 클래스입니다.
- str을 상속하여 Enum 멤버를 문자열 값처럼 사용할 수 있습니다.
"""

PRIMARY_KEY = "PRIMARY KEY"
FOREIGN_KEY = "FOREIGN KEY"
UNIQUE = "UNIQUE"
CHECK = "CHECK"
NOT_NULL = "NOT NULL" # 일부 DB에서는 제약조건으로 취급
DEFAULT = "DEFAULT" # 일부 DB에서는 제약조건으로 취급
INDEX = "INDEX" # 제약조건은 아니지만, 관련 정보로 포함
11 changes: 10 additions & 1 deletion app/core/enum/db_key_prefix_name.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,13 @@ class DBSaveIdEnum(Enum):
user_db = "USER-DB"
driver = "DRIVER"
api_key = "API-KEY"
chat_tab = "CHAT-TAB"
chat_tab = "CHAT_TAB"

database_annotation = "DB-ANNO"
table_annotation = "TBL-ANNO"
column_annotation = "COL-ANNO"
table_constraint = "TC-ANNO"
constraint_column = "CC-ANNO"
index_annotation = "IDX-ANNO"
index_column = "IC-ANNO"
table_relationship = "TR-ANNO"
18 changes: 18 additions & 0 deletions app/core/status.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ class CommonCode(Enum):
SUCCESS_GET_CHAT_MESSAGES = (status.HTTP_200_OK, "2304", "채팅 탭의 모든 메시지를 성공적으로 불러왔습니다.")

""" ANNOTATION 성공 코드 - 24xx """
SUCCESS_CREATE_ANNOTATION = (status.HTTP_201_CREATED, "2400", "어노테이션을 성공적으로 생성하였습니다.")
SUCCESS_FIND_ANNOTATION = (status.HTTP_200_OK, "2401", "어노테이션 정보를 성공적으로 조회하였습니다.")
SUCCESS_DELETE_ANNOTATION = (status.HTTP_200_OK, "2402", "어노테이션을 성공적으로 삭제하였습니다.")

""" SQL 성공 코드 - 25xx """

Expand Down Expand Up @@ -82,6 +85,7 @@ class CommonCode(Enum):
NO_CHAT_TAB_DATA = (status.HTTP_404_NOT_FOUND, "4304", "해당 ID를 가진 채팅 탭을 찾을 수 없습니다.")

""" ANNOTATION 클라이언트 에러 코드 - 44xx """
INVALID_ANNOTATION_REQUEST = (status.HTTP_400_BAD_REQUEST, "4400", "어노테이션 요청 데이터가 유효하지 않습니다.")

""" SQL 클라이언트 에러 코드 - 45xx """

Expand All @@ -107,6 +111,11 @@ class CommonCode(Enum):
FAIL_FIND_SCHEMAS = (status.HTTP_500_INTERNAL_SERVER_ERROR, "5102", "디비 스키마 정보 조회 중 에러가 발생했습니다.")
FAIL_FIND_TABLES = (status.HTTP_500_INTERNAL_SERVER_ERROR, "5103", "디비 테이블 정보 조회 중 에러가 발생했습니다.")
FAIL_FIND_COLUMNS = (status.HTTP_500_INTERNAL_SERVER_ERROR, "5104", "디비 컬럼 정보 조회 중 에러가 발생했습니다.")
FAIL_FIND_CONSTRAINTS_OR_INDEXES = (
status.HTTP_500_INTERNAL_SERVER_ERROR,
"5105",
"디비 제약조건 또는 인덱스 정보 조회 중 에러가 발생했습니다.",
)
FAIL_SAVE_PROFILE = (status.HTTP_500_INTERNAL_SERVER_ERROR, "5130", "디비 정보 저장 중 에러가 발생했습니다.")
FAIL_UPDATE_PROFILE = (status.HTTP_500_INTERNAL_SERVER_ERROR, "5150", "디비 정보 업데이트 중 에러가 발생했습니다.")
FAIL_DELETE_PROFILE = (status.HTTP_500_INTERNAL_SERVER_ERROR, "5170", "디비 정보 삭제 중 에러가 발생했습니다.")
Expand All @@ -116,6 +125,15 @@ class CommonCode(Enum):
""" AI CHAT, DB 서버 에러 코드 - 53xx """

""" ANNOTATION 서버 에러 코드 - 54xx """
FAIL_CREATE_ANNOTATION = (status.HTTP_500_INTERNAL_SERVER_ERROR, "5400", "어노테이션 생성 중 에러가 발생했습니다.")
FAIL_FIND_ANNOTATION = (status.HTTP_500_INTERNAL_SERVER_ERROR, "5401", "어노테이션 조회 중 에러가 발생했습니다.")
FAIL_DELETE_ANNOTATION = (status.HTTP_500_INTERNAL_SERVER_ERROR, "5402", "어노테이션 삭제 중 에러가 발생했습니다.")
FAIL_AI_SERVER_CONNECTION = (status.HTTP_503_SERVICE_UNAVAILABLE, "5403", "AI 서버 연결에 실패했습니다.")
FAIL_AI_SERVER_PROCESSING = (
status.HTTP_500_INTERNAL_SERVER_ERROR,
"5404",
"AI 서버가 요청을 처리하는 데 실패했습니다.",
)

""" SQL 서버 에러 코드 - 55xx """

Expand Down
Loading