Skip to content

jogakdal/standard-api-response

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

13 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

API ν‘œμ€€ 응닡 라이브러리

이 λΌμ΄λΈŒλŸ¬λ¦¬λŠ” API 응닡 ν‘œμ€€ν™”λ₯Ό μœ„ν•œ ν΄λž˜μŠ€μ™€ λ©”μ„œλ“œλ₯Ό μ œκ³΅ν•©λ‹ˆλ‹€.

κΈ°λŠ₯

  • 응닡 데이터λ₯Ό ν‘œμ€€ν™”λœ response 포맷으둜 생성 (데이터 제곡 μΈ‘λ©΄)
    • λ¦¬μŠ€νŠΈκ°€ μ—†λŠ” 응닡 데이터 ν‘œμ€€ν™” ꡬ성
    • νŽ˜μ΄μ§€λ„€μ΄μ…˜ ν˜•νƒœ 리슀트 ꡬ성
    • 더보기 ν˜•νƒœ 리슀트 ꡬ성
  • 응닡 λ°μ΄ν„°λ‘œ ν‘œμ€€ν™”λœ λ§€ν•‘ 객체 생성 (데이터 μ†ŒλΉ„ μΈ‘λ©΄)
    • ν‘œμ€€ν™”λœ response 포맷을 λ°μ΄ν„°λ‘œ λ§€ν•‘
    • ν‘œμ€€ν™”λœ response 포맷을 λ°μ΄ν„°λ‘œ λ§€ν•‘ (νŽ˜μ΄μ§€λ„€μ΄μ…˜ 리슀트)
    • ν‘œμ€€ν™”λœ response 포맷을 λ°μ΄ν„°λ‘œ λ§€ν•‘ (더보기 ν˜•νƒœ 리슀트)

ν‘œμ€€ API μŠ€νŽ™μ€ λ‹€μŒ 링크에 μ •μ˜λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.
νœ΄λ„· μž„μ§μ›: https://ihunet.atlassian.net/wiki/spaces/KUDOS/pages/3783786517/API+specification+V1.2
일반 μ‚¬μš©μž: https://velog.io/@jogakdal/standard-api-specification

μ„€μΉ˜

pip install standard-api-response

이 ν”„λ‘œμ νŠΈμ˜ 전체 μ†ŒμŠ€ μ½”λ“œλ₯Ό λ‹€μš΄ λ°›μœΌμ‹œλ €λ©΄ μ €μž₯μ†Œλ₯Ό ν΄λ‘ ν•˜κ³  ν•„μš”ν•œ 쒅속성을 μ„€μΉ˜ν•˜μ‹­μ‹œμ˜€:

git clone https://github.com/jogakdal/standard-api-response.git
cd <repository-directory>
pip install -r requirements.txt

클래슀 μ„€λͺ… - 응닡 생성

StandardResponse

