From 3838eb8117acc17fdeb6eed3ae45d927316a7feb Mon Sep 17 00:00:00 2001 From: ff137 Date: Thu, 29 Aug 2024 12:15:10 +0300 Subject: [PATCH 01/12] :art: Modify count/start query params to be Integers, not Strings Signed-off-by: ff137 --- acapy_agent/holder/routes.py | 31 ++++++++++--------- .../protocols/present_proof/v1_0/routes.py | 31 +++++++++---------- .../protocols/present_proof/v2_0/routes.py | 31 +++++++++---------- 3 files changed, 45 insertions(+), 48 deletions(-) diff --git a/acapy_agent/holder/routes.py b/acapy_agent/holder/routes.py index 37c1d5e1e7..2d8001656c 100644 --- a/acapy_agent/holder/routes.py +++ b/acapy_agent/holder/routes.py @@ -27,11 +27,13 @@ ENDPOINT_VALIDATE, INDY_WQL_EXAMPLE, INDY_WQL_VALIDATE, - NUM_STR_NATURAL_EXAMPLE, - NUM_STR_NATURAL_VALIDATE, + NATURAL_NUM_EXAMPLE, + NATURAL_NUM_VALIDATE, NUM_STR_WHOLE_EXAMPLE, NUM_STR_WHOLE_VALIDATE, UUID4_EXAMPLE, + WHOLE_NUM_EXAMPLE, + WHOLE_NUM_VALIDATE, ) from ..storage.error import StorageError, StorageNotFoundError from ..storage.vc_holder.base import VCHolder @@ -64,17 +66,22 @@ class CredInfoListSchema(OpenAPISchema): class CredentialsListQueryStringSchema(OpenAPISchema): """Parameters and validators for query string in credentials list query.""" - start = fields.Str( + start = fields.Int( required=False, - validate=NUM_STR_WHOLE_VALIDATE, - metadata={"description": "Start index", "example": NUM_STR_WHOLE_EXAMPLE}, + load_default=0, + validate=WHOLE_NUM_VALIDATE, + metadata={ + "description": "Start index", + "example": WHOLE_NUM_EXAMPLE, + }, ) - count = fields.Str( + count = fields.Int( required=False, - validate=NUM_STR_NATURAL_VALIDATE, + load_default=10, + validate=NATURAL_NUM_VALIDATE, metadata={ "description": "Maximum number to retrieve", - "example": NUM_STR_NATURAL_EXAMPLE, + "example": NATURAL_NUM_EXAMPLE, }, ) wql = fields.Str( @@ -379,17 +386,13 @@ async def credentials_list(request: web.BaseRequest): """ context: AdminRequestContext = request["context"] - start = request.query.get("start") - count = request.query.get("count") + start = int(request.query.get("start", 0)) + count = int(request.query.get("count", 10)) # url encoded json wql encoded_wql = request.query.get("wql") or "{}" wql = json.loads(encoded_wql) - # defaults - start = int(start) if isinstance(start, str) else 0 - count = int(count) if isinstance(count, str) else 10 - if context.settings.get(wallet_type_config) == "askar-anoncreds": holder = AnonCredsHolder(context.profile) credentials = await holder.get_credentials(start, count, wql) diff --git a/acapy_agent/protocols/present_proof/v1_0/routes.py b/acapy_agent/protocols/present_proof/v1_0/routes.py index 97459a687e..cee88c5e9c 100644 --- a/acapy_agent/protocols/present_proof/v1_0/routes.py +++ b/acapy_agent/protocols/present_proof/v1_0/routes.py @@ -29,12 +29,12 @@ from ....messaging.valid import ( INDY_EXTRA_WQL_EXAMPLE, INDY_EXTRA_WQL_VALIDATE, - NUM_STR_NATURAL_EXAMPLE, - NUM_STR_NATURAL_VALIDATE, - NUM_STR_WHOLE_EXAMPLE, - NUM_STR_WHOLE_VALIDATE, + NATURAL_NUM_EXAMPLE, + NATURAL_NUM_VALIDATE, UUID4_EXAMPLE, UUID4_VALIDATE, + WHOLE_NUM_EXAMPLE, + WHOLE_NUM_VALIDATE, ) from ....storage.error import StorageError, StorageNotFoundError from ....utils.tracing import AdminAPIMessageTracingSchema, get_timer, trace_event @@ -235,21 +235,22 @@ class CredentialsFetchQueryStringSchema(OpenAPISchema): "example": "1_name_uuid,2_score_uuid", }, ) - start = fields.Str( + start = fields.Int( required=False, - validate=NUM_STR_WHOLE_VALIDATE, + load_default=0, + validate=WHOLE_NUM_VALIDATE, metadata={ "description": "Start index", - "strict": True, - "example": NUM_STR_WHOLE_EXAMPLE, + "example": WHOLE_NUM_EXAMPLE, }, ) - count = fields.Str( + count = fields.Int( required=False, - validate=NUM_STR_NATURAL_VALIDATE, + load_default=10, + validate=NATURAL_NUM_VALIDATE, metadata={ "description": "Maximum number to retrieve", - "example": NUM_STR_NATURAL_EXAMPLE, + "example": NATURAL_NUM_EXAMPLE, }, ) extra_query = fields.Str( @@ -413,17 +414,13 @@ async def presentation_exchange_credentials_list(request: web.BaseRequest): except StorageNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err - start = request.query.get("start") - count = request.query.get("count") + start = int(request.query.get("start", 0)) + count = int(request.query.get("count", 10)) # url encoded json extra_query encoded_extra_query = request.query.get("extra_query") or "{}" extra_query = json.loads(encoded_extra_query) - # defaults - start = int(start) if isinstance(start, str) else 0 - count = int(count) if isinstance(count, str) else 10 - holder = profile.inject(IndyHolder) try: credentials = await holder.get_credentials_for_presentation_request_by_referent( diff --git a/acapy_agent/protocols/present_proof/v2_0/routes.py b/acapy_agent/protocols/present_proof/v2_0/routes.py index 2edb0e4c08..853ed892f0 100644 --- a/acapy_agent/protocols/present_proof/v2_0/routes.py +++ b/acapy_agent/protocols/present_proof/v2_0/routes.py @@ -30,12 +30,12 @@ from ....messaging.valid import ( INDY_EXTRA_WQL_EXAMPLE, INDY_EXTRA_WQL_VALIDATE, - NUM_STR_NATURAL_EXAMPLE, - NUM_STR_NATURAL_VALIDATE, - NUM_STR_WHOLE_EXAMPLE, - NUM_STR_WHOLE_VALIDATE, + NATURAL_NUM_EXAMPLE, + NATURAL_NUM_VALIDATE, UUID4_EXAMPLE, UUID4_VALIDATE, + WHOLE_NUM_EXAMPLE, + WHOLE_NUM_VALIDATE, ) from ....storage.base import BaseStorage from ....storage.error import StorageError, StorageNotFoundError @@ -348,21 +348,22 @@ class V20CredentialsFetchQueryStringSchema(OpenAPISchema): "example": "1_name_uuid,2_score_uuid", }, ) - start = fields.Str( + start = fields.Int( required=False, - validate=NUM_STR_WHOLE_VALIDATE, + load_default=0, + validate=WHOLE_NUM_VALIDATE, metadata={ "description": "Start index", - "strict": True, - "example": NUM_STR_WHOLE_EXAMPLE, + "example": WHOLE_NUM_EXAMPLE, }, ) - count = fields.Str( + count = fields.Int( required=False, - validate=NUM_STR_NATURAL_VALIDATE, + load_default=10, + validate=NATURAL_NUM_VALIDATE, metadata={ "description": "Maximum number to retrieve", - "example": NUM_STR_NATURAL_EXAMPLE, + "example": NATURAL_NUM_EXAMPLE, }, ) extra_query = fields.Str( @@ -544,17 +545,13 @@ async def present_proof_credentials_list(request: web.BaseRequest): except StorageNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err - start = request.query.get("start") - count = request.query.get("count") + start = int(request.query.get("start", 0)) + count = int(request.query.get("count", 10)) # url encoded json extra_query encoded_extra_query = request.query.get("extra_query") or "{}" extra_query = json.loads(encoded_extra_query) - # defaults - start = int(start) if isinstance(start, str) else 0 - count = int(count) if isinstance(count, str) else 10 - wallet_type = profile.settings.get_value("wallet.type") if wallet_type == "askar-anoncreds": indy_holder = AnonCredsHolder(profile) From da28bc4a9672d25957bbbce157f0f22345194de9 Mon Sep 17 00:00:00 2001 From: ff137 Date: Thu, 29 Aug 2024 12:18:04 +0300 Subject: [PATCH 02/12] :memo: Update openapi spec Signed-off-by: ff137 --- open-api/openapi.json | 40 ++++++++++++++++++++++++---------------- open-api/swagger.json | 40 ++++++++++++++++++++++++---------------- 2 files changed, 48 insertions(+), 32 deletions(-) diff --git a/open-api/openapi.json b/open-api/openapi.json index 444c97fbbe..ead3da68b1 100644 --- a/open-api/openapi.json +++ b/open-api/openapi.json @@ -2058,16 +2058,18 @@ "in" : "query", "name" : "count", "schema" : { - "pattern" : "^[1-9][0-9]*$", - "type" : "string" + "default" : 10, + "minimum" : 1, + "type" : "integer" } }, { "description" : "Start index", "in" : "query", "name" : "start", "schema" : { - "pattern" : "^[0-9]*$", - "type" : "string" + "default" : 0, + "minimum" : 0, + "type" : "integer" } }, { "description" : "(JSON) WQL query", @@ -2101,16 +2103,18 @@ "in" : "query", "name" : "count", "schema" : { - "pattern" : "^[1-9][0-9]*$", - "type" : "string" + "default" : 10, + "minimum" : 1, + "type" : "integer" } }, { "description" : "Start index", "in" : "query", "name" : "start", "schema" : { - "pattern" : "^[0-9]*$", - "type" : "string" + "default" : 0, + "minimum" : 0, + "type" : "integer" } }, { "description" : "(JSON) WQL query", @@ -4927,8 +4931,9 @@ "in" : "query", "name" : "count", "schema" : { - "pattern" : "^[1-9][0-9]*$", - "type" : "string" + "default" : 10, + "minimum" : 1, + "type" : "integer" } }, { "description" : "(JSON) object mapping referents to extra WQL queries", @@ -4950,8 +4955,9 @@ "in" : "query", "name" : "start", "schema" : { - "pattern" : "^[0-9]*$", - "type" : "string" + "default" : 0, + "minimum" : 0, + "type" : "integer" } } ], "responses" : { @@ -5345,8 +5351,9 @@ "in" : "query", "name" : "count", "schema" : { - "pattern" : "^[1-9][0-9]*$", - "type" : "string" + "default" : 10, + "minimum" : 1, + "type" : "integer" } }, { "description" : "(JSON) object mapping referents to extra WQL queries", @@ -5368,8 +5375,9 @@ "in" : "query", "name" : "start", "schema" : { - "pattern" : "^[0-9]*$", - "type" : "string" + "default" : 0, + "minimum" : 0, + "type" : "integer" } } ], "responses" : { diff --git a/open-api/swagger.json b/open-api/swagger.json index 3267ac846e..1bbb89732a 100644 --- a/open-api/swagger.json +++ b/open-api/swagger.json @@ -1720,15 +1720,17 @@ "in" : "query", "description" : "Maximum number to retrieve", "required" : false, - "type" : "string", - "pattern" : "^[1-9][0-9]*$" + "type" : "integer", + "default" : 10, + "minimum" : 1 }, { "name" : "start", "in" : "query", "description" : "Start index", "required" : false, - "type" : "string", - "pattern" : "^[0-9]*$" + "type" : "integer", + "default" : 0, + "minimum" : 0 }, { "name" : "wql", "in" : "query", @@ -1764,15 +1766,17 @@ "in" : "query", "description" : "Maximum number to retrieve", "required" : false, - "type" : "string", - "pattern" : "^[1-9][0-9]*$" + "type" : "integer", + "default" : 10, + "minimum" : 1 }, { "name" : "start", "in" : "query", "description" : "Start index", "required" : false, - "type" : "string", - "pattern" : "^[0-9]*$" + "type" : "integer", + "default" : 0, + "minimum" : 0 }, { "name" : "wql", "in" : "query", @@ -4052,8 +4056,9 @@ "in" : "query", "description" : "Maximum number to retrieve", "required" : false, - "type" : "string", - "pattern" : "^[1-9][0-9]*$" + "type" : "integer", + "default" : 10, + "minimum" : 1 }, { "name" : "extra_query", "in" : "query", @@ -4072,8 +4077,9 @@ "in" : "query", "description" : "Start index", "required" : false, - "type" : "string", - "pattern" : "^[0-9]*$" + "type" : "integer", + "default" : 0, + "minimum" : 0 } ], "responses" : { "200" : { @@ -4391,8 +4397,9 @@ "in" : "query", "description" : "Maximum number to retrieve", "required" : false, - "type" : "string", - "pattern" : "^[1-9][0-9]*$" + "type" : "integer", + "default" : 10, + "minimum" : 1 }, { "name" : "extra_query", "in" : "query", @@ -4411,8 +4418,9 @@ "in" : "query", "description" : "Start index", "required" : false, - "type" : "string", - "pattern" : "^[0-9]*$" + "type" : "integer", + "default" : 0, + "minimum" : 0 } ], "deprecated" : true, "responses" : { From 299e963f3d8e0da78d5e915b5265c5a55cea1a97 Mon Sep 17 00:00:00 2001 From: ff137 Date: Tue, 12 Nov 2024 15:18:23 +0200 Subject: [PATCH 03/12] :rewind: Revert start/count field to remain String type; mark as deprecated Signed-off-by: ff137 --- acapy_agent/holder/routes.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/acapy_agent/holder/routes.py b/acapy_agent/holder/routes.py index 2d8001656c..e8e52c3ae2 100644 --- a/acapy_agent/holder/routes.py +++ b/acapy_agent/holder/routes.py @@ -27,13 +27,11 @@ ENDPOINT_VALIDATE, INDY_WQL_EXAMPLE, INDY_WQL_VALIDATE, - NATURAL_NUM_EXAMPLE, - NATURAL_NUM_VALIDATE, + NUM_STR_NATURAL_EXAMPLE, + NUM_STR_NATURAL_VALIDATE, NUM_STR_WHOLE_EXAMPLE, NUM_STR_WHOLE_VALIDATE, UUID4_EXAMPLE, - WHOLE_NUM_EXAMPLE, - WHOLE_NUM_VALIDATE, ) from ..storage.error import StorageError, StorageNotFoundError from ..storage.vc_holder.base import VCHolder @@ -66,22 +64,24 @@ class CredInfoListSchema(OpenAPISchema): class CredentialsListQueryStringSchema(OpenAPISchema): """Parameters and validators for query string in credentials list query.""" - start = fields.Int( + start = fields.Str( required=False, load_default=0, - validate=WHOLE_NUM_VALIDATE, + validate=NUM_STR_WHOLE_VALIDATE, metadata={ - "description": "Start index", - "example": WHOLE_NUM_EXAMPLE, + "description": "Start index (DEPRECATED - use offset instead)", + "example": NUM_STR_WHOLE_EXAMPLE, + "deprecated": True, }, ) - count = fields.Int( + count = fields.Str( required=False, load_default=10, - validate=NATURAL_NUM_VALIDATE, + validate=NUM_STR_NATURAL_VALIDATE, metadata={ - "description": "Maximum number to retrieve", - "example": NATURAL_NUM_EXAMPLE, + "description": "Maximum number to retrieve (DEPRECATED - use limit instead)", + "example": NUM_STR_NATURAL_EXAMPLE, + "deprecated": True, }, ) wql = fields.Str( From 56c6cdf0cfbc43e8b688efb021d45fdb768eda54 Mon Sep 17 00:00:00 2001 From: ff137 Date: Tue, 12 Nov 2024 15:18:59 +0200 Subject: [PATCH 04/12] :sparkles: Implement limit/offset alongside deprecated count/start Signed-off-by: ff137 --- acapy_agent/holder/routes.py | 37 ++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/acapy_agent/holder/routes.py b/acapy_agent/holder/routes.py index e8e52c3ae2..493c44b1c1 100644 --- a/acapy_agent/holder/routes.py +++ b/acapy_agent/holder/routes.py @@ -33,6 +33,7 @@ NUM_STR_WHOLE_VALIDATE, UUID4_EXAMPLE, ) +from ..storage.base import DEFAULT_PAGE_SIZE, MAXIMUM_PAGE_SIZE from ..storage.error import StorageError, StorageNotFoundError from ..storage.vc_holder.base import VCHolder from ..storage.vc_holder.vc_record import VCRecordSchema @@ -84,6 +85,23 @@ class CredentialsListQueryStringSchema(OpenAPISchema): "deprecated": True, }, ) + limit = fields.Int( + required=False, + validate=lambda x: x > 0 and x <= MAXIMUM_PAGE_SIZE, + metadata={"description": "Number of results to return", "example": 50}, + error_messages={ + "validator_failed": ( + "Value must be greater than 0 and " + f"less than or equal to {MAXIMUM_PAGE_SIZE}" + ) + }, + ) + offset = fields.Int( + required=False, + validate=lambda x: x >= 0, + metadata={"description": "Offset for pagination", "example": 0}, + error_messages={"validator_failed": "Value must be 0 or greater"}, + ) wql = fields.Str( required=False, validate=INDY_WQL_VALIDATE, @@ -386,8 +404,17 @@ async def credentials_list(request: web.BaseRequest): """ context: AdminRequestContext = request["context"] - start = int(request.query.get("start", 0)) - count = int(request.query.get("count", 10)) + + # Handle both old style start/count and new limit/offset + # TODO: Remove start/count and swap to PaginatedQuerySchema and get_limit_offset + if "limit" in request.query or "offset" in request.query: + # New style - use limit/offset + limit = int(request.query.get("limit", DEFAULT_PAGE_SIZE)) + offset = int(request.query.get("offset", 0)) + else: + # Old style - use start/count + limit = int(request.query.get("count", "10")) + offset = int(request.query.get("start", "0")) # url encoded json wql encoded_wql = request.query.get("wql") or "{}" @@ -395,12 +422,14 @@ async def credentials_list(request: web.BaseRequest): if context.settings.get(wallet_type_config) == "askar-anoncreds": holder = AnonCredsHolder(context.profile) - credentials = await holder.get_credentials(start, count, wql) + credentials = await holder.get_credentials(limit=limit, offset=offset, wql=wql) else: async with context.profile.session() as session: holder = session.inject(IndyHolder) try: - credentials = await holder.get_credentials(start, count, wql) + credentials = await holder.get_credentials( + limit=limit, offset=offset, wql=wql + ) except IndyHolderError as err: raise web.HTTPBadRequest(reason=err.roll_up) from err From 37a6e281067ba25ccdc8242defb928018a3248be Mon Sep 17 00:00:00 2001 From: ff137 Date: Tue, 12 Nov 2024 15:26:44 +0200 Subject: [PATCH 05/12] :art: Standardise naming convention from count/start to limit/offset Signed-off-by: ff137 --- acapy_agent/anoncreds/holder.py | 23 ++++++++++--------- acapy_agent/anoncreds/tests/test_holder.py | 10 ++++---- acapy_agent/indy/credx/holder.py | 23 ++++++++++--------- .../indy/credx/tests/test_cred_issuance.py | 12 +++++----- .../protocols/present_proof/v2_0/routes.py | 6 ++--- 5 files changed, 38 insertions(+), 36 deletions(-) diff --git a/acapy_agent/anoncreds/holder.py b/acapy_agent/anoncreds/holder.py index 04a1a16fac..ae14e4be5d 100644 --- a/acapy_agent/anoncreds/holder.py +++ b/acapy_agent/anoncreds/holder.py @@ -379,12 +379,12 @@ async def store_credential_w3c( return credential_id - async def get_credentials(self, start: int, count: int, wql: dict): + async def get_credentials(self, *, offset: int, limit: int, wql: dict): """Get credentials stored in the wallet. Args: - start: Starting index - count: Number of records to return + offset: Starting index + limit: Number of records to return wql: wql query dict """ @@ -395,8 +395,8 @@ async def get_credentials(self, start: int, count: int, wql: dict): rows = self.profile.store.scan( category=CATEGORY_CREDENTIAL, tag_filter=wql, - offset=start, - limit=count, + offset=offset, + limit=limit, profile=self.profile.settings.get("wallet.askar_profile"), ) async for row in rows: @@ -413,8 +413,9 @@ async def get_credentials_for_presentation_request_by_referent( self, presentation_request: dict, referents: Sequence[str], - start: int, - count: int, + *, + offset: int, + limit: int, extra_query: Optional[dict] = None, ): """Get credentials stored in the wallet. @@ -422,8 +423,8 @@ async def get_credentials_for_presentation_request_by_referent( Args: presentation_request: Valid presentation request from issuer referents: Presentation request referents to use to search for creds - start: Starting index - count: Maximum number of records to return + offset: Starting index + limit: Maximum number of records to return extra_query: wql query dict """ @@ -466,8 +467,8 @@ async def get_credentials_for_presentation_request_by_referent( rows = self.profile.store.scan( category=CATEGORY_CREDENTIAL, tag_filter=tag_filter, - offset=start, - limit=count, + offset=offset, + limit=limit, profile=self.profile.settings.get("wallet.askar_profile"), ) async for row in rows: diff --git a/acapy_agent/anoncreds/tests/test_holder.py b/acapy_agent/anoncreds/tests/test_holder.py index ba69a7ebd7..6f8dae681d 100644 --- a/acapy_agent/anoncreds/tests/test_holder.py +++ b/acapy_agent/anoncreds/tests/test_holder.py @@ -397,7 +397,7 @@ async def test_store_credential_failed_trx(self, *_): async def test_get_credentials(self, _): async with self.profile.session() as session: await session.handle.insert(CATEGORY_CREDENTIAL, json.dumps(MOCK_CRED)) - result = await self.holder.get_credentials(0, 10, {}) + result = await self.holder.get_credentials(offset=0, limit=10, wql={}) assert isinstance(result, list) assert len(result) == 1 @@ -410,9 +410,9 @@ async def test_get_credentials_errors(self): ) with self.assertRaises(AnonCredsHolderError): - await self.holder.get_credentials(0, 10, {}) + await self.holder.get_credentials(offset=0, limit=10, wql={}) with self.assertRaises(AnonCredsHolderError): - await self.holder.get_credentials(0, 10, {}) + await self.holder.get_credentials(offset=0, limit=10, wql={}) async def test_get_credentials_for_presentation_request_by_referent(self): self.profile.store.scan = mock.Mock( @@ -432,13 +432,13 @@ async def test_get_credentials_for_presentation_request_by_referent(self): "restrictions": [{"schema_name": "MYCO Biomarker"}], } await self.holder.get_credentials_for_presentation_request_by_referent( - mock_pres_req, None, start=0, count=10 + mock_pres_req, None, offset=0, limit=10 ) # non-existent referent with self.assertRaises(AnonCredsHolderError): await self.holder.get_credentials_for_presentation_request_by_referent( - mock_pres_req, "not-found-ref", start=0, count=10 + mock_pres_req, "not-found-ref", offset=0, limit=10 ) @mock.patch.object(Credential, "load", return_value=MockCredential()) diff --git a/acapy_agent/indy/credx/holder.py b/acapy_agent/indy/credx/holder.py index 27ba7837e2..089f1620d1 100644 --- a/acapy_agent/indy/credx/holder.py +++ b/acapy_agent/indy/credx/holder.py @@ -236,12 +236,12 @@ async def store_credential( return credential_id - async def get_credentials(self, start: int, count: int, wql: dict): + async def get_credentials(self, *, offset: int, limit: int, wql: dict): """Get credentials stored in the wallet. Args: - start: Starting index - count: Number of records to return + offset: Starting index + limit: Number of records to return wql: wql query dict """ @@ -252,8 +252,8 @@ async def get_credentials(self, start: int, count: int, wql: dict): rows = self._profile.store.scan( category=CATEGORY_CREDENTIAL, tag_filter=wql, - offset=start, - limit=count, + offset=offset, + limit=limit, profile=self._profile.settings.get("wallet.askar_profile"), ) async for row in rows: @@ -270,8 +270,9 @@ async def get_credentials_for_presentation_request_by_referent( self, presentation_request: dict, referents: Sequence[str], - start: int, - count: int, + *, + offset: int, + limit: int, extra_query: Optional[dict] = None, ): """Get credentials stored in the wallet. @@ -279,8 +280,8 @@ async def get_credentials_for_presentation_request_by_referent( Args: presentation_request: Valid presentation request from issuer referents: Presentation request referents to use to search for creds - start: Starting index - count: Maximum number of records to return + offset: Starting index + limit: Maximum number of records to return extra_query: wql query dict """ @@ -323,8 +324,8 @@ async def get_credentials_for_presentation_request_by_referent( rows = self._profile.store.scan( category=CATEGORY_CREDENTIAL, tag_filter=tag_filter, - offset=start, - limit=count, + offset=offset, + limit=limit, profile=self._profile.settings.get("wallet.askar_profile"), ) async for row in rows: diff --git a/acapy_agent/indy/credx/tests/test_cred_issuance.py b/acapy_agent/indy/credx/tests/test_cred_issuance.py index 9b8c222dc2..b1ce0c7058 100644 --- a/acapy_agent/indy/credx/tests/test_cred_issuance.py +++ b/acapy_agent/indy/credx/tests/test_cred_issuance.py @@ -142,9 +142,9 @@ async def test_issue_store_non_rev(self): await self.holder.get_credentials_for_presentation_request_by_referent( PRES_REQ_NON_REV, None, - 0, - 10, - {}, + offset=0, + limit=10, + extra_query={}, ) ) assert pres_creds == [ @@ -257,9 +257,9 @@ async def test_issue_store_rev(self): await self.holder.get_credentials_for_presentation_request_by_referent( PRES_REQ_REV, None, - 0, - 10, - {}, + offset=0, + limit=10, + extra_query={}, ) ) assert pres_creds == [ diff --git a/acapy_agent/protocols/present_proof/v2_0/routes.py b/acapy_agent/protocols/present_proof/v2_0/routes.py index 853ed892f0..9d7e14fa0b 100644 --- a/acapy_agent/protocols/present_proof/v2_0/routes.py +++ b/acapy_agent/protocols/present_proof/v2_0/routes.py @@ -568,9 +568,9 @@ async def present_proof_credentials_list(request: web.BaseRequest): await indy_holder.get_credentials_for_presentation_request_by_referent( indy_pres_request, pres_referents, - start, - count, - extra_query, + offset=start, + limit=count, + extra_query=extra_query, ) ) From 7730ded9eb36547293ed4f510543c9be367887b0 Mon Sep 17 00:00:00 2001 From: ff137 Date: Tue, 12 Nov 2024 15:34:39 +0200 Subject: [PATCH 06/12] :rewind: Revert start/count field to remain String type; mark as deprecated Signed-off-by: ff137 --- .../protocols/present_proof/v1_0/routes.py | 31 ++++++++------- .../protocols/present_proof/v2_0/routes.py | 38 +++++++++---------- 2 files changed, 35 insertions(+), 34 deletions(-) diff --git a/acapy_agent/protocols/present_proof/v1_0/routes.py b/acapy_agent/protocols/present_proof/v1_0/routes.py index cee88c5e9c..5783a5df6d 100644 --- a/acapy_agent/protocols/present_proof/v1_0/routes.py +++ b/acapy_agent/protocols/present_proof/v1_0/routes.py @@ -29,12 +29,12 @@ from ....messaging.valid import ( INDY_EXTRA_WQL_EXAMPLE, INDY_EXTRA_WQL_VALIDATE, - NATURAL_NUM_EXAMPLE, - NATURAL_NUM_VALIDATE, + NUM_STR_NATURAL_EXAMPLE, + NUM_STR_NATURAL_VALIDATE, + NUM_STR_WHOLE_EXAMPLE, + NUM_STR_WHOLE_VALIDATE, UUID4_EXAMPLE, UUID4_VALIDATE, - WHOLE_NUM_EXAMPLE, - WHOLE_NUM_VALIDATE, ) from ....storage.error import StorageError, StorageNotFoundError from ....utils.tracing import AdminAPIMessageTracingSchema, get_timer, trace_event @@ -235,22 +235,25 @@ class CredentialsFetchQueryStringSchema(OpenAPISchema): "example": "1_name_uuid,2_score_uuid", }, ) - start = fields.Int( + start = fields.Str( required=False, - load_default=0, - validate=WHOLE_NUM_VALIDATE, + load_default="0", + validate=NUM_STR_WHOLE_VALIDATE, metadata={ - "description": "Start index", - "example": WHOLE_NUM_EXAMPLE, + "description": "Start index (DEPRECATED - use offset instead)", + "strict": True, + "example": NUM_STR_WHOLE_EXAMPLE, + "deprecated": True, }, ) - count = fields.Int( + count = fields.Str( required=False, - load_default=10, - validate=NATURAL_NUM_VALIDATE, + load_default="10", + validate=NUM_STR_NATURAL_VALIDATE, metadata={ - "description": "Maximum number to retrieve", - "example": NATURAL_NUM_EXAMPLE, + "description": "Maximum number to retrieve (DEPRECATED - use limit instead)", + "example": NUM_STR_NATURAL_EXAMPLE, + "deprecated": True, }, ) extra_query = fields.Str( diff --git a/acapy_agent/protocols/present_proof/v2_0/routes.py b/acapy_agent/protocols/present_proof/v2_0/routes.py index 9d7e14fa0b..73dbca305c 100644 --- a/acapy_agent/protocols/present_proof/v2_0/routes.py +++ b/acapy_agent/protocols/present_proof/v2_0/routes.py @@ -30,12 +30,12 @@ from ....messaging.valid import ( INDY_EXTRA_WQL_EXAMPLE, INDY_EXTRA_WQL_VALIDATE, - NATURAL_NUM_EXAMPLE, - NATURAL_NUM_VALIDATE, + NUM_STR_NATURAL_EXAMPLE, + NUM_STR_NATURAL_VALIDATE, + NUM_STR_WHOLE_EXAMPLE, + NUM_STR_WHOLE_VALIDATE, UUID4_EXAMPLE, UUID4_VALIDATE, - WHOLE_NUM_EXAMPLE, - WHOLE_NUM_VALIDATE, ) from ....storage.base import BaseStorage from ....storage.error import StorageError, StorageNotFoundError @@ -54,12 +54,7 @@ from . import problem_report_for_record, report_problem from .formats.handler import V20PresFormatHandlerError from .manager import V20PresManager -from .message_types import ( - ATTACHMENT_FORMAT, - PRES_20_PROPOSAL, - PRES_20_REQUEST, - SPEC_URI, -) +from .message_types import ATTACHMENT_FORMAT, PRES_20_PROPOSAL, PRES_20_REQUEST, SPEC_URI from .messages.pres_format import V20PresFormat from .messages.pres_problem_report import ProblemReportReason from .messages.pres_proposal import V20PresProposal @@ -348,22 +343,25 @@ class V20CredentialsFetchQueryStringSchema(OpenAPISchema): "example": "1_name_uuid,2_score_uuid", }, ) - start = fields.Int( + start = fields.Str( required=False, - load_default=0, - validate=WHOLE_NUM_VALIDATE, + load_default="0", + validate=NUM_STR_WHOLE_VALIDATE, metadata={ - "description": "Start index", - "example": WHOLE_NUM_EXAMPLE, + "description": "Start index (DEPRECATED - use offset instead)", + "strict": True, + "example": NUM_STR_WHOLE_EXAMPLE, + "deprecated": True, }, ) - count = fields.Int( + count = fields.Str( required=False, - load_default=10, - validate=NATURAL_NUM_VALIDATE, + load_default="10", + validate=NUM_STR_NATURAL_VALIDATE, metadata={ - "description": "Maximum number to retrieve", - "example": NATURAL_NUM_EXAMPLE, + "description": "Maximum number to retrieve (DEPRECATED - use limit instead)", + "example": NUM_STR_NATURAL_EXAMPLE, + "deprecated": True, }, ) extra_query = fields.Str( From 1753b0d86a1b7069e5592af839ed5a5ae7f5a3f8 Mon Sep 17 00:00:00 2001 From: ff137 Date: Tue, 12 Nov 2024 15:39:18 +0200 Subject: [PATCH 07/12] :sparkles: Implement limit/offset alongside deprecated count/start Signed-off-by: ff137 --- .../protocols/present_proof/v1_0/routes.py | 36 ++++++++++++++--- .../protocols/present_proof/v2_0/routes.py | 39 +++++++++++++++---- 2 files changed, 63 insertions(+), 12 deletions(-) diff --git a/acapy_agent/protocols/present_proof/v1_0/routes.py b/acapy_agent/protocols/present_proof/v1_0/routes.py index 5783a5df6d..7fb2eee48b 100644 --- a/acapy_agent/protocols/present_proof/v1_0/routes.py +++ b/acapy_agent/protocols/present_proof/v1_0/routes.py @@ -36,6 +36,7 @@ UUID4_EXAMPLE, UUID4_VALIDATE, ) +from ....storage.base import DEFAULT_PAGE_SIZE, MAXIMUM_PAGE_SIZE from ....storage.error import StorageError, StorageNotFoundError from ....utils.tracing import AdminAPIMessageTracingSchema, get_timer, trace_event from ....wallet.error import WalletNotFoundError @@ -256,6 +257,23 @@ class CredentialsFetchQueryStringSchema(OpenAPISchema): "deprecated": True, }, ) + limit = fields.Int( + required=False, + validate=lambda x: x > 0 and x <= MAXIMUM_PAGE_SIZE, + metadata={"description": "Number of results to return", "example": 50}, + error_messages={ + "validator_failed": ( + "Value must be greater than 0 and " + f"less than or equal to {MAXIMUM_PAGE_SIZE}" + ) + }, + ) + offset = fields.Int( + required=False, + validate=lambda x: x >= 0, + metadata={"description": "Offset for pagination", "example": 0}, + error_messages={"validator_failed": "Value must be 0 or greater"}, + ) extra_query = fields.Str( required=False, validate=INDY_EXTRA_WQL_VALIDATE, @@ -417,8 +435,16 @@ async def presentation_exchange_credentials_list(request: web.BaseRequest): except StorageNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err - start = int(request.query.get("start", 0)) - count = int(request.query.get("count", 10)) + # Handle both old style start/count and new limit/offset + # TODO: Remove start/count and swap to PaginatedQuerySchema and get_limit_offset + if "limit" in request.query or "offset" in request.query: + # New style - use limit/offset + limit = int(request.query.get("limit", DEFAULT_PAGE_SIZE)) + offset = int(request.query.get("offset", 0)) + else: + # Old style - use start/count + limit = int(request.query.get("count", "10")) + offset = int(request.query.get("start", "0")) # url encoded json extra_query encoded_extra_query = request.query.get("extra_query") or "{}" @@ -429,9 +455,9 @@ async def presentation_exchange_credentials_list(request: web.BaseRequest): credentials = await holder.get_credentials_for_presentation_request_by_referent( pres_ex_record._presentation_request.ser, presentation_referents, - start, - count, - extra_query, + offset=offset, + limit=limit, + extra_query=extra_query, ) except IndyHolderError as err: if pres_ex_record: diff --git a/acapy_agent/protocols/present_proof/v2_0/routes.py b/acapy_agent/protocols/present_proof/v2_0/routes.py index 73dbca305c..d69ddbf0f4 100644 --- a/acapy_agent/protocols/present_proof/v2_0/routes.py +++ b/acapy_agent/protocols/present_proof/v2_0/routes.py @@ -37,7 +37,7 @@ UUID4_EXAMPLE, UUID4_VALIDATE, ) -from ....storage.base import BaseStorage +from ....storage.base import DEFAULT_PAGE_SIZE, MAXIMUM_PAGE_SIZE, BaseStorage from ....storage.error import StorageError, StorageNotFoundError from ....storage.vc_holder.base import VCHolder from ....storage.vc_holder.vc_record import VCRecord @@ -364,6 +364,23 @@ class V20CredentialsFetchQueryStringSchema(OpenAPISchema): "deprecated": True, }, ) + limit = fields.Int( + required=False, + validate=lambda x: x > 0 and x <= MAXIMUM_PAGE_SIZE, + metadata={"description": "Number of results to return", "example": 50}, + error_messages={ + "validator_failed": ( + "Value must be greater than 0 and " + f"less than or equal to {MAXIMUM_PAGE_SIZE}" + ) + }, + ) + offset = fields.Int( + required=False, + validate=lambda x: x >= 0, + metadata={"description": "Offset for pagination", "example": 0}, + error_messages={"validator_failed": "Value must be 0 or greater"}, + ) extra_query = fields.Str( required=False, validate=INDY_EXTRA_WQL_VALIDATE, @@ -543,8 +560,16 @@ async def present_proof_credentials_list(request: web.BaseRequest): except StorageNotFoundError as err: raise web.HTTPNotFound(reason=err.roll_up) from err - start = int(request.query.get("start", 0)) - count = int(request.query.get("count", 10)) + # Handle both old style start/count and new limit/offset + # TODO: Remove start/count and swap to PaginatedQuerySchema and get_limit_offset + if "limit" in request.query or "offset" in request.query: + # New style - use limit/offset + limit = int(request.query.get("limit", DEFAULT_PAGE_SIZE)) + offset = int(request.query.get("offset", 0)) + else: + # Old style - use start/count + limit = int(request.query.get("count", "10")) + offset = int(request.query.get("start", "0")) # url encoded json extra_query encoded_extra_query = request.query.get("extra_query") or "{}" @@ -566,8 +591,8 @@ async def present_proof_credentials_list(request: web.BaseRequest): await indy_holder.get_credentials_for_presentation_request_by_referent( indy_pres_request, pres_referents, - offset=start, - limit=count, + offset=offset, + limit=limit, extra_query=extra_query, ) ) @@ -727,7 +752,7 @@ async def present_proof_credentials_list(request: web.BaseRequest): search = dif_holder.search_credentials( proof_types=proof_type, pd_uri_list=uri_group ) - cred_group = await search.fetch(count) + cred_group = await search.fetch(limit) ( cred_group_vcrecord_list, cred_group_vcrecord_ids_set, @@ -741,7 +766,7 @@ async def present_proof_credentials_list(request: web.BaseRequest): proof_types=proof_type, pd_uri_list=uri_list, ) - records = await search.fetch(count) + records = await search.fetch(limit) # Avoiding addition of duplicate records vcrecord_list, vcrecord_ids_set = await process_vcrecords_return_list( records, record_ids From 0e309d6069c98432ff128e25c2f41672b8acee2a Mon Sep 17 00:00:00 2001 From: ff137 Date: Tue, 12 Nov 2024 15:41:45 +0200 Subject: [PATCH 08/12] :art: Remove unused query string schema from `w3c_creds_list` Signed-off-by: ff137 --- acapy_agent/holder/routes.py | 1 - 1 file changed, 1 deletion(-) diff --git a/acapy_agent/holder/routes.py b/acapy_agent/holder/routes.py index 493c44b1c1..cda58ccc75 100644 --- a/acapy_agent/holder/routes.py +++ b/acapy_agent/holder/routes.py @@ -508,7 +508,6 @@ async def w3c_cred_remove(request: web.BaseRequest): summary="Fetch W3C credentials from wallet", ) @request_schema(W3CCredentialsListRequestSchema()) -@querystring_schema(CredentialsListQueryStringSchema()) @response_schema(VCRecordListSchema(), 200, description="") @tenant_authentication async def w3c_creds_list(request: web.BaseRequest): From 9bd5f30050af9d74833b6440d59993755e189687 Mon Sep 17 00:00:00 2001 From: ff137 Date: Tue, 12 Nov 2024 15:43:49 +0200 Subject: [PATCH 09/12] :art: Rename args Signed-off-by: ff137 --- acapy_agent/indy/models/xform.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/acapy_agent/indy/models/xform.py b/acapy_agent/indy/models/xform.py index 6e80794889..c8082ce576 100644 --- a/acapy_agent/indy/models/xform.py +++ b/acapy_agent/indy/models/xform.py @@ -34,8 +34,8 @@ async def indy_proof_req_preview2indy_requested_creds( credentials = await holder.get_credentials_for_presentation_request_by_referent( presentation_request=indy_proof_req, referents=(referent,), - start=0, - count=100, + offset=0, + limit=100, ) if not credentials: raise ValueError( @@ -87,8 +87,8 @@ async def indy_proof_req_preview2indy_requested_creds( credentials = await holder.get_credentials_for_presentation_request_by_referent( presentation_request=indy_proof_req, referents=(referent,), - start=0, - count=100, + offset=0, + limit=100, ) if not credentials: raise ValueError( From 94ab4d04d17f57f947517907244238bd5118e710 Mon Sep 17 00:00:00 2001 From: ff137 Date: Tue, 12 Nov 2024 19:55:55 +0200 Subject: [PATCH 10/12] :memo: Update openapi specs Signed-off-by: ff137 --- open-api/openapi.json | 147 +++++++++++++++++++++++------------------- open-api/swagger.json | 139 +++++++++++++++++++++------------------ 2 files changed, 157 insertions(+), 129 deletions(-) diff --git a/open-api/openapi.json b/open-api/openapi.json index ead3da68b1..398b1d02ef 100644 --- a/open-api/openapi.json +++ b/open-api/openapi.json @@ -2054,23 +2054,37 @@ "/credentials" : { "get" : { "parameters" : [ { - "description" : "Maximum number to retrieve", + "description" : "Maximum number to retrieve (DEPRECATED - use limit instead)", "in" : "query", "name" : "count", "schema" : { - "default" : 10, - "minimum" : 1, + "default" : "10", + "pattern" : "^[1-9][0-9]*$", + "type" : "string" + } + }, { + "description" : "Number of results to return", + "in" : "query", + "name" : "limit", + "schema" : { "type" : "integer" } }, { - "description" : "Start index", + "description" : "Offset for pagination", "in" : "query", - "name" : "start", + "name" : "offset", "schema" : { - "default" : 0, - "minimum" : 0, "type" : "integer" } + }, { + "description" : "Start index (DEPRECATED - use offset instead)", + "in" : "query", + "name" : "start", + "schema" : { + "default" : "0", + "pattern" : "^[0-9]*$", + "type" : "string" + } }, { "description" : "(JSON) WQL query", "in" : "query", @@ -2098,33 +2112,6 @@ }, "/credentials/w3c" : { "post" : { - "parameters" : [ { - "description" : "Maximum number to retrieve", - "in" : "query", - "name" : "count", - "schema" : { - "default" : 10, - "minimum" : 1, - "type" : "integer" - } - }, { - "description" : "Start index", - "in" : "query", - "name" : "start", - "schema" : { - "default" : 0, - "minimum" : 0, - "type" : "integer" - } - }, { - "description" : "(JSON) WQL query", - "in" : "query", - "name" : "wql", - "schema" : { - "pattern" : "^{.*}$", - "type" : "string" - } - } ], "requestBody" : { "content" : { "*/*" : { @@ -4927,13 +4914,13 @@ "type" : "string" } }, { - "description" : "Maximum number to retrieve", + "description" : "Maximum number to retrieve (DEPRECATED - use limit instead)", "in" : "query", "name" : "count", "schema" : { - "default" : 10, - "minimum" : 1, - "type" : "integer" + "default" : "10", + "pattern" : "^[1-9][0-9]*$", + "type" : "string" } }, { "description" : "(JSON) object mapping referents to extra WQL queries", @@ -4943,6 +4930,20 @@ "pattern" : "^{\\s*\".*?\"\\s*:\\s*{.*?}\\s*(,\\s*\".*?\"\\s*:\\s*{.*?}\\s*)*\\s*}$", "type" : "string" } + }, { + "description" : "Number of results to return", + "in" : "query", + "name" : "limit", + "schema" : { + "type" : "integer" + } + }, { + "description" : "Offset for pagination", + "in" : "query", + "name" : "offset", + "schema" : { + "type" : "integer" + } }, { "description" : "Proof request referents of interest, comma-separated", "in" : "query", @@ -4951,13 +4952,13 @@ "type" : "string" } }, { - "description" : "Start index", + "description" : "Start index (DEPRECATED - use offset instead)", "in" : "query", "name" : "start", "schema" : { - "default" : 0, - "minimum" : 0, - "type" : "integer" + "default" : "0", + "pattern" : "^[0-9]*$", + "type" : "string" } } ], "responses" : { @@ -5347,13 +5348,13 @@ "type" : "string" } }, { - "description" : "Maximum number to retrieve", + "description" : "Maximum number to retrieve (DEPRECATED - use limit instead)", "in" : "query", "name" : "count", "schema" : { - "default" : 10, - "minimum" : 1, - "type" : "integer" + "default" : "10", + "pattern" : "^[1-9][0-9]*$", + "type" : "string" } }, { "description" : "(JSON) object mapping referents to extra WQL queries", @@ -5363,6 +5364,20 @@ "pattern" : "^{\\s*\".*?\"\\s*:\\s*{.*?}\\s*(,\\s*\".*?\"\\s*:\\s*{.*?}\\s*)*\\s*}$", "type" : "string" } + }, { + "description" : "Number of results to return", + "in" : "query", + "name" : "limit", + "schema" : { + "type" : "integer" + } + }, { + "description" : "Offset for pagination", + "in" : "query", + "name" : "offset", + "schema" : { + "type" : "integer" + } }, { "description" : "Proof request referents of interest, comma-separated", "in" : "query", @@ -5371,13 +5386,13 @@ "type" : "string" } }, { - "description" : "Start index", + "description" : "Start index (DEPRECATED - use offset instead)", "in" : "query", "name" : "start", "schema" : { - "default" : 0, - "minimum" : 0, - "type" : "integer" + "default" : "0", + "pattern" : "^[0-9]*$", + "type" : "string" } } ], "responses" : { @@ -8384,12 +8399,12 @@ "type" : "string" }, "kid" : { - "description" : "Optional kid to bind to the keypair, such as a verificationMethod.", + "description" : "Optional kid to bind to the keypair, such as a verificationMethod.", "example" : "did:web:example.com#key-01", "type" : "string" }, "seed" : { - "description" : "Optional seed to generate the key pair. Must enable insecure wallet mode.", + "description" : "Optional seed to generate the key pair. Must enable insecure wallet mode.", "example" : "00000000000000000000000000000000", "type" : "string" } @@ -8458,7 +8473,7 @@ }, "wallet_type" : { "description" : "Type of the wallet to create. Must be same as base wallet.", - "enum" : [ "askar", "askar-anoncreds", "in_memory" ], + "enum" : [ "askar", "askar-anoncreds" ], "example" : "askar", "type" : "string" }, @@ -9330,7 +9345,7 @@ "type" : "string" }, "mediation_id" : { - "description" : "Medation ID to use for endpoint information.", + "description" : "Mediation ID to use for endpoint information.", "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}", "type" : "string" @@ -9561,47 +9576,47 @@ "additionalProperties" : true, "properties" : { "challenge" : { - "description" : "The value is used once for a particular domain and window of time. This value is used to mitigate replay attacks.", + "description" : "The value is used once for a particular domain and window of time. This value is used to mitigate replay attacks.", "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", "type" : "string" }, "created" : { - "description" : "The date and time the proof was created is OPTIONAL and, if included, MUST be specified as an [XMLSCHEMA11-2] dateTimeStamp string", + "description" : "The date and time the proof was created is OPTIONAL and, if included, MUST be specified as an [XMLSCHEMA11-2] dateTimeStamp string", "example" : "2010-01-01T19:23:24Z", "type" : "string" }, "cryptosuite" : { - "description" : "An identifier for the cryptographic suite that can be used to verify the proof.", + "description" : "An identifier for the cryptographic suite that can be used to verify the proof.", "example" : "eddsa-jcs-2022", "type" : "string" }, "domain" : { - "description" : "It conveys one or more security domains in which the proof is meant to be used.", + "description" : "It conveys one or more security domains in which the proof is meant to be used.", "example" : "example.com", "type" : "string" }, "expires" : { - "description" : "The expires property is OPTIONAL and, if present, specifies when the proof expires. If present, it MUST be an [XMLSCHEMA11-2] dateTimeStamp string", + "description" : "The expires property is OPTIONAL and, if present, specifies when the proof expires. If present, it MUST be an [XMLSCHEMA11-2] dateTimeStamp string", "example" : "2010-01-01T19:23:24Z", "type" : "string" }, "id" : { - "description" : "An optional identifier for the proof, which MUST be a URL [URL], such as a UUID as a URN", + "description" : "An optional identifier for the proof, which MUST be a URL [URL], such as a UUID as a URN", "example" : "urn:uuid:6a1676b8-b51f-11ed-937b-d76685a20ff5", "type" : "string" }, "nonce" : { - "description" : "One use of this field is to increase privacy by decreasing linkability that is the result of deterministically generated signatures.", + "description" : "One use of this field is to increase privacy by decreasing linkability that is the result of deterministically generated signatures.", "example" : "CF69iO3nfvqRsRBNElE8b4wO39SyJHPM7Gg1nExltW5vSfQA1lvDCR/zXX1To0/4NLo==", "type" : "string" }, "previousProof" : { - "description" : "Each value identifies another data integrity proof that MUST verify before the current proof is processed.", + "description" : "Each value identifies another data integrity proof that MUST verify before the current proof is processed.", "example" : "urn:uuid:6a1676b8-b51f-11ed-937b-d76685a20ff5", "type" : "string" }, "proofPurpose" : { - "description" : "The proof purpose acts as a safeguard to prevent the proof from being misused by being applied to a purpose other than the one that was intended.", + "description" : "The proof purpose acts as a safeguard to prevent the proof from being misused by being applied to a purpose other than the one that was intended.", "example" : "assertionMethod", "type" : "string" }, @@ -9611,12 +9626,12 @@ "type" : "string" }, "type" : { - "description" : "The specific type of proof MUST be specified as a string that maps to a URL [URL].", + "description" : "The specific type of proof MUST be specified as a string that maps to a URL [URL].", "example" : "DataIntegrityProof", "type" : "string" }, "verificationMethod" : { - "description" : "A verification method is the means and information needed to verify the proof. ", + "description" : "A verification method is the means and information needed to verify the proof.", "example" : "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", "pattern" : "\\w+:(\\/?\\/?)[^\\s]+", "type" : "string" @@ -14093,7 +14108,7 @@ "UpdateKeyRequest" : { "properties" : { "kid" : { - "description" : "New kid to bind to the key pair, such as a verificationMethod.", + "description" : "New kid to bind to the key pair, such as a verificationMethod.", "example" : "did:web:example.com#key-02", "type" : "string" }, diff --git a/open-api/swagger.json b/open-api/swagger.json index 1bbb89732a..b3f325664f 100644 --- a/open-api/swagger.json +++ b/open-api/swagger.json @@ -1718,19 +1718,31 @@ "parameters" : [ { "name" : "count", "in" : "query", - "description" : "Maximum number to retrieve", + "description" : "Maximum number to retrieve (DEPRECATED - use limit instead)", "required" : false, - "type" : "integer", - "default" : 10, - "minimum" : 1 + "type" : "string", + "default" : "10", + "pattern" : "^[1-9][0-9]*$" + }, { + "name" : "limit", + "in" : "query", + "description" : "Number of results to return", + "required" : false, + "type" : "integer" + }, { + "name" : "offset", + "in" : "query", + "description" : "Offset for pagination", + "required" : false, + "type" : "integer" }, { "name" : "start", "in" : "query", - "description" : "Start index", + "description" : "Start index (DEPRECATED - use offset instead)", "required" : false, - "type" : "integer", - "default" : 0, - "minimum" : 0 + "type" : "string", + "default" : "0", + "pattern" : "^[0-9]*$" }, { "name" : "wql", "in" : "query", @@ -1761,29 +1773,6 @@ "schema" : { "$ref" : "#/definitions/W3CCredentialsListRequest" } - }, { - "name" : "count", - "in" : "query", - "description" : "Maximum number to retrieve", - "required" : false, - "type" : "integer", - "default" : 10, - "minimum" : 1 - }, { - "name" : "start", - "in" : "query", - "description" : "Start index", - "required" : false, - "type" : "integer", - "default" : 0, - "minimum" : 0 - }, { - "name" : "wql", - "in" : "query", - "description" : "(JSON) WQL query", - "required" : false, - "type" : "string", - "pattern" : "^{.*}$" } ], "responses" : { "200" : { @@ -4054,11 +4043,11 @@ }, { "name" : "count", "in" : "query", - "description" : "Maximum number to retrieve", + "description" : "Maximum number to retrieve (DEPRECATED - use limit instead)", "required" : false, - "type" : "integer", - "default" : 10, - "minimum" : 1 + "type" : "string", + "default" : "10", + "pattern" : "^[1-9][0-9]*$" }, { "name" : "extra_query", "in" : "query", @@ -4066,6 +4055,18 @@ "required" : false, "type" : "string", "pattern" : "^{\\s*\".*?\"\\s*:\\s*{.*?}\\s*(,\\s*\".*?\"\\s*:\\s*{.*?}\\s*)*\\s*}$" + }, { + "name" : "limit", + "in" : "query", + "description" : "Number of results to return", + "required" : false, + "type" : "integer" + }, { + "name" : "offset", + "in" : "query", + "description" : "Offset for pagination", + "required" : false, + "type" : "integer" }, { "name" : "referent", "in" : "query", @@ -4075,11 +4076,11 @@ }, { "name" : "start", "in" : "query", - "description" : "Start index", + "description" : "Start index (DEPRECATED - use offset instead)", "required" : false, - "type" : "integer", - "default" : 0, - "minimum" : 0 + "type" : "string", + "default" : "0", + "pattern" : "^[0-9]*$" } ], "responses" : { "200" : { @@ -4395,11 +4396,11 @@ }, { "name" : "count", "in" : "query", - "description" : "Maximum number to retrieve", + "description" : "Maximum number to retrieve (DEPRECATED - use limit instead)", "required" : false, - "type" : "integer", - "default" : 10, - "minimum" : 1 + "type" : "string", + "default" : "10", + "pattern" : "^[1-9][0-9]*$" }, { "name" : "extra_query", "in" : "query", @@ -4407,6 +4408,18 @@ "required" : false, "type" : "string", "pattern" : "^{\\s*\".*?\"\\s*:\\s*{.*?}\\s*(,\\s*\".*?\"\\s*:\\s*{.*?}\\s*)*\\s*}$" + }, { + "name" : "limit", + "in" : "query", + "description" : "Number of results to return", + "required" : false, + "type" : "integer" + }, { + "name" : "offset", + "in" : "query", + "description" : "Offset for pagination", + "required" : false, + "type" : "integer" }, { "name" : "referent", "in" : "query", @@ -4416,11 +4429,11 @@ }, { "name" : "start", "in" : "query", - "description" : "Start index", + "description" : "Start index (DEPRECATED - use offset instead)", "required" : false, - "type" : "integer", - "default" : 0, - "minimum" : 0 + "type" : "string", + "default" : "0", + "pattern" : "^[0-9]*$" } ], "deprecated" : true, "responses" : { @@ -7015,12 +7028,12 @@ "kid" : { "type" : "string", "example" : "did:web:example.com#key-01", - "description" : "Optional kid to bind to the keypair, such as a verificationMethod." + "description" : "Optional kid to bind to the keypair, such as a verificationMethod." }, "seed" : { "type" : "string", "example" : "00000000000000000000000000000000", - "description" : "Optional seed to generate the key pair. Must enable insecure wallet mode." + "description" : "Optional seed to generate the key pair. Must enable insecure wallet mode." } } }, @@ -7089,7 +7102,7 @@ "type" : "string", "example" : "askar", "description" : "Type of the wallet to create. Must be same as base wallet.", - "enum" : [ "askar", "askar-anoncreds", "in_memory" ] + "enum" : [ "askar", "askar-anoncreds" ] }, "wallet_webhook_urls" : { "type" : "array", @@ -7917,7 +7930,7 @@ "mediation_id" : { "type" : "string", "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Medation ID to use for endpoint information.", + "description" : "Mediation ID to use for endpoint information.", "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" } } @@ -8143,47 +8156,47 @@ "challenge" : { "type" : "string", "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "The value is used once for a particular domain and window of time. This value is used to mitigate replay attacks." + "description" : "The value is used once for a particular domain and window of time. This value is used to mitigate replay attacks." }, "created" : { "type" : "string", "example" : "2010-01-01T19:23:24Z", - "description" : "The date and time the proof was created is OPTIONAL and, if included, MUST be specified as an [XMLSCHEMA11-2] dateTimeStamp string" + "description" : "The date and time the proof was created is OPTIONAL and, if included, MUST be specified as an [XMLSCHEMA11-2] dateTimeStamp string" }, "cryptosuite" : { "type" : "string", "example" : "eddsa-jcs-2022", - "description" : "An identifier for the cryptographic suite that can be used to verify the proof." + "description" : "An identifier for the cryptographic suite that can be used to verify the proof." }, "domain" : { "type" : "string", "example" : "example.com", - "description" : "It conveys one or more security domains in which the proof is meant to be used." + "description" : "It conveys one or more security domains in which the proof is meant to be used." }, "expires" : { "type" : "string", "example" : "2010-01-01T19:23:24Z", - "description" : "The expires property is OPTIONAL and, if present, specifies when the proof expires. If present, it MUST be an [XMLSCHEMA11-2] dateTimeStamp string" + "description" : "The expires property is OPTIONAL and, if present, specifies when the proof expires. If present, it MUST be an [XMLSCHEMA11-2] dateTimeStamp string" }, "id" : { "type" : "string", "example" : "urn:uuid:6a1676b8-b51f-11ed-937b-d76685a20ff5", - "description" : "An optional identifier for the proof, which MUST be a URL [URL], such as a UUID as a URN" + "description" : "An optional identifier for the proof, which MUST be a URL [URL], such as a UUID as a URN" }, "nonce" : { "type" : "string", "example" : "CF69iO3nfvqRsRBNElE8b4wO39SyJHPM7Gg1nExltW5vSfQA1lvDCR/zXX1To0/4NLo==", - "description" : "One use of this field is to increase privacy by decreasing linkability that is the result of deterministically generated signatures." + "description" : "One use of this field is to increase privacy by decreasing linkability that is the result of deterministically generated signatures." }, "previousProof" : { "type" : "string", "example" : "urn:uuid:6a1676b8-b51f-11ed-937b-d76685a20ff5", - "description" : "Each value identifies another data integrity proof that MUST verify before the current proof is processed." + "description" : "Each value identifies another data integrity proof that MUST verify before the current proof is processed." }, "proofPurpose" : { "type" : "string", "example" : "assertionMethod", - "description" : "The proof purpose acts as a safeguard to prevent the proof from being misused by being applied to a purpose other than the one that was intended." + "description" : "The proof purpose acts as a safeguard to prevent the proof from being misused by being applied to a purpose other than the one that was intended." }, "proofValue" : { "type" : "string", @@ -8193,12 +8206,12 @@ "type" : { "type" : "string", "example" : "DataIntegrityProof", - "description" : "The specific type of proof MUST be specified as a string that maps to a URL [URL]." + "description" : "The specific type of proof MUST be specified as a string that maps to a URL [URL]." }, "verificationMethod" : { "type" : "string", "example" : "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", - "description" : "A verification method is the means and information needed to verify the proof. ", + "description" : "A verification method is the means and information needed to verify the proof.", "pattern" : "\\w+:(\\/?\\/?)[^\\s]+" } }, @@ -12487,7 +12500,7 @@ "kid" : { "type" : "string", "example" : "did:web:example.com#key-02", - "description" : "New kid to bind to the key pair, such as a verificationMethod." + "description" : "New kid to bind to the key pair, such as a verificationMethod." }, "multikey" : { "type" : "string", From 19cdaff78412e2e712d9d35c86436a1737d2c98a Mon Sep 17 00:00:00 2001 From: ff137 Date: Tue, 12 Nov 2024 22:10:08 +0200 Subject: [PATCH 11/12] :white_check_mark: Signed-off-by: ff137 --- acapy_agent/indy/credx/tests/test_cred_issuance.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acapy_agent/indy/credx/tests/test_cred_issuance.py b/acapy_agent/indy/credx/tests/test_cred_issuance.py index b1ce0c7058..f0154b0a7b 100644 --- a/acapy_agent/indy/credx/tests/test_cred_issuance.py +++ b/acapy_agent/indy/credx/tests/test_cred_issuance.py @@ -132,7 +132,7 @@ async def test_issue_store_non_rev(self): assert not await self.holder.get_mime_type(cred_id, "name") - creds = await self.holder.get_credentials(None, None, None) + creds = await self.holder.get_credentials(offset=None, limit=None, wql=None) assert len(creds) == 1 assert creds[0] == stored_cred @@ -247,7 +247,7 @@ async def test_issue_store_rev(self): assert found stored_cred = json.loads(found) - creds = await self.holder.get_credentials(None, None, None) + creds = await self.holder.get_credentials(offset=None, limit=None, wql=None) assert len(creds) == 1 assert creds[0] == stored_cred From 3c16aef47705420afcf9b98c419655601e24b140 Mon Sep 17 00:00:00 2001 From: ff137 Date: Sat, 30 Nov 2024 10:41:34 +0200 Subject: [PATCH 12/12] :art: Update start/count keywords to offset/limit Signed-off-by: ff137 --- acapy_agent/anoncreds/models/utils.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/acapy_agent/anoncreds/models/utils.py b/acapy_agent/anoncreds/models/utils.py index 51f140844f..ac5797190c 100644 --- a/acapy_agent/anoncreds/models/utils.py +++ b/acapy_agent/anoncreds/models/utils.py @@ -39,8 +39,8 @@ async def get_requested_creds_from_proof_request_preview( credentials = await holder.get_credentials_for_presentation_request_by_referent( presentation_request=proof_request, referents=(referent,), - start=0, - count=100, + offset=0, + limit=100, ) if not credentials: raise ValueError(_get_value_error_msg(proof_request, referent)) @@ -61,8 +61,8 @@ async def get_requested_creds_from_proof_request_preview( credentials = await holder.get_credentials_for_presentation_request_by_referent( presentation_request=proof_request, referents=(referent,), - start=0, - count=100, + offset=0, + limit=100, ) if not credentials: raise ValueError(_get_value_error_msg(proof_request, referent))