diff --git a/aries_cloudagent/messaging/models/paginated_query.py b/aries_cloudagent/messaging/models/paginated_query.py index bc05b1448e..c6e6e99795 100644 --- a/aries_cloudagent/messaging/models/paginated_query.py +++ b/aries_cloudagent/messaging/models/paginated_query.py @@ -1,10 +1,12 @@ """Class for paginated query parameters.""" -from marshmallow import fields +from typing import Tuple -from aries_cloudagent.storage.base import DEFAULT_PAGE_SIZE, MAXIMUM_PAGE_SIZE +from aiohttp.web import BaseRequest +from marshmallow import fields from ...messaging.models.openapi import OpenAPISchema +from ...storage.base import DEFAULT_PAGE_SIZE, MAXIMUM_PAGE_SIZE class PaginatedQuerySchema(OpenAPISchema): @@ -29,3 +31,18 @@ class PaginatedQuerySchema(OpenAPISchema): metadata={"description": "Offset for pagination", "example": 0}, error_messages={"validator_failed": "Value must be 0 or greater"}, ) + + +def get_limit_offset(request: BaseRequest) -> Tuple[int, int]: + """Read the limit and offset query parameters from a request as ints, with defaults. + + Args: + request: aiohttp request object + + Returns: + A tuple of the limit and offset values + """ + + limit = int(request.query.get("limit", DEFAULT_PAGE_SIZE)) + offset = int(request.query.get("offset", 0)) + return limit, offset diff --git a/aries_cloudagent/multitenant/admin/routes.py b/aries_cloudagent/multitenant/admin/routes.py index 96b6a6653a..d4ce438db2 100644 --- a/aries_cloudagent/multitenant/admin/routes.py +++ b/aries_cloudagent/multitenant/admin/routes.py @@ -16,10 +16,9 @@ from ...core.profile import ProfileManagerProvider from ...messaging.models.base import BaseModelError from ...messaging.models.openapi import OpenAPISchema -from ...messaging.models.paginated_query import PaginatedQuerySchema +from ...messaging.models.paginated_query import PaginatedQuerySchema, get_limit_offset from ...messaging.valid import UUID4_EXAMPLE, JSONWebToken from ...multitenant.base import BaseMultitenantManager -from ...storage.base import DEFAULT_PAGE_SIZE from ...storage.error import StorageError, StorageNotFoundError from ...utils.endorsement_setup import attempt_auto_author_with_endorser_setup from ...utils.profiles import subwallet_type_not_same_as_base_wallet_raise_web_exception @@ -382,8 +381,7 @@ async def wallets_list(request: web.BaseRequest): if wallet_name: query["wallet_name"] = wallet_name - limit = int(request.query.get("limit", DEFAULT_PAGE_SIZE)) - offset = int(request.query.get("offset", 0)) + limit, offset = get_limit_offset(request) try: async with profile.session() as session: diff --git a/aries_cloudagent/protocols/connections/v1_0/routes.py b/aries_cloudagent/protocols/connections/v1_0/routes.py index e067b547af..c6cbe47ecd 100644 --- a/aries_cloudagent/protocols/connections/v1_0/routes.py +++ b/aries_cloudagent/protocols/connections/v1_0/routes.py @@ -19,6 +19,7 @@ from ....connections.models.conn_record import ConnRecord, ConnRecordSchema from ....messaging.models.base import BaseModelError from ....messaging.models.openapi import OpenAPISchema +from ....messaging.models.paginated_query import PaginatedQuerySchema, get_limit_offset from ....messaging.valid import ( ENDPOINT_EXAMPLE, ENDPOINT_VALIDATE, @@ -236,7 +237,7 @@ class ConnectionStaticResultSchema(OpenAPISchema): record = fields.Nested(ConnRecordSchema(), required=True) -class ConnectionsListQueryStringSchema(OpenAPISchema): +class ConnectionsListQueryStringSchema(PaginatedQuerySchema): """Parameters and validators for connections list request query string.""" alias = fields.Str( @@ -468,11 +469,18 @@ async def connections_list(request: web.BaseRequest): if request.query.get("connection_protocol"): post_filter["connection_protocol"] = request.query["connection_protocol"] + limit, offset = get_limit_offset(request) + profile = context.profile try: async with profile.session() as session: records = await ConnRecord.query( - session, tag_filter, post_filter_positive=post_filter, alt=True + session, + tag_filter, + limit=limit, + offset=offset, + post_filter_positive=post_filter, + alt=True, ) results = [record.serialize() for record in records] results.sort(key=connection_sort_key) diff --git a/aries_cloudagent/protocols/connections/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/connections/v1_0/tests/test_routes.py index d561e8f0a0..d56494d5bd 100644 --- a/aries_cloudagent/protocols/connections/v1_0/tests/test_routes.py +++ b/aries_cloudagent/protocols/connections/v1_0/tests/test_routes.py @@ -103,6 +103,8 @@ async def test_connections_list(self): "their_public_did": "a_public_did", "invitation_msg_id": "dummy_msg", }, + limit=100, + offset=0, post_filter_positive={ "their_role": list(ConnRecord.Role.REQUESTER.value), "connection_protocol": "connections/1.0", diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/routes.py b/aries_cloudagent/protocols/issue_credential/v1_0/routes.py index e05e039bea..67cad773a7 100644 --- a/aries_cloudagent/protocols/issue_credential/v1_0/routes.py +++ b/aries_cloudagent/protocols/issue_credential/v1_0/routes.py @@ -22,6 +22,7 @@ from ....messaging.credential_definitions.util import CRED_DEF_TAGS from ....messaging.models.base import BaseModelError from ....messaging.models.openapi import OpenAPISchema +from ....messaging.models.paginated_query import PaginatedQuerySchema, get_limit_offset from ....messaging.valid import ( INDY_CRED_DEF_ID_EXAMPLE, INDY_CRED_DEF_ID_VALIDATE, @@ -57,7 +58,7 @@ class IssueCredentialModuleResponseSchema(OpenAPISchema): """Response schema for Issue Credential Module.""" -class V10CredentialExchangeListQueryStringSchema(OpenAPISchema): +class V10CredentialExchangeListQueryStringSchema(PaginatedQuerySchema): """Parameters and validators for credential exchange list query.""" connection_id = fields.Str( @@ -403,11 +404,15 @@ async def credential_exchange_list(request: web.BaseRequest): if request.query.get(k, "") != "" } + limit, offset = get_limit_offset(request) + try: async with context.profile.session() as session: records = await V10CredentialExchange.query( session=session, tag_filter=tag_filter, + limit=limit, + offset=offset, post_filter_positive=post_filter, ) results = [record.serialize() for record in records] diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/routes.py b/aries_cloudagent/protocols/issue_credential/v2_0/routes.py index bf15b91df2..aecbb6778d 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/routes.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/routes.py @@ -26,6 +26,7 @@ from ....messaging.decorators.attach_decorator import AttachDecorator from ....messaging.models.base import BaseModelError from ....messaging.models.openapi import OpenAPISchema +from ....messaging.models.paginated_query import PaginatedQuerySchema, get_limit_offset from ....messaging.valid import ( INDY_CRED_DEF_ID_EXAMPLE, INDY_CRED_DEF_ID_VALIDATE, @@ -63,7 +64,7 @@ class V20IssueCredentialModuleResponseSchema(OpenAPISchema): """Response schema for v2.0 Issue Credential Module.""" -class V20CredExRecordListQueryStringSchema(OpenAPISchema): +class V20CredExRecordListQueryStringSchema(PaginatedQuerySchema): """Parameters and validators for credential exchange record list query.""" connection_id = fields.Str( @@ -566,11 +567,15 @@ async def credential_exchange_list(request: web.BaseRequest): if request.query.get(k, "") != "" } + limit, offset = get_limit_offset(request) + try: async with profile.session() as session: cred_ex_records = await V20CredExRecord.query( session=session, tag_filter=tag_filter, + limit=limit, + offset=offset, post_filter_positive=post_filter, ) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/routes.py b/aries_cloudagent/protocols/present_proof/v1_0/routes.py index 3cf4ae38ee..1af0f985eb 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/routes.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/routes.py @@ -25,6 +25,7 @@ from ....messaging.decorators.attach_decorator import AttachDecorator from ....messaging.models.base import BaseModelError from ....messaging.models.openapi import OpenAPISchema +from ....messaging.models.paginated_query import PaginatedQuerySchema, get_limit_offset from ....messaging.valid import ( INDY_EXTRA_WQL_EXAMPLE, INDY_EXTRA_WQL_VALIDATE, @@ -54,7 +55,7 @@ class V10PresentProofModuleResponseSchema(OpenAPISchema): """Response schema for Present Proof Module.""" -class V10PresentationExchangeListQueryStringSchema(OpenAPISchema): +class V10PresentationExchangeListQueryStringSchema(PaginatedQuerySchema): """Parameters and validators for presentation exchange list query.""" connection_id = fields.Str( @@ -310,11 +311,15 @@ async def presentation_exchange_list(request: web.BaseRequest): if request.query.get(k, "") != "" } + limit, offset = get_limit_offset(request) + try: async with context.profile.session() as session: records = await V10PresentationExchange.query( session=session, tag_filter=tag_filter, + limit=limit, + offset=offset, post_filter_positive=post_filter, ) results = [record.serialize() for record in records] diff --git a/aries_cloudagent/protocols/present_proof/v2_0/routes.py b/aries_cloudagent/protocols/present_proof/v2_0/routes.py index 943494522d..cb0f23fc2e 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/routes.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/routes.py @@ -26,6 +26,7 @@ from ....messaging.decorators.attach_decorator import AttachDecorator from ....messaging.models.base import BaseModelError from ....messaging.models.openapi import OpenAPISchema +from ....messaging.models.paginated_query import PaginatedQuerySchema, get_limit_offset from ....messaging.valid import ( INDY_EXTRA_WQL_EXAMPLE, INDY_EXTRA_WQL_VALIDATE, @@ -70,7 +71,7 @@ class V20PresentProofModuleResponseSchema(OpenAPISchema): """Response schema for Present Proof Module.""" -class V20PresExRecordListQueryStringSchema(OpenAPISchema): +class V20PresExRecordListQueryStringSchema(PaginatedQuerySchema): """Parameters and validators for presentation exchange list query.""" connection_id = fields.Str( @@ -448,11 +449,15 @@ async def present_proof_list(request: web.BaseRequest): if request.query.get(k, "") != "" } + limit, offset = get_limit_offset(request) + try: async with profile.session() as session: records = await V20PresExRecord.query( session=session, tag_filter=tag_filter, + limit=limit, + offset=offset, post_filter_positive=post_filter, ) results = [record.serialize() for record in records]