ν‘œμ€€ API 응닡을 κ΅¬μ„±ν•˜λŠ” ν΄λž˜μŠ€μž…λ‹ˆλ‹€.

  • 속성:

    • status (str): 응닡 성곡 μ—¬λΆ€, 'success' λ˜λŠ” 'error'둜 μ§€μ •λ©λ‹ˆλ‹€.
    • version (str): API 버전.
    • datetime (datetime): 응닡 μ‹œκ°.
    • duration (int): 처리 μ‹œκ°„ (λ°€λ¦¬μ΄ˆ).
    • payload (generic): 응닡 데이터.
  • λ©”μ„œλ“œ:

    • build(payload=None, callback=None, status=None, version=None):
      • 응닡 데이터, 응닡 μƒνƒœ, API 버전을 μ΄μš©ν•˜μ—¬ ν‘œμ€€ 응닡 객체(StandardResponse)λ₯Ό μƒμ„±ν•©λ‹ˆλ‹€.
      • payloadκ°€ None인 경우 callback ν•¨μˆ˜λ₯Ό μ΄μš©ν•˜μ—¬ payloadλ₯Ό μƒμ„±ν•©λ‹ˆλ‹€.
      • duration μžλ™ 계산을 ν•˜λ €λ©΄ callback ν•¨μˆ˜λ₯Ό μ΄μš©ν•˜μ—¬ payloadλ₯Ό 생성해야 ν•©λ‹ˆλ‹€.
      • callback ν•¨μˆ˜λŠ” payload, status, version을 λ°˜ν™˜ν•΄μ•Ό ν•©λ‹ˆλ‹€.
      • callback ν•¨μˆ˜κ°€ λ°˜ν™˜ν•œ statusκ°€ None이 μ•„λ‹ˆλ©΄ StandardResponse 객체의 status ν•„λ“œμ— μ§€μ •λ©λ‹ˆλ‹€.
      • callback ν•¨μˆ˜κ°€ λ°˜ν™˜ν•œ version이 None이 μ•„λ‹ˆλ©΄ StandardResponse 객체의 version ν•„λ“œμ— μ§€μ •λ©λ‹ˆλ‹€.
      • μ΄λŠ” νŽ˜μ΄λ‘œλ“œ 생성 쀑 λ°œμƒν•  수 μžˆλŠ” 였λ₯˜ μ½”λ“œλ₯Ό StandardResponse 객체에 λ°˜μ˜ν•˜κΈ° μœ„ν•¨μž…λ‹ˆλ‹€.
  • μ‚¬μš© 예:

class SamplePayload(BaseModel):
    value_1: str
    value_2: int

@app.get('/item')
async def sample_item():
    def __lambda():
        payload = SamplePayload(value_1='sample', value_2=0)
        return payload, None, None

    return StandardResponse.build(callback=__lambda)

ErrorPayloadItem

였λ₯˜ νŽ˜μ΄λ‘œλ“œμ˜ errors 리슀트의 였λ₯˜ μ•„μ΄ν…œμ„ κ΅¬μ„±ν•©λ‹ˆλ‹€.

  • 속성:
    • code (str): 였λ₯˜ μ½”λ“œ.
    • message (str): 였λ₯˜ λ©”μ‹œμ§€.

ErrorPayload

였λ₯˜ νŽ˜μ΄λ‘œλ“œλ₯Ό λ‚˜νƒ€λƒ…λ‹ˆλ‹€.

  • 속성:
    • errors (List[ErrorPayloadItem]): 였λ₯˜ 리슀트.
    • appendix (Optional[dict]): μΆ”κ°€ 정보.
  • λ©”μ„œλ“œ:
    • add_error(code: str, message: str):
      • 였λ₯˜ μ•„μ΄ν…œμ„ μΆ”κ°€ν•©λ‹ˆλ‹€.
    • build(code, message, appendix: Optional[dict] = None):
      • 단일 였λ₯˜ μ•„μ΄ν…œμœΌλ‘œ 였λ₯˜ νŽ˜μ΄λ‘œλ“œ 객체λ₯Ό μƒμ„±ν•©λ‹ˆλ‹€.
      • code: 였λ₯˜ μ½”λ“œ.
      • message: 였λ₯˜ λ©”μ‹œμ§€.
      • appendix: μΆ”κ°€ 정보 (선택 사항).

PageableList

νŽ˜μ΄μ§€ ν˜•νƒœμ˜ 리슀트 응닡을 생성할 λ•Œ μ‚¬μš©ν•©λ‹ˆλ‹€.
Generic을 μ΄μš©ν•˜μ—¬ 리슀트 μ•„μ΄ν…œμ˜ μ‹€μ œ νƒ€μž…μ„ μ§€μ •ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

  • 속성:
    • page (PageInfo): νŽ˜μ΄μ§€ 정보.
    • order (Optional[OrderInfo]): μ •λ ¬ 정보
    • items: (Items[I]): μ•„μ΄ν…œ 정보
  • λ©”μ„œλ“œ:
    • build(total_items: int, page_size: int, current_page: int, items, order_info: OrderInfo=None):
      • PageableList 객체λ₯Ό μƒμ„±ν•©λ‹ˆλ‹€.
      • 총 μ•„μ΄ν…œ μˆ˜μ™€ νŽ˜μ΄μ§€ λ‹Ή μ•„μ΄ν…œ 수λ₯Ό μ΄μš©ν•˜μ—¬ νŽ˜μ΄μ§€ 정보(PageInfo 객체)λ₯Ό μƒμ„±ν•©λ‹ˆλ‹€.
  • μ‚¬μš© 예:
