-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Feat: swagger json 배포 * Docs: FAQ swagger 파일 수정 * Docs: response schema 수정 * Docs: response 모델 수정 * Chore: setup-dev 실행 시 pre-commit을 install 하도록 커맨드 구성 * Docs: swagger 모듈 분리 * Chore: 중복 command 제거
- Loading branch information
Showing
16 changed files
with
437 additions
and
131 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from django.apps import AppConfig | ||
|
||
|
||
class CoreConfig(AppConfig): | ||
name = "core" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
from rest_framework import status | ||
from rest_framework.exceptions import APIException | ||
|
||
|
||
class BaseException(APIException): | ||
status_code = status.HTTP_200_OK | ||
default_detail = "Internal Server Error" | ||
default_code = "ERROR" | ||
|
||
|
||
class NotExistException(BaseException): | ||
status_code = status.HTTP_200_OK | ||
default_detail = "Not Exist." | ||
default_code = "Not Exist" |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
from django.core.exceptions import PermissionDenied as DjangoPermissionDenied | ||
from django.http import Http404 | ||
from rest_framework.exceptions import APIException, NotFound, PermissionDenied | ||
from rest_framework.views import set_rollback | ||
|
||
from core.responses.base import BaseResponse | ||
|
||
|
||
def custom_exception_handler(exc, context): | ||
if isinstance(exc, Http404): | ||
exc = NotFound(*(exc.args)) | ||
elif isinstance(exc, DjangoPermissionDenied): | ||
exc = PermissionDenied(*(exc.args)) | ||
|
||
if isinstance(exc, APIException): | ||
headers = {} | ||
if getattr(exc, "auth_header", None): | ||
headers["WWW-Authenticate"] = exc.auth_header | ||
if getattr(exc, "wait", None): | ||
headers["Retry-After"] = "%d" % exc.wait | ||
|
||
error = dict(code=exc.default_code, message=exc.detail) | ||
|
||
set_rollback() | ||
return BaseResponse(error=error, headers=headers, status="ERROR") | ||
|
||
return None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
from rest_framework.pagination import PageNumberPagination as DRFPageNumberPagination | ||
|
||
from .responses.base import BaseResponse | ||
|
||
|
||
class PageNumberPagination(DRFPageNumberPagination): | ||
page_size = 10 | ||
page_size_query_param = "page_size" | ||
max_page_size = 100 # page 단위의 요청 최대 size | ||
|
||
def get_paginated_response(self, data): | ||
pagination = { | ||
"count": self.page.paginator.count, | ||
"next": self.get_next_link(), | ||
"previous": self.get_previous_link(), | ||
} | ||
return BaseResponse(data=data, pagination=pagination) | ||
|
||
def get_paginated_response_schema(self, schema: dict) -> dict: | ||
return schema |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
from typing import Literal | ||
|
||
from rest_framework.response import Response | ||
from rest_framework.serializers import Serializer | ||
from rest_framework.status import HTTP_200_OK | ||
|
||
from core.responses.serializer import SuccessResponseSerializer | ||
|
||
|
||
class BaseResponse(Response): | ||
def __init__( | ||
self, | ||
data: dict | list | None = None, | ||
error: dict | None = None, | ||
pagination: dict | None = None, | ||
template_name: str | None = None, | ||
headers: dict | None = None, | ||
exception: bool = False, | ||
content_type: str | None = None, | ||
status: Literal["SUCCESS", "ERROR"] = "SUCCESS", | ||
): | ||
response_format = { | ||
"status": status, | ||
"error": error, | ||
"data": data, | ||
"pagination": pagination, | ||
} | ||
super().__init__( | ||
response_format, HTTP_200_OK, template_name, headers, exception, content_type | ||
) | ||
|
||
def __class_getitem__(cls, *args, **kwargs): | ||
return cls | ||
|
||
def get_response_schema(self) -> Serializer: | ||
return SuccessResponseSerializer() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
from rest_framework import serializers | ||
|
||
|
||
class ErrorSerializer(serializers.Serializer): | ||
code = serializers.CharField() | ||
message = serializers.CharField() | ||
|
||
def to_representation(self, instance): | ||
return { | ||
"code": instance.code, | ||
"message": instance.message, | ||
} | ||
|
||
|
||
class PageNumberPaginationSerializer(serializers.Serializer): | ||
count = serializers.IntegerField() | ||
next = serializers.CharField() | ||
previous = serializers.CharField() | ||
|
||
def to_representation(self, instance): | ||
return { | ||
"count": instance.count, | ||
"next": instance.next, | ||
"previous": instance.previous, | ||
} | ||
|
||
|
||
class SuccessResponseSerializer(serializers.Serializer): | ||
status = serializers.CharField(default="SUCCESS") | ||
data = serializers.DictField(required=False) | ||
pagination = PageNumberPaginationSerializer(required=False, allow_null=True) | ||
|
||
def to_representation(self, instance): | ||
return { | ||
"status": instance.status, | ||
"data": instance.data, | ||
"error": None, | ||
"pagination": None, | ||
} | ||
|
||
|
||
class ListSuccessResponseSerializer(serializers.Serializer): | ||
status = serializers.CharField(default="SUCCESS") | ||
data = serializers.ListField(required=False, child=serializers.DictField()) | ||
pagination = PageNumberPaginationSerializer(required=False, allow_null=True) | ||
|
||
def to_representation(self, instance): | ||
return { | ||
"status": instance.status, | ||
"data": instance.data, | ||
"error": None, | ||
"pagination": instance.pagination, | ||
} | ||
|
||
|
||
class ErrorResponseSerializer(serializers.Serializer): | ||
status = serializers.CharField(default="ERROR") | ||
error = ErrorSerializer() | ||
|
||
def to_representation(self, instance): | ||
return { | ||
"status": instance.status, | ||
"error": instance.error, | ||
"data": None, | ||
"pagination": None, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
from typing import Callable | ||
|
||
from drf_spectacular.utils import extend_schema | ||
|
||
|
||
class SwaggerSchema: | ||
@classmethod | ||
def generate_schema(cls, *args, **kwargs) -> Callable: | ||
return extend_schema(*args, **kwargs) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# Create your tests here. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
from drf_spectacular.utils import OpenApiExample, OpenApiResponse | ||
|
||
from core.responses.serializer import ( | ||
ErrorResponseSerializer, | ||
ListSuccessResponseSerializer, | ||
SuccessResponseSerializer, | ||
) | ||
from core.swagger import SwaggerSchema | ||
|
||
|
||
class FAQAPIDocs(SwaggerSchema): | ||
@classmethod | ||
def retrieve(cls): | ||
responses = { | ||
"성공": OpenApiResponse( | ||
response=SuccessResponseSerializer, | ||
description="단일 응답 성공", | ||
examples=[ | ||
OpenApiExample( | ||
name="FAQ 조회", | ||
value={ | ||
"status": "SUCCESS", | ||
"data": { | ||
"id": 1, | ||
"question": "질문", | ||
"answer": "답변", | ||
"created_at": "2021-01-01", | ||
}, | ||
}, | ||
), | ||
], | ||
), | ||
"에러": OpenApiResponse( | ||
response=ErrorResponseSerializer, | ||
description="응답 에러", | ||
examples=[ | ||
OpenApiExample( | ||
name="데이터 없음", | ||
value={ | ||
"status": "ERROR", | ||
"error": {"code": "NOT_EXIST", "message": "데이터가 없습니다."}, | ||
}, | ||
), | ||
], | ||
), | ||
} | ||
return cls.generate_schema( | ||
operation_id="faq_retrieve", description="FAQ 조회", responses=responses | ||
) | ||
|
||
@classmethod | ||
def list(cls): | ||
responses = { | ||
"성공": OpenApiResponse( | ||
response=ListSuccessResponseSerializer, | ||
description="다중 응답 성공", | ||
examples=[ | ||
OpenApiExample( | ||
name="FAQ 목록 조회 (페이지네이션 있음)", | ||
value={ | ||
"status": "SUCCESS", | ||
"data": [ | ||
{ | ||
"id": 1, | ||
"question": "질문1", | ||
"answer": "답변1", | ||
}, | ||
], | ||
"pagination": { | ||
"count": 20, | ||
"next": "http://localhost:8000/api/v1/faqs/?page=2", | ||
"previous": "http://localhost:8000/api/v1/faqs/?page=1", | ||
}, | ||
}, | ||
), | ||
OpenApiExample( | ||
name="FAQ 목록 조회 (데이터 있음)", | ||
value={ | ||
"status": "SUCCESS", | ||
"data": [ | ||
{ | ||
"id": 1, | ||
"question": "질문1", | ||
"answer": "답변1", | ||
}, | ||
], | ||
"pagination": {"count": 1, "next": None, "previous": None}, | ||
}, | ||
), | ||
OpenApiExample( | ||
name="FAQ 목록 조회 (데이터 없음)", | ||
value={ | ||
"status": "SUCCESS", | ||
"data": [], | ||
"pagination": {"count": 0, "next": None, "previous": None}, | ||
}, | ||
), | ||
], | ||
), | ||
"에러": OpenApiResponse(response=ErrorResponseSerializer, description="응답 에러"), | ||
} | ||
return cls.generate_schema( | ||
operation_id="faq_list", description="모든 FAQ 목록 조회", responses=responses | ||
) |
Oops, something went wrong.