Skip to content

presentation.routers.api.v1.accounts

src.presentation.routers.api.v1.accounts

Accounts resource handlers.

Handler functions for account management endpoints. Routes are registered via ROUTE_REGISTRY in routes/registry.py.

Handlers

list_accounts - List all accounts for user get_account - Get account details sync_accounts - Sync accounts from provider list_accounts_by_connection - List accounts for a connection

Reference
  • docs/architecture/api-design-patterns.md
  • docs/architecture/error-handling-architecture.md

Classes

Functions

list_accounts async

list_accounts(
    request: Request,
    current_user: AuthenticatedUser,
    active_only: Annotated[
        bool,
        Query(description="Only return active accounts"),
    ] = False,
    account_type: Annotated[
        str | None,
        Query(
            description="Filter by account type (e.g., brokerage, ira)"
        ),
    ] = None,
    handler: ListAccountsByUserHandler = Depends(
        handler_factory(ListAccountsByUserHandler)
    ),
) -> AccountListResponse | JSONResponse

List all accounts for the authenticated user.

GET /api/v1/accounts → 200 OK

Parameters:

Name Type Description Default
request Request

FastAPI request object.

required
current_user AuthenticatedUser

Authenticated user (from JWT).

required
active_only Annotated[bool, Query(description='Only return active accounts')]

Filter to only active accounts.

False
account_type Annotated[str | None, Query(description='Filter by account type (e.g., brokerage, ira)')]

Filter by account type.

None
handler ListAccountsByUserHandler

List accounts handler (injected).

Depends(handler_factory(ListAccountsByUserHandler))

Returns:

Type Description
AccountListResponse | JSONResponse

AccountListResponse with list of accounts.

AccountListResponse | JSONResponse

JSONResponse with RFC 9457 error on failure.

Source code in src/presentation/routers/api/v1/accounts.py
async def list_accounts(
    request: Request,
    current_user: AuthenticatedUser,
    active_only: Annotated[
        bool,
        Query(description="Only return active accounts"),
    ] = False,
    account_type: Annotated[
        str | None,
        Query(description="Filter by account type (e.g., brokerage, ira)"),
    ] = None,
    handler: ListAccountsByUserHandler = Depends(
        handler_factory(ListAccountsByUserHandler)
    ),
) -> AccountListResponse | JSONResponse:
    """List all accounts for the authenticated user.

    GET /api/v1/accounts → 200 OK

    Args:
        request: FastAPI request object.
        current_user: Authenticated user (from JWT).
        active_only: Filter to only active accounts.
        account_type: Filter by account type.
        handler: List accounts handler (injected).

    Returns:
        AccountListResponse with list of accounts.
        JSONResponse with RFC 9457 error on failure.
    """
    # Convert string to AccountType enum if provided
    account_type_enum: AccountType | None = None
    if account_type:
        try:
            account_type_enum = AccountType(account_type)
        except ValueError:
            # Invalid account type - will return empty list
            pass

    query = ListAccountsByUser(
        user_id=current_user.user_id,
        active_only=active_only,
        account_type=account_type_enum,
    )
    result = await handler.handle(query)

    if isinstance(result, Failure):
        app_error = _map_account_error(result.error)
        return ErrorResponseBuilder.from_application_error(
            error=app_error,
            request=request,
            trace_id=get_trace_id() or "",
        )

    return AccountListResponse.from_dto(result.value)

get_account async

get_account(
    request: Request,
    current_user: AuthenticatedUser,
    account_id: Annotated[
        UUID, Path(description="Account UUID")
    ],
    handler: GetAccountHandler = Depends(
        handler_factory(GetAccountHandler)
    ),
) -> AccountResponse | JSONResponse

Get a specific account.

GET /api/v1/accounts/{id} → 200 OK

Parameters:

Name Type Description Default
request Request

FastAPI request object.

required
current_user AuthenticatedUser

Authenticated user (from JWT).

required
account_id Annotated[UUID, Path(description='Account UUID')]

Account UUID.

required
handler GetAccountHandler

Get account handler (injected).

Depends(handler_factory(GetAccountHandler))

Returns:

Type Description
AccountResponse | JSONResponse

AccountResponse with account details.

AccountResponse | JSONResponse

JSONResponse with RFC 9457 error on failure.

Source code in src/presentation/routers/api/v1/accounts.py
async def get_account(
    request: Request,
    current_user: AuthenticatedUser,
    account_id: Annotated[UUID, Path(description="Account UUID")],
    handler: GetAccountHandler = Depends(handler_factory(GetAccountHandler)),
) -> AccountResponse | JSONResponse:
    """Get a specific account.

    GET /api/v1/accounts/{id} → 200 OK

    Args:
        request: FastAPI request object.
        current_user: Authenticated user (from JWT).
        account_id: Account UUID.
        handler: Get account handler (injected).

    Returns:
        AccountResponse with account details.
        JSONResponse with RFC 9457 error on failure.
    """
    query = GetAccount(
        account_id=account_id,
        user_id=current_user.user_id,
    )
    result = await handler.handle(query)

    if isinstance(result, Failure):
        app_error = _map_account_error(result.error)
        return ErrorResponseBuilder.from_application_error(
            error=app_error,
            request=request,
            trace_id=get_trace_id() or "",
        )

    return AccountResponse.from_dto(result.value)