class SampleItem(BaseModel):
    key: str
    value: int


class SamplePageListPayload(BaseModel):
    value_1: str
    value_2: int
    pageable: PageableList[SampleItem]


class SampleService:
    def __init__(self):
        self.item_list = []
        for i in range(100):
            self.item_list.append(SampleItem(key=f'key_{i}', value=i))

    def get_pageable_list(self, page: int, page_size: int):
        # page == 0 이면 λͺ¨λ“  데이터 λ°˜ν™˜
        if page <= 0:
            page = 1
            page_size = len(self.item_list)

        page_list = PageableList[SampleItem].build(
            items=self.item_list[(page - 1) * page_size : page * page_size],
            total_items=len(self.item_list),
            page_size=page_size,
            current_page=page
        )

        payload = SamplePageListPayload(
            value_1='page_list_sample',
            value_2=0,
            pageable=page_list.model_dump()  # Pydanticμ—μ„œ custom model에 λŒ€ν•œ 직렬화λ₯Ό μˆ˜ν–‰ν•  λ•Œ dictλ₯Ό μ‚¬μš©ν•˜λ―€λ‘œ dict둜 λ³€ν™˜
        )
        return payload


@app.get('/page_list/{page}')
async def sample_page_list(
    page: int = Path(description='νŽ˜μ΄μ§€ 번호, 0인 경우 λͺ¨λ“  데이터 λ°˜ν™˜', ge=0),
    page_size: int = Query(default=10, description='νŽ˜μ΄μ§€ λ‹Ή μ•„μ΄ν…œ 수', ge=1),
):
    def __lambda():
        payload = sample_service.get_pageable_list(page, page_size)
        return payload, None, None

    sample_service = SampleService()
    return StandardResponse.build(callback=__lambda)

IncrementalList

증뢄 ν˜•μ‹μ˜ 리슀트 응닡을 생성할 λ•Œ μ‚¬μš©ν•©λ‹ˆλ‹€.
Generic을 μ΄μš©ν•˜μ—¬ 리슀트 μ•„μ΄ν…œμ˜ μ‹€νƒ€μž…μ„ μ§€μ •ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

  • 속성:
    • cursor (CursorInfo): μ»€μ„œ 정보.
    • order (Optional[OrderInfo]): μ •λ ¬ 정보
    • items: (Items[I]): μ•„μ΄ν…œ 정보
  • μ‚¬μš© 예:
class SampleItem(BaseModel):
    key: str
    value: int


class SampleIncrementalListPayload(BaseModel):
    value_1: str
    value_2: int
    incremental: IncrementalList[SampleItem]


class SampleService:
    def __init__(self):
        self.item_list = []
        for i in range(100):
            self.item_list.append(SampleItem(key=f'key_{i}', value=i))

    def get_incremental_list(self, start_index: int, how_many: int):
        item_count = len(self.item_list)

        if start_index >= item_count:
            return SampleIncrementalListPayload(
                value_1='no more item',
                value_2=0,
                incremental=IncrementalList[SampleItem](
                    cursor=CursorInfo(field='sequence', start=start_index, end=None, expandable=False),
                    order=OrderInfo(sorted=True, by=[OrderBy(field='key', direction=OrderDirection.ASC)]).model_dump(),
                    items=Items[SampleItem](total=item_count, current=0, list=[]).model_dump()
                ).model_dump()
            )

        real_fetch_size = min(how_many, item_count - start_index)

        order = OrderInfo(
            sorted=True,
            by=[
                OrderBy(field='key', direction=OrderDirection.ASC),
                OrderBy(field='value', direction=OrderDirection.ASC)
            ]
        )

        items = Items.build(item_count, self.item_list[start_index: start_index + real_fetch_size])

        cursor = CursorInfo.build_from_total(
            start_index=start_index,
            how_many=how_many,
            total_items=item_count,
            field='sequence'
        )

        incremental = IncrementalList[SampleItem](
            cursor=cursor,
            order=order.model_dump(),
            items=items.model_dump()
        )

        return SampleIncrementalListPayload(
            value_1='expandable_list_sample',
            value_2=0,
            incremental=incremental.model_dump()
        )


