From 04e35da6fb8f753a3784146fd3da83e87c4d93ca Mon Sep 17 00:00:00 2001 From: YoshihitoAso Date: Mon, 28 Oct 2024 23:12:11 +0900 Subject: [PATCH] Add search query and sort item for ChildAccount API --- app/model/schema/__init__.py | 1 + app/model/schema/account.py | 34 +- app/model/schema/token_holders.py | 2 +- app/routers/issuer/account.py | 120 ++- app/routers/issuer/token_holders.py | 2 +- docs/ibet_prime.yaml | 98 +- .../app/test_accounts_ListAllChildAccount.py | 940 +++++++++++++++++- .../app/test_accounts_RetrieveChildAccount.py | 7 + 8 files changed, 1159 insertions(+), 45 deletions(-) diff --git a/app/model/schema/__init__.py b/app/model/schema/__init__.py index 692ddbbe..0b45144d 100644 --- a/app/model/schema/__init__.py +++ b/app/model/schema/__init__.py @@ -30,6 +30,7 @@ CreateUpdateChildAccountRequest, ListAllChildAccountQuery, ListAllChildAccountResponse, + ListAllChildAccountSortItem, ) from .batch_issue_redeem import ( BatchIssueRedeemUploadIdResponse, diff --git a/app/model/schema/account.py b/app/model/schema/account.py index 17ddd15b..68578416 100644 --- a/app/model/schema/account.py +++ b/app/model/schema/account.py @@ -18,10 +18,12 @@ """ from datetime import datetime +from enum import StrEnum from typing import Optional from pydantic import BaseModel, Field, RootModel, field_validator +from app.model import ValidatedDatetimeStr from app.model.db import AccountRsaStatus from app.model.schema.base import BasePaginationQuery, ResultSet, SortOrder from app.model.schema.personal_info import PersonalInfo, PersonalInfoInput @@ -48,6 +50,8 @@ class ChildAccount(BaseModel): child_account_index: int child_account_address: str personal_information: PersonalInfo | None + created: datetime | None + modified: datetime | None ############################ @@ -135,10 +139,38 @@ class CreateUpdateChildAccountRequest(BaseModel): personal_information: PersonalInfoInput +class ListAllChildAccountSortItem(StrEnum): + child_account_index = "child_account_index" + child_account_address = "child_account_address" + name = "name" + created = "created" + modified = "modified" + + class ListAllChildAccountQuery(BasePaginationQuery): + child_account_address: Optional[str] = Field( + None, description="Child account address (partial match)" + ) + name: Optional[str] = Field(None, description="Child account name (partial match)") + created_from: Optional[ValidatedDatetimeStr] = Field( + None, description="Personal information created datetime (From)" + ) + created_to: Optional[ValidatedDatetimeStr] = Field( + None, description="Personal information created datetime (To)" + ) + modified_from: Optional[ValidatedDatetimeStr] = Field( + None, description="Personal information modified datetime (From)" + ) + modified_to: Optional[ValidatedDatetimeStr] = Field( + None, description="Personal information modified datetime (To)" + ) + + sort_item: Optional[ListAllChildAccountSortItem] = Field( + ListAllChildAccountSortItem.child_account_index, description="Sort item" + ) sort_order: Optional[SortOrder] = Field( SortOrder.ASC, - description="The sort order of the child_account_index. 0:asc, 1:desc", + description=SortOrder.__doc__, ) diff --git a/app/model/schema/token_holders.py b/app/model/schema/token_holders.py index 7e996eb7..4d48c476 100644 --- a/app/model/schema/token_holders.py +++ b/app/model/schema/token_holders.py @@ -123,7 +123,7 @@ class RetrieveTokenHoldersCollectionSortItem(StrEnum): hold_balance = "hold_balance" locked_balance = "locked_balance" key_manager = "key_manager" - holder_name = "tax_category" + tax_category = "tax_category" class RetrieveTokenHoldersCollectionQuery(BasePaginationQuery): diff --git a/app/routers/issuer/account.py b/app/routers/issuer/account.py index 322bad9b..27288e15 100644 --- a/app/routers/issuer/account.py +++ b/app/routers/issuer/account.py @@ -68,6 +68,7 @@ CreateUpdateChildAccountRequest, ListAllChildAccountQuery, ListAllChildAccountResponse, + ListAllChildAccountSortItem, ) from app.utils.check_utils import ( address_is_valid_address, @@ -94,6 +95,7 @@ router = APIRouter(tags=["account"]) local_tz = pytz.timezone(TZ) +utc_tz = pytz.timezone("UTC") # POST: /accounts @@ -704,13 +706,76 @@ async def list_all_child_account( ) ) total = await db.scalar(select(func.count()).select_from(stmt.subquery())) - count = total + + if get_query.child_account_address is not None: + stmt = stmt.where( + ChildAccount.child_account_address.like( + "%" + get_query.child_account_address + "%" + ) + ) + if get_query.name is not None: + stmt = stmt.where( + IDXPersonalInfo._personal_info["name"] + .as_string() + .like("%" + get_query.name + "%") + ) + if get_query.created_from: + _created_from = datetime.strptime( + get_query.created_from + ".000000", "%Y-%m-%d %H:%M:%S.%f" + ) + stmt = stmt.where( + IDXPersonalInfo.created + >= local_tz.localize(_created_from).astimezone(utc_tz) + ) + if get_query.created_to: + _created_to = datetime.strptime( + get_query.created_to + ".999999", "%Y-%m-%d %H:%M:%S.%f" + ) + stmt = stmt.where( + IDXPersonalInfo.created <= local_tz.localize(_created_to).astimezone(utc_tz) + ) + if get_query.modified_from: + _modified_from = datetime.strptime( + get_query.modified_from + ".000000", "%Y-%m-%d %H:%M:%S.%f" + ) + stmt = stmt.where( + IDXPersonalInfo.modified + >= local_tz.localize(_modified_from).astimezone(utc_tz) + ) + if get_query.modified_to: + _modified_to = datetime.strptime( + get_query.modified_to + ".999999", "%Y-%m-%d %H:%M:%S.%f" + ) + stmt = stmt.where( + IDXPersonalInfo.modified + <= local_tz.localize(_modified_to).astimezone(utc_tz) + ) + + count = await db.scalar(select(func.count()).select_from(stmt.subquery())) # Sort - if get_query.sort_order == 0: # ASC - stmt = stmt.order_by(asc(ChildAccount.child_account_index)) - else: # DESC - stmt = stmt.order_by(desc(ChildAccount.child_account_index)) + def _order(_order): + if _order == 0: + return asc + else: + return desc + + _sort_order = get_query.sort_order + match get_query.sort_item: + case ListAllChildAccountSortItem.child_account_index: + stmt = stmt.order_by(_order(_sort_order)(ChildAccount.child_account_index)) + case ListAllChildAccountSortItem.child_account_address: + stmt = stmt.order_by( + _order(_sort_order)(ChildAccount.child_account_address) + ) + case ListAllChildAccountSortItem.name: + stmt = stmt.order_by( + _order(_sort_order)(IDXPersonalInfo._personal_info["name"].as_string()) + ) + case ListAllChildAccountSortItem.created: + stmt = stmt.order_by(_order(_sort_order)(IDXPersonalInfo.created)) + case ListAllChildAccountSortItem.modified: + stmt = stmt.order_by(_order(_sort_order)(IDXPersonalInfo.modified)) # Pagination if get_query.limit is not None: @@ -724,16 +789,26 @@ async def list_all_child_account( child_accounts = [] for _tmp_child_account in _tmp_child_accounts: - child_accounts.append( - { + if _tmp_child_account[1] is not None: + _personal_info = _tmp_child_account[1].json() + _child_account = { "issuer_address": _tmp_child_account[0].issuer_address, "child_account_index": _tmp_child_account[0].child_account_index, "child_account_address": _tmp_child_account[0].child_account_address, - "personal_information": _tmp_child_account[1].personal_info - if _tmp_child_account[1] is not None - else None, + "personal_information": _personal_info["personal_info"], + "created": _personal_info["created"], + "modified": _personal_info["modified"], } - ) + else: + _child_account = { + "issuer_address": _tmp_child_account[0].issuer_address, + "child_account_index": _tmp_child_account[0].child_account_index, + "child_account_address": _tmp_child_account[0].child_account_address, + "personal_information": None, + "created": None, + "modified": None, + } + child_accounts.append(_child_account) return json_response( { @@ -800,16 +875,27 @@ async def retrieve_child_account( if _child_account is None: raise HTTPException(status_code=404, detail="child account does not exist") - return json_response( - { + if _child_account[1] is not None: + _personal_info = _child_account[1].json() + _resp_account = { "issuer_address": _child_account[0].issuer_address, "child_account_index": _child_account[0].child_account_index, "child_account_address": _child_account[0].child_account_address, - "personal_information": _child_account[1].personal_info - if _child_account[1] is not None - else None, + "personal_information": _personal_info["personal_info"], + "created": _personal_info["created"], + "modified": _personal_info["modified"], } - ) + else: + _resp_account = { + "issuer_address": _child_account[0].issuer_address, + "child_account_index": _child_account[0].child_account_index, + "child_account_address": _child_account[0].child_account_address, + "personal_information": None, + "created": None, + "modified": None, + } + + return json_response(_resp_account) # POST: /accounts/{issuer_address}/child_accounts/{child_account_index} diff --git a/app/routers/issuer/token_holders.py b/app/routers/issuer/token_holders.py index f55ae241..9464f395 100644 --- a/app/routers/issuer/token_holders.py +++ b/app/routers/issuer/token_holders.py @@ -582,7 +582,7 @@ async def retrieve_token_holders_collection( count = await db.scalar(select(func.count()).select_from(stmt.subquery())) # Sort - if get_query.sort_item == RetrieveTokenHoldersCollectionSortItem.holder_name: + if get_query.sort_item == RetrieveTokenHoldersCollectionSortItem.tax_category: sort_attr = IDXPersonalInfo._personal_info["tax_category"].as_integer() elif get_query.sort_item == RetrieveTokenHoldersCollectionSortItem.key_manager: sort_attr = IDXPersonalInfo._personal_info["key_manager"].as_string() diff --git a/docs/ibet_prime.yaml b/docs/ibet_prime.yaml index 903612f8..05cf9987 100644 --- a/docs/ibet_prime.yaml +++ b/docs/ibet_prime.yaml @@ -483,6 +483,77 @@ paths: description: Limit for pagination title: Limit description: Limit for pagination + - name: child_account_address + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: Child account address (partial match) + title: Child Account Address + description: Child account address (partial match) + - name: name + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: Child account name (partial match) + title: Name + description: Child account name (partial match) + - name: created_from + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: Personal information created datetime (From) + title: Created From + description: Personal information created datetime (From) + - name: created_to + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: Personal information created datetime (To) + title: Created To + description: Personal information created datetime (To) + - name: modified_from + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: Personal information modified datetime (From) + title: Modified From + description: Personal information modified datetime (From) + - name: modified_to + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: Personal information modified datetime (To) + title: Modified To + description: Personal information modified datetime (To) + - name: sort_item + in: query + required: false + schema: + anyOf: + - $ref: '#/components/schemas/ListAllChildAccountSortItem' + - type: 'null' + description: Sort item + default: child_account_index + title: Sort Item + description: Sort item - name: sort_order in: query required: false @@ -490,10 +561,10 @@ paths: anyOf: - $ref: '#/components/schemas/SortOrder' - type: 'null' - description: The sort order of the child_account_index. 0:asc, 1:desc + description: 'Sort order (0: ASC, 1: DESC)' default: 0 title: Sort Order - description: The sort order of the child_account_index. 0:asc, 1:desc + description: 'Sort order (0: ASC, 1: DESC)' responses: '200': description: Successful Response @@ -10378,12 +10449,26 @@ components: anyOf: - $ref: '#/components/schemas/PersonalInfo' - type: 'null' + created: + anyOf: + - type: string + format: date-time + - type: 'null' + title: Created + modified: + anyOf: + - type: string + format: date-time + - type: 'null' + title: Modified type: object required: - issuer_address - child_account_index - child_account_address - personal_information + - created + - modified title: ChildAccount description: Child account schema ChildAccountResponse: @@ -12892,6 +12977,15 @@ components: - child_accounts title: ListAllChildAccountResponse description: List all child accounts schema (RESPONSE) + ListAllChildAccountSortItem: + type: string + enum: + - child_account_index + - child_account_address + - name + - created + - modified + title: ListAllChildAccountSortItem ListAllDVPAgentAccountResponse: items: $ref: '#/components/schemas/DVPAgentAccountResponse' diff --git a/tests/app/test_accounts_ListAllChildAccount.py b/tests/app/test_accounts_ListAllChildAccount.py index 1bf26054..99b556ac 100644 --- a/tests/app/test_accounts_ListAllChildAccount.py +++ b/tests/app/test_accounts_ListAllChildAccount.py @@ -17,6 +17,11 @@ SPDX-License-Identifier: Apache-2.0 """ +import datetime +from unittest.mock import ANY + +import pytest + from app.model.db import Account, ChildAccount, IDXPersonalInfo, PersonalInfoDataSource @@ -39,8 +44,9 @@ class TestListAllChildAccount: ########################################################################### # - # Normal search + # Base query # - Personal information is not set + @pytest.mark.freeze_time("2024-10-28 12:34:56") def test_normal_1_1(self, client, db): # Prepare data _account = Account() @@ -69,37 +75,48 @@ def test_normal_1_1(self, client, db): "child_account_index": 1, "child_account_address": self.child_account_address[0], "personal_information": None, + "created": None, + "modified": None, }, { "issuer_address": self.issuer_address, "child_account_index": 2, "child_account_address": self.child_account_address[1], "personal_information": None, + "created": None, + "modified": None, }, { "issuer_address": self.issuer_address, "child_account_index": 3, "child_account_address": self.child_account_address[2], "personal_information": None, + "created": None, + "modified": None, }, { "issuer_address": self.issuer_address, "child_account_index": 4, "child_account_address": self.child_account_address[3], "personal_information": None, + "created": None, + "modified": None, }, { "issuer_address": self.issuer_address, "child_account_index": 5, "child_account_address": self.child_account_address[4], "personal_information": None, + "created": None, + "modified": None, }, ], } # - # Normal search + # Base query # - Personal information is set + @pytest.mark.freeze_time("2024-10-28 12:34:56") def test_normal_1_2(self, client, db): # Prepare data _account = Account() @@ -153,6 +170,8 @@ def test_normal_1_2(self, client, db): "is_corporate": False, "tax_category": 1, }, + "created": "2024-10-28T21:34:56+09:00", + "modified": "2024-10-28T21:34:56+09:00", }, { "issuer_address": self.issuer_address, @@ -168,6 +187,8 @@ def test_normal_1_2(self, client, db): "is_corporate": False, "tax_category": 2, }, + "created": "2024-10-28T21:34:56+09:00", + "modified": "2024-10-28T21:34:56+09:00", }, { "issuer_address": self.issuer_address, @@ -183,73 +204,944 @@ def test_normal_1_2(self, client, db): "is_corporate": False, "tax_category": 3, }, + "created": "2024-10-28T21:34:56+09:00", + "modified": "2024-10-28T21:34:56+09:00", }, ], } - # - # Sort order - def test_normal_2(self, client, db): + # + # Search query + # - child_account_address (partial match) + @pytest.mark.freeze_time("2024-10-28 12:34:56") + def test_normal_2_1(self, client, db): # Prepare data _account = Account() _account.issuer_address = self.issuer_address db.add(_account) - for i in range(1, 6): + for i in range(1, 4): _child_account = ChildAccount() _child_account.issuer_address = self.issuer_address _child_account.child_account_index = i _child_account.child_account_address = self.child_account_address[i - 1] db.add(_child_account) + _off_personal_info = IDXPersonalInfo() + _off_personal_info.issuer_address = self.issuer_address + _off_personal_info.account_address = self.child_account_address[i - 1] + _off_personal_info.personal_info = { + "key_manager": "SELF", + "name": f"name_test_{i}", + "postal_code": f"postal_code_test_{i}", + "address": f"address_test_{i}", + "email": f"email_test_{i}", + "birth": f"birth_test_{i}", + "is_corporate": False, + "tax_category": i, + } + _off_personal_info.data_source = PersonalInfoDataSource.OFF_CHAIN + db.add(_off_personal_info) + db.commit() # Call API resp = client.get( - self.base_url.format(self.issuer_address), params={"sort_order": 1} + self.base_url.format(self.issuer_address), + params={"child_account_address": "0xfc"}, ) # Assertion assert resp.status_code == 200 assert resp.json() == { - "result_set": {"count": 5, "offset": None, "limit": None, "total": 5}, + "result_set": {"count": 1, "offset": None, "limit": None, "total": 3}, "child_accounts": [ { "issuer_address": self.issuer_address, - "child_account_index": 5, - "child_account_address": self.child_account_address[4], - "personal_information": None, + "child_account_index": 2, + "child_account_address": self.child_account_address[1], + "personal_information": { + "key_manager": "SELF", + "name": "name_test_2", + "postal_code": "postal_code_test_2", + "address": "address_test_2", + "email": "email_test_2", + "birth": "birth_test_2", + "is_corporate": False, + "tax_category": 2, + }, + "created": "2024-10-28T21:34:56+09:00", + "modified": "2024-10-28T21:34:56+09:00", }, + ], + } + + # + # Search query + # - name (partial match) + @pytest.mark.freeze_time("2024-10-28 12:34:56") + def test_normal_2_2(self, client, db): + # Prepare data + _account = Account() + _account.issuer_address = self.issuer_address + db.add(_account) + + for i in range(1, 4): + _child_account = ChildAccount() + _child_account.issuer_address = self.issuer_address + _child_account.child_account_index = i + _child_account.child_account_address = self.child_account_address[i - 1] + db.add(_child_account) + + _off_personal_info = IDXPersonalInfo() + _off_personal_info.issuer_address = self.issuer_address + _off_personal_info.account_address = self.child_account_address[i - 1] + _off_personal_info.personal_info = { + "key_manager": "SELF", + "name": f"name_test_{i}", + "postal_code": f"postal_code_test_{i}", + "address": f"address_test_{i}", + "email": f"email_test_{i}", + "birth": f"birth_test_{i}", + "is_corporate": False, + "tax_category": i, + } + _off_personal_info.data_source = PersonalInfoDataSource.OFF_CHAIN + db.add(_off_personal_info) + + db.commit() + + # Call API + resp = client.get( + self.base_url.format(self.issuer_address), params={"name": "test_2"} + ) + + # Assertion + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 1, "offset": None, "limit": None, "total": 3}, + "child_accounts": [ { "issuer_address": self.issuer_address, - "child_account_index": 4, - "child_account_address": self.child_account_address[3], - "personal_information": None, + "child_account_index": 2, + "child_account_address": self.child_account_address[1], + "personal_information": { + "key_manager": "SELF", + "name": "name_test_2", + "postal_code": "postal_code_test_2", + "address": "address_test_2", + "email": "email_test_2", + "birth": "birth_test_2", + "is_corporate": False, + "tax_category": 2, + }, + "created": "2024-10-28T21:34:56+09:00", + "modified": "2024-10-28T21:34:56+09:00", + }, + ], + } + + # + # Search query + # - created_from + def test_normal_2_3(self, client, db): + # Prepare data + _account = Account() + _account.issuer_address = self.issuer_address + db.add(_account) + + for i in range(1, 4): + _child_account = ChildAccount() + _child_account.issuer_address = self.issuer_address + _child_account.child_account_index = i + _child_account.child_account_address = self.child_account_address[i - 1] + db.add(_child_account) + + _off_personal_info = IDXPersonalInfo() + _off_personal_info.issuer_address = self.issuer_address + _off_personal_info.account_address = self.child_account_address[i - 1] + _off_personal_info.personal_info = { + "key_manager": "SELF", + "name": f"name_test_{i}", + "postal_code": f"postal_code_test_{i}", + "address": f"address_test_{i}", + "email": f"email_test_{i}", + "birth": f"birth_test_{i}", + "is_corporate": False, + "tax_category": i, + } + _off_personal_info.data_source = PersonalInfoDataSource.OFF_CHAIN + _off_personal_info.created = datetime.datetime( + 2024, 10, 28, 0, 0, i, tzinfo=datetime.UTC + ) + db.add(_off_personal_info) + + db.commit() + + # Call API + resp = client.get( + self.base_url.format(self.issuer_address), + params={"created_from": "2024-10-28 09:00:02"}, + ) + + # Assertion + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 2, "offset": None, "limit": None, "total": 3}, + "child_accounts": [ + { + "issuer_address": self.issuer_address, + "child_account_index": 2, + "child_account_address": self.child_account_address[1], + "personal_information": { + "key_manager": "SELF", + "name": "name_test_2", + "postal_code": "postal_code_test_2", + "address": "address_test_2", + "email": "email_test_2", + "birth": "birth_test_2", + "is_corporate": False, + "tax_category": 2, + }, + "created": "2024-10-28T09:00:02+09:00", + "modified": ANY, }, { "issuer_address": self.issuer_address, "child_account_index": 3, "child_account_address": self.child_account_address[2], - "personal_information": None, + "personal_information": { + "key_manager": "SELF", + "name": "name_test_3", + "postal_code": "postal_code_test_3", + "address": "address_test_3", + "email": "email_test_3", + "birth": "birth_test_3", + "is_corporate": False, + "tax_category": 3, + }, + "created": "2024-10-28T09:00:03+09:00", + "modified": ANY, + }, + ], + } + + # + # Search query + # - created_to + def test_normal_2_4(self, client, db): + # Prepare data + _account = Account() + _account.issuer_address = self.issuer_address + db.add(_account) + + for i in range(1, 4): + _child_account = ChildAccount() + _child_account.issuer_address = self.issuer_address + _child_account.child_account_index = i + _child_account.child_account_address = self.child_account_address[i - 1] + db.add(_child_account) + + _off_personal_info = IDXPersonalInfo() + _off_personal_info.issuer_address = self.issuer_address + _off_personal_info.account_address = self.child_account_address[i - 1] + _off_personal_info.personal_info = { + "key_manager": "SELF", + "name": f"name_test_{i}", + "postal_code": f"postal_code_test_{i}", + "address": f"address_test_{i}", + "email": f"email_test_{i}", + "birth": f"birth_test_{i}", + "is_corporate": False, + "tax_category": i, + } + _off_personal_info.data_source = PersonalInfoDataSource.OFF_CHAIN + _off_personal_info.created = datetime.datetime( + 2024, 10, 28, 0, 0, i, tzinfo=datetime.UTC + ) + db.add(_off_personal_info) + + db.commit() + + # Call API + resp = client.get( + self.base_url.format(self.issuer_address), + params={"created_to": "2024-10-28 09:00:02"}, + ) + + # Assertion + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 2, "offset": None, "limit": None, "total": 3}, + "child_accounts": [ + { + "issuer_address": self.issuer_address, + "child_account_index": 1, + "child_account_address": self.child_account_address[0], + "personal_information": { + "key_manager": "SELF", + "name": "name_test_1", + "postal_code": "postal_code_test_1", + "address": "address_test_1", + "email": "email_test_1", + "birth": "birth_test_1", + "is_corporate": False, + "tax_category": 1, + }, + "created": "2024-10-28T09:00:01+09:00", + "modified": ANY, }, { "issuer_address": self.issuer_address, "child_account_index": 2, "child_account_address": self.child_account_address[1], - "personal_information": None, + "personal_information": { + "key_manager": "SELF", + "name": "name_test_2", + "postal_code": "postal_code_test_2", + "address": "address_test_2", + "email": "email_test_2", + "birth": "birth_test_2", + "is_corporate": False, + "tax_category": 2, + }, + "created": "2024-10-28T09:00:02+09:00", + "modified": ANY, }, - { - "issuer_address": self.issuer_address, - "child_account_index": 1, - "child_account_address": self.child_account_address[0], - "personal_information": None, + ], + } + + # + # Search query + # - modified_from + def test_normal_2_5(self, client, db): + # Prepare data + _account = Account() + _account.issuer_address = self.issuer_address + db.add(_account) + + for i in range(1, 4): + _child_account = ChildAccount() + _child_account.issuer_address = self.issuer_address + _child_account.child_account_index = i + _child_account.child_account_address = self.child_account_address[i - 1] + db.add(_child_account) + + _off_personal_info = IDXPersonalInfo() + _off_personal_info.issuer_address = self.issuer_address + _off_personal_info.account_address = self.child_account_address[i - 1] + _off_personal_info.personal_info = { + "key_manager": "SELF", + "name": f"name_test_{i}", + "postal_code": f"postal_code_test_{i}", + "address": f"address_test_{i}", + "email": f"email_test_{i}", + "birth": f"birth_test_{i}", + "is_corporate": False, + "tax_category": i, + } + _off_personal_info.data_source = PersonalInfoDataSource.OFF_CHAIN + _off_personal_info.modified = datetime.datetime( + 2024, 10, 28, 0, 0, i, tzinfo=datetime.UTC + ) + db.add(_off_personal_info) + + db.commit() + + # Call API + resp = client.get( + self.base_url.format(self.issuer_address), + params={"modified_from": "2024-10-28 09:00:02"}, + ) + + # Assertion + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 2, "offset": None, "limit": None, "total": 3}, + "child_accounts": [ + { + "issuer_address": self.issuer_address, + "child_account_index": 2, + "child_account_address": self.child_account_address[1], + "personal_information": { + "key_manager": "SELF", + "name": "name_test_2", + "postal_code": "postal_code_test_2", + "address": "address_test_2", + "email": "email_test_2", + "birth": "birth_test_2", + "is_corporate": False, + "tax_category": 2, + }, + "created": ANY, + "modified": "2024-10-28T09:00:02+09:00", + }, + { + "issuer_address": self.issuer_address, + "child_account_index": 3, + "child_account_address": self.child_account_address[2], + "personal_information": { + "key_manager": "SELF", + "name": "name_test_3", + "postal_code": "postal_code_test_3", + "address": "address_test_3", + "email": "email_test_3", + "birth": "birth_test_3", + "is_corporate": False, + "tax_category": 3, + }, + "created": ANY, + "modified": "2024-10-28T09:00:03+09:00", + }, + ], + } + + # + # Search query + # - modified_to + def test_normal_2_6(self, client, db): + # Prepare data + _account = Account() + _account.issuer_address = self.issuer_address + db.add(_account) + + for i in range(1, 4): + _child_account = ChildAccount() + _child_account.issuer_address = self.issuer_address + _child_account.child_account_index = i + _child_account.child_account_address = self.child_account_address[i - 1] + db.add(_child_account) + + _off_personal_info = IDXPersonalInfo() + _off_personal_info.issuer_address = self.issuer_address + _off_personal_info.account_address = self.child_account_address[i - 1] + _off_personal_info.personal_info = { + "key_manager": "SELF", + "name": f"name_test_{i}", + "postal_code": f"postal_code_test_{i}", + "address": f"address_test_{i}", + "email": f"email_test_{i}", + "birth": f"birth_test_{i}", + "is_corporate": False, + "tax_category": i, + } + _off_personal_info.data_source = PersonalInfoDataSource.OFF_CHAIN + _off_personal_info.modified = datetime.datetime( + 2024, 10, 28, 0, 0, i, tzinfo=datetime.UTC + ) + db.add(_off_personal_info) + + db.commit() + + # Call API + resp = client.get( + self.base_url.format(self.issuer_address), + params={"modified_to": "2024-10-28 09:00:02"}, + ) + + # Assertion + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 2, "offset": None, "limit": None, "total": 3}, + "child_accounts": [ + { + "issuer_address": self.issuer_address, + "child_account_index": 1, + "child_account_address": self.child_account_address[0], + "personal_information": { + "key_manager": "SELF", + "name": "name_test_1", + "postal_code": "postal_code_test_1", + "address": "address_test_1", + "email": "email_test_1", + "birth": "birth_test_1", + "is_corporate": False, + "tax_category": 1, + }, + "created": ANY, + "modified": "2024-10-28T09:00:01+09:00", + }, + { + "issuer_address": self.issuer_address, + "child_account_index": 2, + "child_account_address": self.child_account_address[1], + "personal_information": { + "key_manager": "SELF", + "name": "name_test_2", + "postal_code": "postal_code_test_2", + "address": "address_test_2", + "email": "email_test_2", + "birth": "birth_test_2", + "is_corporate": False, + "tax_category": 2, + }, + "created": ANY, + "modified": "2024-10-28T09:00:02+09:00", + }, + ], + } + + # + # Sort order + # - child_account_index (default) + def test_normal_3_1(self, client, db): + # Prepare data + _account = Account() + _account.issuer_address = self.issuer_address + db.add(_account) + + for i in range(1, 6): + _child_account = ChildAccount() + _child_account.issuer_address = self.issuer_address + _child_account.child_account_index = i + _child_account.child_account_address = self.child_account_address[i - 1] + db.add(_child_account) + + db.commit() + + # Call API + resp = client.get( + self.base_url.format(self.issuer_address), params={"sort_order": 1} + ) + + # Assertion + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 5, "offset": None, "limit": None, "total": 5}, + "child_accounts": [ + { + "issuer_address": self.issuer_address, + "child_account_index": 5, + "child_account_address": self.child_account_address[4], + "personal_information": None, + "created": None, + "modified": None, + }, + { + "issuer_address": self.issuer_address, + "child_account_index": 4, + "child_account_address": self.child_account_address[3], + "personal_information": None, + "created": None, + "modified": None, + }, + { + "issuer_address": self.issuer_address, + "child_account_index": 3, + "child_account_address": self.child_account_address[2], + "personal_information": None, + "created": None, + "modified": None, + }, + { + "issuer_address": self.issuer_address, + "child_account_index": 2, + "child_account_address": self.child_account_address[1], + "personal_information": None, + "created": None, + "modified": None, + }, + { + "issuer_address": self.issuer_address, + "child_account_index": 1, + "child_account_address": self.child_account_address[0], + "personal_information": None, + "created": None, + "modified": None, + }, + ], + } + + # + # Sort order + # - child_account_address + def test_normal_3_2(self, client, db): + # Prepare data + _account = Account() + _account.issuer_address = self.issuer_address + db.add(_account) + + for i in range(1, 6): + _child_account = ChildAccount() + _child_account.issuer_address = self.issuer_address + _child_account.child_account_index = i + _child_account.child_account_address = self.child_account_address[i - 1] + db.add(_child_account) + + db.commit() + + # Call API + resp = client.get( + self.base_url.format(self.issuer_address), + params={"sort_item": "child_account_address", "sort_order": 1}, + ) + + # Assertion + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 5, "offset": None, "limit": None, "total": 5}, + "child_accounts": [ + { + "issuer_address": self.issuer_address, + "child_account_index": 2, + "child_account_address": self.child_account_address[1], + "personal_information": None, + "created": None, + "modified": None, + }, + { + "issuer_address": self.issuer_address, + "child_account_index": 3, + "child_account_address": self.child_account_address[2], + "personal_information": None, + "created": None, + "modified": None, + }, + { + "issuer_address": self.issuer_address, + "child_account_index": 5, + "child_account_address": self.child_account_address[4], + "personal_information": None, + "created": None, + "modified": None, + }, + { + "issuer_address": self.issuer_address, + "child_account_index": 1, + "child_account_address": self.child_account_address[0], + "personal_information": None, + "created": None, + "modified": None, + }, + { + "issuer_address": self.issuer_address, + "child_account_index": 4, + "child_account_address": self.child_account_address[3], + "personal_information": None, + "created": None, + "modified": None, + }, + ], + } + + # + # Sort order + # - name + def test_normal_3_3(self, client, db): + # Prepare data + _account = Account() + _account.issuer_address = self.issuer_address + db.add(_account) + + for i in range(1, 4): + _child_account = ChildAccount() + _child_account.issuer_address = self.issuer_address + _child_account.child_account_index = i + _child_account.child_account_address = self.child_account_address[i - 1] + db.add(_child_account) + + _off_personal_info = IDXPersonalInfo() + _off_personal_info.issuer_address = self.issuer_address + _off_personal_info.account_address = self.child_account_address[i - 1] + _off_personal_info.personal_info = { + "key_manager": "SELF", + "name": f"name_test_{i}", + "postal_code": f"postal_code_test_{i}", + "address": f"address_test_{i}", + "email": f"email_test_{i}", + "birth": f"birth_test_{i}", + "is_corporate": False, + "tax_category": i, + } + _off_personal_info.data_source = PersonalInfoDataSource.OFF_CHAIN + _off_personal_info.created = datetime.datetime( + 2024, 10, 28, 0, 0, i, tzinfo=datetime.UTC + ) + _off_personal_info.modified = datetime.datetime( + 2024, 10, 28, 0, 0, i, tzinfo=datetime.UTC + ) + db.add(_off_personal_info) + + db.commit() + + # Call API + resp = client.get( + self.base_url.format(self.issuer_address), + params={"sort_item": "name", "sort_order": 1}, + ) + + # Assertion + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 3, "offset": None, "limit": None, "total": 3}, + "child_accounts": [ + { + "issuer_address": self.issuer_address, + "child_account_index": 3, + "child_account_address": self.child_account_address[2], + "personal_information": { + "key_manager": "SELF", + "name": "name_test_3", + "postal_code": "postal_code_test_3", + "address": "address_test_3", + "email": "email_test_3", + "birth": "birth_test_3", + "is_corporate": False, + "tax_category": 3, + }, + "created": "2024-10-28T09:00:03+09:00", + "modified": "2024-10-28T09:00:03+09:00", + }, + { + "issuer_address": self.issuer_address, + "child_account_index": 2, + "child_account_address": self.child_account_address[1], + "personal_information": { + "key_manager": "SELF", + "name": "name_test_2", + "postal_code": "postal_code_test_2", + "address": "address_test_2", + "email": "email_test_2", + "birth": "birth_test_2", + "is_corporate": False, + "tax_category": 2, + }, + "created": "2024-10-28T09:00:02+09:00", + "modified": "2024-10-28T09:00:02+09:00", + }, + { + "issuer_address": self.issuer_address, + "child_account_index": 1, + "child_account_address": self.child_account_address[0], + "personal_information": { + "key_manager": "SELF", + "name": "name_test_1", + "postal_code": "postal_code_test_1", + "address": "address_test_1", + "email": "email_test_1", + "birth": "birth_test_1", + "is_corporate": False, + "tax_category": 1, + }, + "created": "2024-10-28T09:00:01+09:00", + "modified": "2024-10-28T09:00:01+09:00", + }, + ], + } + + # + # Sort order + # - created + def test_normal_3_4(self, client, db): + # Prepare data + _account = Account() + _account.issuer_address = self.issuer_address + db.add(_account) + + for i in range(1, 4): + _child_account = ChildAccount() + _child_account.issuer_address = self.issuer_address + _child_account.child_account_index = i + _child_account.child_account_address = self.child_account_address[i - 1] + db.add(_child_account) + + _off_personal_info = IDXPersonalInfo() + _off_personal_info.issuer_address = self.issuer_address + _off_personal_info.account_address = self.child_account_address[i - 1] + _off_personal_info.personal_info = { + "key_manager": "SELF", + "name": f"name_test_{i}", + "postal_code": f"postal_code_test_{i}", + "address": f"address_test_{i}", + "email": f"email_test_{i}", + "birth": f"birth_test_{i}", + "is_corporate": False, + "tax_category": i, + } + _off_personal_info.data_source = PersonalInfoDataSource.OFF_CHAIN + _off_personal_info.created = datetime.datetime( + 2024, 10, 28, 0, 0, i, tzinfo=datetime.UTC + ) + _off_personal_info.modified = datetime.datetime( + 2024, 10, 28, 0, 0, i, tzinfo=datetime.UTC + ) + db.add(_off_personal_info) + + db.commit() + + # Call API + resp = client.get( + self.base_url.format(self.issuer_address), + params={"sort_item": "created", "sort_order": 1}, + ) + + # Assertion + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 3, "offset": None, "limit": None, "total": 3}, + "child_accounts": [ + { + "issuer_address": self.issuer_address, + "child_account_index": 3, + "child_account_address": self.child_account_address[2], + "personal_information": { + "key_manager": "SELF", + "name": "name_test_3", + "postal_code": "postal_code_test_3", + "address": "address_test_3", + "email": "email_test_3", + "birth": "birth_test_3", + "is_corporate": False, + "tax_category": 3, + }, + "created": "2024-10-28T09:00:03+09:00", + "modified": "2024-10-28T09:00:03+09:00", + }, + { + "issuer_address": self.issuer_address, + "child_account_index": 2, + "child_account_address": self.child_account_address[1], + "personal_information": { + "key_manager": "SELF", + "name": "name_test_2", + "postal_code": "postal_code_test_2", + "address": "address_test_2", + "email": "email_test_2", + "birth": "birth_test_2", + "is_corporate": False, + "tax_category": 2, + }, + "created": "2024-10-28T09:00:02+09:00", + "modified": "2024-10-28T09:00:02+09:00", + }, + { + "issuer_address": self.issuer_address, + "child_account_index": 1, + "child_account_address": self.child_account_address[0], + "personal_information": { + "key_manager": "SELF", + "name": "name_test_1", + "postal_code": "postal_code_test_1", + "address": "address_test_1", + "email": "email_test_1", + "birth": "birth_test_1", + "is_corporate": False, + "tax_category": 1, + }, + "created": "2024-10-28T09:00:01+09:00", + "modified": "2024-10-28T09:00:01+09:00", + }, + ], + } + + # + # Sort order + # - modified + def test_normal_3_5(self, client, db): + # Prepare data + _account = Account() + _account.issuer_address = self.issuer_address + db.add(_account) + + for i in range(1, 4): + _child_account = ChildAccount() + _child_account.issuer_address = self.issuer_address + _child_account.child_account_index = i + _child_account.child_account_address = self.child_account_address[i - 1] + db.add(_child_account) + + _off_personal_info = IDXPersonalInfo() + _off_personal_info.issuer_address = self.issuer_address + _off_personal_info.account_address = self.child_account_address[i - 1] + _off_personal_info.personal_info = { + "key_manager": "SELF", + "name": f"name_test_{i}", + "postal_code": f"postal_code_test_{i}", + "address": f"address_test_{i}", + "email": f"email_test_{i}", + "birth": f"birth_test_{i}", + "is_corporate": False, + "tax_category": i, + } + _off_personal_info.data_source = PersonalInfoDataSource.OFF_CHAIN + _off_personal_info.created = datetime.datetime( + 2024, 10, 28, 0, 0, i, tzinfo=datetime.UTC + ) + _off_personal_info.modified = datetime.datetime( + 2024, 10, 28, 0, 0, i, tzinfo=datetime.UTC + ) + db.add(_off_personal_info) + + db.commit() + + # Call API + resp = client.get( + self.base_url.format(self.issuer_address), + params={"sort_item": "modified", "sort_order": 1}, + ) + + # Assertion + assert resp.status_code == 200 + assert resp.json() == { + "result_set": {"count": 3, "offset": None, "limit": None, "total": 3}, + "child_accounts": [ + { + "issuer_address": self.issuer_address, + "child_account_index": 3, + "child_account_address": self.child_account_address[2], + "personal_information": { + "key_manager": "SELF", + "name": "name_test_3", + "postal_code": "postal_code_test_3", + "address": "address_test_3", + "email": "email_test_3", + "birth": "birth_test_3", + "is_corporate": False, + "tax_category": 3, + }, + "created": "2024-10-28T09:00:03+09:00", + "modified": "2024-10-28T09:00:03+09:00", + }, + { + "issuer_address": self.issuer_address, + "child_account_index": 2, + "child_account_address": self.child_account_address[1], + "personal_information": { + "key_manager": "SELF", + "name": "name_test_2", + "postal_code": "postal_code_test_2", + "address": "address_test_2", + "email": "email_test_2", + "birth": "birth_test_2", + "is_corporate": False, + "tax_category": 2, + }, + "created": "2024-10-28T09:00:02+09:00", + "modified": "2024-10-28T09:00:02+09:00", + }, + { + "issuer_address": self.issuer_address, + "child_account_index": 1, + "child_account_address": self.child_account_address[0], + "personal_information": { + "key_manager": "SELF", + "name": "name_test_1", + "postal_code": "postal_code_test_1", + "address": "address_test_1", + "email": "email_test_1", + "birth": "birth_test_1", + "is_corporate": False, + "tax_category": 1, + }, + "created": "2024-10-28T09:00:01+09:00", + "modified": "2024-10-28T09:00:01+09:00", }, ], } - # + # # Offset / Limit - def test_normal_3(self, client, db): + def test_normal_4(self, client, db): # Prepare data _account = Account() _account.issuer_address = self.issuer_address @@ -279,6 +1171,8 @@ def test_normal_3(self, client, db): "child_account_index": 3, "child_account_address": self.child_account_address[2], "personal_information": None, + "created": None, + "modified": None, }, ], } diff --git a/tests/app/test_accounts_RetrieveChildAccount.py b/tests/app/test_accounts_RetrieveChildAccount.py index 26cda50f..5ac794c2 100644 --- a/tests/app/test_accounts_RetrieveChildAccount.py +++ b/tests/app/test_accounts_RetrieveChildAccount.py @@ -17,6 +17,8 @@ SPDX-License-Identifier: Apache-2.0 """ +import pytest + from app.model.db import Account, ChildAccount, IDXPersonalInfo, PersonalInfoDataSource @@ -57,10 +59,13 @@ def test_normal_1_1(self, client, db): "child_account_index": 1, "child_account_address": self.child_account_address, "personal_information": None, + "created": None, + "modified": None, } # # Personal information is set + @pytest.mark.freeze_time("2024-10-28 12:34:56") def test_normal_1_2(self, client, db): # Prepare data _account = Account() @@ -110,6 +115,8 @@ def test_normal_1_2(self, client, db): "is_corporate": False, "tax_category": 10, }, + "created": "2024-10-28T21:34:56+09:00", + "modified": "2024-10-28T21:34:56+09:00", } ###########################################################################