sync_accounts async

sync_accounts(
    request: Request,
    current_user: AuthenticatedUser,
    data: SyncAccountsRequest,
    handler: SyncAccountsHandler = Depends(
        handler_factory(SyncAccountsHandler)
    ),
) -> SyncAccountsResponse | JSONResponse

Sync accounts from a provider connection.

POST /api/v1/accounts/syncs → 201 Created

Fetches accounts from the provider and upserts them to the database. Returns sync statistics (created/updated counts).

Parameters:

Name Type Description Default
request Request

FastAPI request object.

required
current_user AuthenticatedUser

Authenticated user (from JWT).

required
data SyncAccountsRequest

Sync request with connection_id and force flag.

required
handler SyncAccountsHandler

Sync accounts handler (injected).

Depends(handler_factory(SyncAccountsHandler))

Returns:

Type Description
SyncAccountsResponse | JSONResponse

SyncAccountsResponse with sync statistics.

SyncAccountsResponse | JSONResponse

JSONResponse with RFC 9457 error on failure.

Source code in src/presentation/routers/api/v1/accounts.py
async def sync_accounts(
    request: Request,
    current_user: AuthenticatedUser,
    data: SyncAccountsRequest,
    handler: SyncAccountsHandler = Depends(handler_factory(SyncAccountsHandler)),
) -> SyncAccountsResponse | JSONResponse:
    """Sync accounts from a provider connection.

    POST /api/v1/accounts/syncs → 201 Created

    Fetches accounts from the provider and upserts them to the database.
    Returns sync statistics (created/updated counts).

    Args:
        request: FastAPI request object.
        current_user: Authenticated user (from JWT).
        data: Sync request with connection_id and force flag.
        handler: Sync accounts handler (injected).

    Returns:
        SyncAccountsResponse with sync statistics.
        JSONResponse with RFC 9457 error on failure.
    """
    command = SyncAccounts(
        user_id=current_user.user_id,
        connection_id=data.connection_id,
        force=data.force,
    )
    result = await handler.handle(command)

    if isinstance(result, Failure):
        app_error = _map_account_error(result.error)
        return ErrorResponseBuilder.from_application_error(
            error=app_error,
            request=request,
            trace_id=get_trace_id() or "",
        )

    # Convert handler result to response
    sync_result = result.value
    return SyncAccountsResponse(
        created=sync_result.created,
        updated=sync_result.updated,
        unchanged=sync_result.unchanged,
        errors=sync_result.errors,
        message=sync_result.message,
        accounts_created=sync_result.created,
        accounts_updated=sync_result.updated,
    )

list_accounts_by_connection async

list_accounts_by_connection(
    request: Request,
    current_user: AuthenticatedUser,
    connection_id: Annotated[
        UUID, Path(description="Provider connection UUID")
    ],
    active_only: Annotated[
        bool,
        Query(description="Only return active accounts"),
    ] = False,
    handler: ListAccountsByConnectionHandler = Depends(
        handler_factory(ListAccountsByConnectionHandler)
    ),
) -> AccountListResponse | JSONResponse

List accounts for a specific provider connection.

GET /api/v1/providers/{id}/accounts → 200 OK

Parameters:

Name Type Description Default
request Request

FastAPI request object.

required
current_user AuthenticatedUser

Authenticated user (from JWT).

required
connection_id Annotated[UUID, Path(description='Provider connection UUID')]

Provider connection UUID.

required
active_only Annotated[bool, Query(description='Only return active accounts')]

Filter to only active accounts.

False
handler ListAccountsByConnectionHandler

List accounts by connection handler (injected).

Depends(handler_factory(ListAccountsByConnectionHandler))

Returns:

Type Description
AccountListResponse | JSONResponse

AccountListResponse with list of accounts.

AccountListResponse | JSONResponse

JSONResponse with RFC 9457 error on failure.

Source code in src/presentation/routers/api/v1/accounts.py
async def list_accounts_by_connection(
    request: Request,
    current_user: AuthenticatedUser,
    connection_id: Annotated[UUID, Path(description="Provider connection UUID")],
    active_only: Annotated[
        bool,
        Query(description="Only return active accounts"),
    ] = False,
    handler: ListAccountsByConnectionHandler = Depends(
        handler_factory(ListAccountsByConnectionHandler)
    ),
) -> AccountListResponse | JSONResponse:
    """List accounts for a specific provider connection.

    GET /api/v1/providers/{id}/accounts → 200 OK

    Args:
        request: FastAPI request object.
        current_user: Authenticated user (from JWT).
        connection_id: Provider connection UUID.
        active_only: Filter to only active accounts.
        handler: List accounts by connection handler (injected).

    Returns:
        AccountListResponse with list of accounts.
        JSONResponse with RFC 9457 error on failure.
    """
    query = ListAccountsByConnection(
        connection_id=connection_id,
        user_id=current_user.user_id,
        active_only=active_only,
    )
    result = await handler.handle(query)

    if isinstance(result, Failure):
        app_error = _map_account_error(result.error)
        return ErrorResponseBuilder.from_application_error(
            error=app_error,
            request=request,
            trace_id=get_trace_id() or "",
        )

    return AccountListResponse.from_dto(result.value)