@app.get('/more_list/{start_index}')
async def sample_incremental_list(
    start_index: int = Path(description='μ‹œμž‘ 인덱슀', ge=0),
    how_many: int = Query(default=10, description='ν•œ λ²ˆμ— κ°€μ Έμ˜¬ μ•„μ΄ν…œ 수', ge=1),
):
    def __lambda():
        payload = sample_service.get_incremental_list(start_index, how_many)
        return payload, None, None

    sample_service = SampleService()
    return StandardResponse.build(callback=__lambda)

PageInfo

νŽ˜μ΄μ§€ 정보λ₯Ό κ΅¬μ„±ν•©λ‹ˆλ‹€. PageableList의 page 속성을 ꡬ성할 λ•Œ μ‚¬μš©ν•©λ‹ˆλ‹€.

  • 속성:
    • size (int): νŽ˜μ΄μ§€ λ‹Ή μ•„μ΄ν…œ 수
    • current (int): ν˜„μž¬ νŽ˜μ΄μ§€ 번호 total (int): 전체 νŽ˜μ΄μ§€ 수
  • λ©”μ„œλ“œ:
    • calc_total_pages(total_items: int, page_size: int):
      • 전체 μ•„μ΄ν…œ μˆ˜μ™€ νŽ˜μ΄μ§€ λ‹Ή μ•„μ΄ν…œ 수λ₯Ό μ΄μš©ν•˜μ—¬ 전체 νŽ˜μ΄μ§€ 수λ₯Ό κ³„μ‚°ν•©λ‹ˆλ‹€.

CursorInfo

μ»€μ„œ 정보λ₯Ό κ΅¬μ„±ν•©λ‹ˆλ‹€. IncrementalList의 cursor 속성을 ꡬ성할 λ•Œ μ‚¬μš©ν•©λ‹ˆλ‹€.

  • 속성:
    • field (Optional[str]): μ»€μ„œμ˜ 기쀀이 λ˜λŠ” ν•„λ“œ λͺ….
    • start (Any): μ‹œμž‘ 인덱슀 λ˜λŠ” ν‚€.
    • end (Any): 끝 인덱슀 λ˜λŠ” ν‚€.
    • expandable (Optional[bool]): λ‹€μŒ μ•„μ΄ν…œ 쑴재 μ—¬λΆ€.
  • λ©”μ„œλ“œ:
    • build_from_total(start_index: int, how_many: int, total_items: int, field: str=None, convert_index=lambda field_name, index: index)
      • 총 μ•„μ΄ν…œ μˆ˜μ™€ μ‹œμž‘ 인덱슀, 리턴할 μ•„μ΄ν…œ 수λ₯Ό μ‚¬μš©ν•˜μ—¬ CursorInfo 객체λ₯Ό μƒμ„±ν•©λ‹ˆλ‹€.
      • start_indexλŠ” μ‹€μ œ μ»€μ„œ κΈ°μ€€ ν•„λ“œμ˜ νƒ€μž…κ³Ό 관계없이 μ •μˆ˜ν˜• (리슀트의) 인덱슀 정보λ₯Ό 전달해야 ν•©λ‹ˆλ‹€.
      • 리턴할 μ»€μ„œμ˜ μ‹€μ œ 값이 리슀트의 인덱슀 정보가 μ•„λ‹ˆλΌλ©΄ convert_index 콜백 ν•¨μˆ˜λ₯Ό μ΄μš©ν•˜μ—¬ μ»€μ„œ κΈ°μ€€ ν•„λ“œμ˜ κ²‚μœΌλ‘œ λ³€ν™˜ν•΄ 쀄 수 μžˆμŠ΅λ‹ˆλ‹€.
  • μ‚¬μš© 예:
    • IncrementalList 클래슀의 μ‚¬μš© 예λ₯Ό μ°Έμ‘°ν•˜μ‹­μ‹œμ˜€.

Items

μ•„μ΄ν…œ 정보λ₯Ό κ΅¬μ„±ν•©λ‹ˆλ‹€.
PageableList, IncrementalList의 items 속성을 ꡬ성할 λ•Œ μ‚¬μš©ν•©λ‹ˆλ‹€.

  • 속성:
    • total (Optional[int]): 전체 μ•„μ΄ν…œ 수.
    • current (Optional[int]): ν˜„μž¬ μ•„μ΄ν…œ 수.
    • list (list): μ•„μ΄ν…œ 리슀트.
  • λ©”μ„œλ“œ:
    • build(total_items: int, items)
      • Items 객체λ₯Ό μƒμ„±ν•©λ‹ˆλ‹€.
      • currentλŠ” items 리슀트의 μ‹€μ œ size둜 μ§€μ •λ©λ‹ˆλ‹€.
  • μ‚¬μš© 예:
    • IncrementalList 클래슀의 μ‚¬μš© 예λ₯Ό μ°Έμ‘°ν•˜μ‹­μ‹œμ˜€.

OrderInfo

μ •λ ¬ 정보λ₯Ό λ‚˜νƒ€λƒ…λ‹ˆλ‹€.
PageableList, IncrementalList의 order 속성을 ꡬ성할 λ•Œ μ‚¬μš©ν•©λ‹ˆλ‹€.

  • 속성:
    • sorted (bool): μ •λ ¬ μ—¬λΆ€.
    • by (List[OrderBy]): μ •λ ¬λœ ν•„λ“œ.
  • μ‚¬μš© 예:
    • IncrementalList 클래슀의 μ‚¬μš© 예λ₯Ό μ°Έμ‘°ν•˜μ‹­μ‹œμ˜€.

OrderBy

μ •λ ¬λœ ν•„λ“œ 정보λ₯Ό λ‚˜νƒ€λƒ…λ‹ˆλ‹€.
OrderInfo의 by 속성을 ꡬ성할 λ•Œ μ‚¬μš©ν•©λ‹ˆλ‹€.

  • 속성:
    • field (str): μ •λ ¬ν•  ν•„λ“œ λͺ….
    • direction (OrderDirection): μ •λ ¬ λ°©ν–₯. ("ASC": OrderDirection.ASC, "DESC": OrderDirection.DESC)
  • μ‚¬μš© 예:
    • IncrementalList 클래슀의 μ‚¬μš© 예λ₯Ό μ°Έμ‘°ν•˜μ‹­μ‹œμ˜€.

OrderDirection

μ •λ ¬ λ°©ν–₯을 μ§€μ •ν•˜λŠ” Enum ν΄λž˜μŠ€μž…λ‹ˆλ‹€.
OrderBy의 direction 속성을 μ§€μ •ν•  λ•Œ μ‚¬μš©ν•©λ‹ˆλ‹€.

  • 속성:
    • ASC (str): μ˜€λ¦„μ°¨μˆœ.
    • DESC (str): λ‚΄λ¦Όμ°¨μˆœ.
  • μ‚¬μš© 예:
    • IncrementalList 클래슀의 μ‚¬μš© 예λ₯Ό μ°Έμ‘°ν•˜μ‹­μ‹œμ˜€.

클래슀 μ„€λͺ… - 응닡 κ²°κ³Ό λ§€ν•‘

StandardResponseMapper

ν‘œμ€€ API 응닡을 λŒ€μ‘ 객체둜 λ§€ν•‘ν•˜λŠ” ν΄λž˜μŠ€μž…λ‹ˆλ‹€. 기본적으둜 클래슀 νŒŒλΌλ―Έν„°λ‘œ 응닡 json을 μ§€μ •ν•˜λ©΄ ν•΄λ‹Ή json을 파이썬 객체둜 λ³€ν™˜ν•˜μ—¬ response 멀버 λ³€μˆ˜μ— μ €μž₯ν•©λ‹ˆλ‹€. 객체λ₯Ό 생성할 λ•Œ payload νƒ€μž…μ„ μ§€μ •ν•˜λ©΄ response.payload 멀버 λ³€μˆ˜fmf ν•΄λ‹Ή νƒ€μž…μœΌλ‘œ λͺ…μ‹œν•΄ μ€λ‹ˆλ‹€.

  • λ©”μ„œλ“œ:
    • __init__(response: dict, payload_type: Type[BaseModel]=None):
      • 응닡 jsonκ³Ό payload νƒ€μž…μ„ μ΄μš©ν•˜μ—¬ 객체λ₯Ό μƒμ„±ν•©λ‹ˆλ‹€.
    • map_payload(response: dict, payload_type: Type[P]) -> Type[P]:
      • 응닡 json의 payloadλ₯Ό payload_type으둜 λ§€ν•‘ν•©λ‹ˆλ‹€.
    • map_list(payload: dict, list_type: Type[P], list_key: str = 'pageable') -> Type[P]:
      • μ „λ‹¬λœ payload json의 list_key 킀에 ν•΄λ‹Ήν•˜λŠ” 리슀트λ₯Ό list_type으둜 λ§€ν•‘ν•©λ‹ˆλ‹€.
    • map_pageable_list(payload: dict, item_type: Type[P], list_key: str = 'pageable') -> PageableList[P]:
      • map_list ν•¨μˆ˜μ˜ PageableList λ²„μ „μž…λ‹ˆλ‹€.
    • map_incremental_list(payload: dict, item_type: Type[P], list_key: str = 'incremental') -> IncrementalList[P]:
      • map_list ν•¨μˆ˜μ˜ IncrementalList λ²„μ „μž…λ‹ˆλ‹€.
    • 'auto_map_list(payload: dict, item_type: Type[P]) -> Dict[str, _BaseList]'
      • payload에 μžˆλŠ” list 데이터λ₯Ό μžλ™μœΌλ‘œ λ³€ν™˜ν•˜μ—¬ λ°˜ν™˜ν•©λ‹ˆλ‹€.
      • ν˜„μž¬ PageableList, IncrementalList 두 νƒ€μž…λ§Œ μ§€μ›ν•©λ‹ˆλ‹€.
      • payload에 ν•œ 개 μ΄μƒμ˜ λ¦¬μŠ€νŠΈκ°€ μžˆμ„ 경우, λͺ¨λ“  리슀트λ₯Ό λ³€ν™˜ν•˜μ—¬ {'<ν‚€ν•„λ“œ λͺ…>: <객체>} ν˜•νƒœλ‘œ λ°˜ν™˜ν•©λ‹ˆλ‹€.
  • μ‚¬μš© 예:
@pytest.mark.asyncio
async def test_page_list(start_api_server):
  client = AsyncClient(base_url="http://localhost:5010")

  response = await client.get(
    url=f'/page_list/{1}',
    params={
      "page_size": 5
    }
  )
  assert response.status_code == http.HTTPStatus.OK
  json = response.json()
  assert json['status'] == PayloadStatus.SUCCESS

  mapper = StdResponseMapper(json, SamplePageListPayload)
  assert mapper.response.status == PayloadStatus.SUCCESS
  assert mapper.response.payload.pageable.page.size == 5
  assert isinstance(mapper.response.payload, SamplePageListPayload)
  assert isinstance(mapper.response.payload.pageable, PageableList)
  assert isinstance(mapper.response.payload.pageable.items, Items)
  assert isinstance(mapper.response.payload.pageable.items.list[0], SampleItem)
  assert mapper.response.payload.pageable.page.current == 1
  assert mapper.response.payload.pageable.items.current == 5
  assert len(mapper.response.payload.pageable.items.list) == 5
  assert mapper.response.payload.pageable.items.list[0].key == 'key_0'
  assert mapper.response.payload.pageable.items.list[0].value == 0

  payload = StdResponseMapper.map_payload(json, SamplePageListPayload)
  assert isinstance(payload, SamplePageListPayload)
  assert isinstance(payload.pageable, PageableList)
  assert isinstance(payload.pageable.items, Items)
  assert isinstance(payload.pageable.items.list[0], SampleItem)

  # pageable = StdResponseMapper().map_list(json.get('payload'), PageableList[SampleItem], 'pageable')
  pageable = StdResponseMapper.map_pageable_list(json.get('payload'), SampleItem, 'pageable')

  assert isinstance(pageable, PageableList)
  assert isinstance(pageable.items, Items)
  assert isinstance(pageable.items.list[0], SampleItem)
  assert pageable.page.size == 5
  assert pageable.page.current == 1
  assert pageable.items.current == 5
  assert len(pageable.items.list) == 5

  lists = StdResponseMapper.auto_map_list(json.get('payload'), SampleItem)
  assert len(lists) == 1
  assert isinstance(lists['pageable'], PageableList)
  assert isinstance(lists['pageable'].items, Items)
  assert isinstance(lists['pageable'].items.list[0], SampleItem)

응닡 ν•„λ“œ λ³€ν™˜

ResponseKeyConverter 클래슀

  • ResponseKeyConverter 클래슀λ₯Ό μ‚¬μš©ν•˜λ©΄ 응닡을 생성할 λ•Œλ‚˜ 응닡을 λͺ¨λΈλ‘œ λ§€ν•‘ν•  λ•Œ ν•„λ“œλͺ…μ΄λ‚˜ ν•„λ“œλͺ…μ˜ μΌ€μ΄μŠ€ μ»¨λ²€μ…˜μ„ λ³€ν™˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • 상세 μ„€λͺ…은 convertable-key-model λͺ¨λ“ˆ μ„€λͺ…μ„œλ₯Ό μ°Έμ‘°ν•˜μ‹­μ‹œμ˜€.

응닡 ν•„λ“œ λ³€ν™˜ 예제

from pydantic import BaseModel


class SampleItem(BaseModel):
  key: str
  value: int


class SamplePageListPayload(ConvertableKeyModel):
  value_1: str
  value_2: int
  pageable: PageableList[SampleItem]


class SampleService:
  def __init__(self):
    self.item_list = []
    for i in range(100):
      self.item_list.append(SampleItem(key=f'key_{i}', value=i))

  def get_pageable_list(self, page: int, page_size: int):
    # page == 0 이면 λͺ¨λ“  데이터 λ°˜ν™˜
    if page <= 0:
      page = 1
      page_size = len(self.item_list)

    page_list = PageableList[SampleItem].build(
      items=self.item_list[(page - 1) * page_size: page * page_size],
      total_items=len(self.item_list),
      page_size=page_size,
      current_page=page,
      order_info=OrderInfo(sorted=True, by=[OrderBy(field='key', direction=OrderDirection.ASC)]),
    )

    payload = SamplePageListPayload(
      value_1='page_list_sample',
      value_2=0,
      pageable=page_list.convert_key(),  # Pydanticμ—μ„œ custom model에 λŒ€ν•œ 직렬화λ₯Ό μˆ˜ν–‰ν•  λ•Œ dictλ₯Ό μ‚¬μš©ν•˜λ―€λ‘œ dict둜 λ³€ν™˜
    )
    return payload


def test_with_standard_response_class():
  def make_temporary_response():
    def __lambda():
      payload = sample_service.get_pageable_list(page=1, page_size=5)
      return payload, None, None

    sample_service = SampleService()

    ResponseKeyConverter().clear()
    ResponseKeyConverter().add_alias(StandardResponse, 'duration', 'duration_time')
    ResponseKeyConverter().add_alias(PageInfo, 'current', 'current_page')
    ResponseKeyConverter().add_alias(PageInfo, 'size', 'page_size')
    ResponseKeyConverter().add_alias(PageInfo, 'total', 'total_pages')
    ResponseKeyConverter().add_alias(OrderInfo, 'by', 'order_by')
    ResponseKeyConverter().add_alias(Items[SampleItem], 'current', 'current_page')
    ResponseKeyConverter().add_alias(PageableList[SampleItem], 'page', 'page_info')
    ResponseKeyConverter().set_default_case_convention(CaseConvention.CAMEL)

    result = StandardResponse.build(callback=__lambda)
    result = result.convert_key()
    ResponseKeyConverter().clear()
    return result

  response_json = make_temporary_response()
  print(json.dumps(response_json, indent=2, ensure_ascii=False))

  ResponseKeyConverter().add_alias(StandardResponse, 'duration', 'duration_time')
  ResponseKeyConverter().add_alias(PageInfo, 'current', 'current_page')
  ResponseKeyConverter().add_alias(PageInfo, 'size', 'page_size')
  ResponseKeyConverter().add_alias(PageInfo, 'total', 'total_pages')
  ResponseKeyConverter().add_alias(OrderInfo, 'by', 'order_by')
  ResponseKeyConverter().add_alias(Items[SampleItem], 'current', 'current_page')
  ResponseKeyConverter().add_alias(PageableList[SampleItem], 'page', 'page_info')
  ResponseKeyConverter().set_default_case_convention(CaseConvention.CAMEL)

  mapper = StdResponseMapper(response_json, SamplePageListPayload)
  assert mapper.response.status == PayloadStatus.SUCCESS
  assert mapper.response.payload.pageable.page.size == 5
  assert isinstance(mapper.response.payload, SamplePageListPayload)
  assert isinstance(mapper.response.payload.pageable, PageableList)
  assert isinstance(mapper.response.payload.pageable.items, Items)
  assert isinstance(mapper.response.payload.pageable.items.list[0], SampleItem)
  assert mapper.response.payload.pageable.page.current == 1
  assert mapper.response.payload.pageable.items.current == 5
  assert len(mapper.response.payload.pageable.items.list) == 5
  assert mapper.response.payload.pageable.items.list[0].key == 'key_0'
  assert mapper.response.payload.pageable.items.list[0].value == 0

  payload = StdResponseMapper.map_payload(response_json, SamplePageListPayload)
  assert isinstance(payload, SamplePageListPayload)
  assert isinstance(payload.pageable, PageableList)
  assert isinstance(payload.pageable.items, Items)
  assert isinstance(payload.pageable.items.list[0], SampleItem)

  # pageable = StdResponseMapper().map_list(json.get('payload'), PageableList[SampleItem], 'pageable')
  pageable = StdResponseMapper.map_pageable_list(response_json.get('payload'), SampleItem, 'pageable')

  assert isinstance(pageable, PageableList)
  assert isinstance(pageable.items, Items)
  assert isinstance(pageable.items.list[0], SampleItem)
  assert pageable.page.size == 5
  assert pageable.page.current == 1
  assert pageable.items.current == 5
  assert len(pageable.items.list) == 5

  lists = StdResponseMapper.auto_map_list(response_json.get('payload'), SampleItem)
  assert len(lists) == 1
  assert isinstance(lists['pageable'], PageableList)
  assert isinstance(lists['pageable'].items, Items)
  assert isinstance(lists['pageable'].items.list[0], SampleItem)

  ResponseKeyConverter().clear()

λΌμ΄μ„ μŠ€

이 λΌμ΄λΈŒλŸ¬λ¦¬λŠ” λˆ„κ΅¬λ‚˜ μ‚¬μš©ν•  수 μžˆλŠ” 프리 μ†Œν”„νŠΈμ›¨μ–΄μž…λ‹ˆλ‹€. λ‹€λ§Œ μ½”λ“œλ₯Ό μˆ˜μ •ν•  경우 λ³€κ²½λœ λ‚΄μš©μ„ μ›μž‘μ„±μžμ—κ²Œ 톡보해 μ£Όμ‹œλ©΄ κ°μ‚¬ν•˜κ² μŠ΅λ‹ˆλ‹€.

μž‘μ„±μž

ν™©μš©ν˜Έ(jogakdal@gmail.com)

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors