From 7d76906749152cdf89beb39da25dd840580825a5 Mon Sep 17 00:00:00 2001 From: Nikita Turchaninov Date: Wed, 1 Nov 2023 17:02:52 +0300 Subject: [PATCH 1/2] Add limit param support --- cybsi/cloud/auth/api_key.py | 4 ++++ cybsi/cloud/auth/resource.py | 4 ++++ cybsi/cloud/iocean/collection.py | 16 ++++++++++++-- cybsi/cloud/iocean/objects.py | 36 ++++++++++++++++++++++++++++---- cybsi/cloud/iocean/schemas.py | 8 +++++++ docs/index.rst | 1 + docs/user/pagination.rst | 27 ++++++++++++++++++++++++ examples/page_limit.py | 20 ++++++++++++++++++ 8 files changed, 110 insertions(+), 6 deletions(-) create mode 100644 docs/user/pagination.rst create mode 100644 examples/page_limit.py diff --git a/cybsi/cloud/auth/api_key.py b/cybsi/cloud/auth/api_key.py index 0bddbe1..1da14fe 100644 --- a/cybsi/cloud/auth/api_key.py +++ b/cybsi/cloud/auth/api_key.py @@ -113,6 +113,7 @@ def filter( revoked: Optional[bool] = None, description: Optional[str] = None, cursor: Optional[Cursor] = None, + limit: Optional[int] = None, ) -> Page["APIKeyView"]: """Get API keys. @@ -122,6 +123,7 @@ def filter( revoked: Revocation flag. description: Key description. cursor: Page cursor. + limit: Page limit. Return: Page with API-Key common views and next page cursor. Raises: @@ -131,6 +133,8 @@ def filter( params: JsonObject = {} if cursor is not None: params["cursor"] = str(cursor) + if limit is not None: + params["limit"] = limit if revoked is not None: params["revoked"] = bool(revoked) if description is not None: diff --git a/cybsi/cloud/auth/resource.py b/cybsi/cloud/auth/resource.py index f4aaf0b..33ef2a0 100644 --- a/cybsi/cloud/auth/resource.py +++ b/cybsi/cloud/auth/resource.py @@ -15,6 +15,7 @@ def filter( *, parent_id: Optional[int] = None, cursor: Optional[Cursor] = None, + limit: Optional[int] = None, ) -> Page["ResourceView"]: """Get resources. @@ -23,6 +24,7 @@ def filter( Args: parent_id: identifier of parent resource. It must be greater than 0. cursor: Page cursor. + limit: Page limit. Return: Page with resource common views and next page cursor. Raises: @@ -38,6 +40,8 @@ def filter( params["parentID"] = parent_id if cursor is not None: params["cursor"] = str(cursor) + if limit is not None: + params["limit"] = limit resp = self._connector.do_get(path=self._path, params=params) page = Page(self._connector.do_get, resp, ResourceView) return page diff --git a/cybsi/cloud/iocean/collection.py b/cybsi/cloud/iocean/collection.py index 72b566e..bd56657 100644 --- a/cybsi/cloud/iocean/collection.py +++ b/cybsi/cloud/iocean/collection.py @@ -104,7 +104,10 @@ def update(self, collection_id: str, tag: Tag, *, schema_id: Optional[str]): self._connector.do_patch(url, tag=tag, json=body) def filter( - self, *, cursor: Optional[Cursor] = None + self, + *, + cursor: Optional[Cursor] = None, + limit: Optional[int] = None, ) -> Page["CollectionCommonView"]: """Get collections. @@ -112,6 +115,7 @@ def filter( Calls `GET /iocean/collections`. Args: cursor: Page cursor. + limit: Page limit. Return: Page with collection views and next page cursor. Raises: @@ -121,6 +125,8 @@ def filter( params: JsonObject = {} if cursor is not None: params["cursor"] = str(cursor) + if limit is not None: + params["limit"] = limit resp = self._connector.do_get(path=_PATH, params=params) return Page(self._connector.do_get, resp, CollectionCommonView) @@ -221,7 +227,10 @@ async def update(self, collection_id: str, tag: Tag, *, schema_id: Optional[str] await self._connector.do_patch(url, tag=tag, json=body) async def filter( - self, *, cursor: Optional[Cursor] = None + self, + *, + cursor: Optional[Cursor] = None, + limit: Optional[int] = None, ) -> AsyncPage["CollectionCommonView"]: """Get collections. @@ -229,6 +238,7 @@ async def filter( Calls `GET /iocean/collections`. Args: cursor: Page cursor. + limit: Page limit. Return: Page with collection views and next page cursor. Raises: @@ -238,6 +248,8 @@ async def filter( params: JsonObject = {} if cursor is not None: params["cursor"] = str(cursor) + if limit is not None: + params["limit"] = limit resp = await self._connector.do_get(path=_PATH, params=params) return AsyncPage(self._connector.do_get, resp, CollectionCommonView) diff --git a/cybsi/cloud/iocean/objects.py b/cybsi/cloud/iocean/objects.py index 13cf99a..ceb9f3f 100644 --- a/cybsi/cloud/iocean/objects.py +++ b/cybsi/cloud/iocean/objects.py @@ -126,7 +126,11 @@ def delete( self._connector.do_delete(path=path, params=params) def filter( - self, *, collection_id: str, cursor: Optional[Cursor] = None + self, + *, + collection_id: str, + cursor: Optional[Cursor] = None, + limit: Optional[int] = None, ) -> Tuple[Page["ObjectView"], Optional[Cursor]]: """Get objects from the collection. @@ -135,6 +139,7 @@ def filter( Args: collection_id: Collection identifier. cursor: Page cursor. + limit: Page limit. Return: Page with object views. The page contains next page cursor. Changes cursor. The cursor can be used to call :meth:`changes`. @@ -148,6 +153,8 @@ def filter( params: JsonObject = {} if cursor is not None: params["cursor"] = str(cursor) + if limit is not None: + params["limit"] = limit path = _PATH.format(collection_id) resp = self._connector.do_get(path=path, params=params) return Page(self._connector.do_get, resp, ObjectView), _extract_changes_cursor( @@ -155,7 +162,11 @@ def filter( ) def changes( - self, *, collection_id: str, cursor: Cursor + self, + *, + collection_id: str, + cursor: Cursor, + limit: Optional[int] = None, ) -> Page["ObjectChangeView"]: """Get objects changes from the collection. @@ -168,6 +179,7 @@ def changes( obtained when requesting objects :meth:`filter`. Subsequent calls should use cursor property of the page returned by :meth:`changes`. + limit: Page limit. Return: Page with changes. Warning: @@ -189,6 +201,8 @@ def changes( * :attr:`~cybsi.cloud.error.SemanticErrorCodes.CursorOutOfRange` """ params: JsonObject = {"cursor": cursor} + if limit is not None: + params["limit"] = limit path = _PATH.format(collection_id) + "/changes" resp = self._connector.do_get(path=path, params=params) return Page(self._connector.do_get, resp, ObjectChangeView) @@ -271,7 +285,11 @@ async def delete( await self._connector.do_delete(path=path, params=params) async def filter( - self, *, collection_id: str, cursor: Optional[Cursor] = None + self, + *, + collection_id: str, + cursor: Optional[Cursor] = None, + limit: Optional[int] = None, ) -> Tuple[AsyncPage["ObjectView"], Optional[Cursor]]: """Get objects from the collection. @@ -280,6 +298,7 @@ async def filter( Args: collection_id: Collection identifier. cursor: Page cursor. + limit: Page limit. Return: Page with object views. The page contains next page cursor. Changes cursor. The cursor can be used to call :meth:`changes`. @@ -293,6 +312,8 @@ async def filter( params: JsonObject = {} if cursor is not None: params["cursor"] = str(cursor) + if limit is not None: + params["limit"] = limit path = _PATH.format(collection_id) resp = await self._connector.do_get(path=path, params=params) return AsyncPage( @@ -300,7 +321,11 @@ async def filter( ), _extract_changes_cursor(resp) async def changes( - self, *, collection_id: str, cursor: Cursor + self, + *, + collection_id: str, + cursor: Cursor, + limit: Optional[int] = None, ) -> AsyncPage["ObjectChangeView"]: """Get objects changes from the collection. @@ -313,6 +338,7 @@ async def changes( obtained when requesting objects :meth:`filter`. Subsequent calls should use cursor property of the page returned by :meth:`changes`. + limit: Page limit. Return: Page with changes. Warning: @@ -334,6 +360,8 @@ async def changes( * :attr:`~cybsi.cloud.error.SemanticErrorCodes.CursorOutOfRange` """ params: JsonObject = {"cursor": cursor} + if limit is not None: + params["limit"] = limit path = _PATH.format(collection_id) + "/changes" resp = await self._connector.do_get(path=path, params=params) return AsyncPage(self._connector.do_get, resp, ObjectChangeView) diff --git a/cybsi/cloud/iocean/schemas.py b/cybsi/cloud/iocean/schemas.py index 777a493..99fc772 100644 --- a/cybsi/cloud/iocean/schemas.py +++ b/cybsi/cloud/iocean/schemas.py @@ -84,6 +84,7 @@ def filter( self, *, cursor: Optional[Cursor] = None, + limit: Optional[int] = None, ) -> Page["SchemaCommonView"]: """Get an object schemas filtration list. @@ -91,6 +92,7 @@ def filter( Calls `GET /iocean/schemas`. Args: cursor: Page cursor. + limit: Page limit. Returns: Page with schema common views and next page cursor. Raises: @@ -101,6 +103,8 @@ def filter( params: JsonObject = {} if cursor is not None: params["cursor"] = str(cursor) + if limit is not None: + params["limit"] = limit resp = self._connector.do_get(path=_PATH, params=params) return Page(self._connector.do_get, resp, SchemaCommonView) @@ -181,6 +185,7 @@ async def filter( self, *, cursor: Optional[Cursor] = None, + limit: Optional[int] = None, ) -> AsyncPage["SchemaCommonView"]: """Get an object schemas filtration list. @@ -188,6 +193,7 @@ async def filter( Calls `GET /iocean/schemas`. Args: cursor: Page cursor. + limit: Page limit. Returns: Page with schema common views and next page cursor. Raises: @@ -198,6 +204,8 @@ async def filter( params: JsonObject = {} if cursor is not None: params["cursor"] = str(cursor) + if limit is not None: + params["limit"] = limit resp = await self._connector.do_get(path=_PATH, params=params) return AsyncPage(self._connector.do_get, resp, SchemaCommonView) diff --git a/docs/index.rst b/docs/index.rst index ade61d2..0e345fe 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -32,6 +32,7 @@ instructions for getting the most out of Cybsi Cloud SDK. user/install user/quickstart + user/pagination user/examples user/advanced user/authentication diff --git a/docs/user/pagination.rst b/docs/user/pagination.rst new file mode 100644 index 0000000..65c030f --- /dev/null +++ b/docs/user/pagination.rst @@ -0,0 +1,27 @@ +.. _pagination: + +Pagination +========== + +You'll often need to work with collections of elements API provides. + +Cybsi Cloud SDK provides two ways to traverse collections. + +The **first** way is pages traversing. +This approach fits for cases when you need to get page's properties i.e. cursor. +For walking by page elements just iterate through the page. + +.. literalinclude:: ../../examples/pagination_manual.py + +The **second** way is elements traversing. This approach allows you to iterate through +collections without working with pages. To work with collections as with iterator use `chain_pages`. + +.. literalinclude:: ../../examples/get_collection_objects_chained.py + +Limit +----- + +You can define page limit. Backend returns the specified maximum number of elements per page. +Backend overrides this value if limit is not set or value is out of bounds. + +.. literalinclude:: ../../examples/page_limit.py diff --git a/examples/page_limit.py b/examples/page_limit.py new file mode 100644 index 0000000..e90b906 --- /dev/null +++ b/examples/page_limit.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +from typing import Optional + +from cybsi.cloud import Client, Config +from cybsi.cloud.auth import ResourceView +from cybsi.cloud.pagination import Page + +if __name__ == "__main__": + config = Config(api_key="the cryptic string") + + with Client(config) as client: + page: Optional[Page[ResourceView]] = client.auth.resources.filter(limit=3) + while page: + # Got page with maximum of 3 elements + # Page is iterable + for item in page: + # Do something with an item + pass + # Fetch next page + page = page.next_page() From ae70eefdf8091cca33c1b02cff96453873826821 Mon Sep 17 00:00:00 2001 From: Nikita Turchaninov Date: Wed, 1 Nov 2023 17:36:57 +0300 Subject: [PATCH 2/2] Bump to 1.0.5 --- HISTORY.md | 6 ++++++ cybsi/__version__.py | 2 +- pyproject.toml | 4 ++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 54619dd..7fe11b4 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,11 @@ Release History =============== + +1.0.5 (2023-11-01) +------------------ + +- Add page limit + 1.0.4 (2023-09-29) ------------------ diff --git a/cybsi/__version__.py b/cybsi/__version__.py index 52f70fb..495eb3d 100644 --- a/cybsi/__version__.py +++ b/cybsi/__version__.py @@ -1,4 +1,4 @@ -__version__ = "1.0.4" +__version__ = "1.0.5" __title__ = "cybsi-cloud-sdk" __description__ = "Cybsi Cloud development kit" __license__ = "Apache License 2.0" diff --git a/pyproject.toml b/pyproject.toml index 6c868ca..cdfde28 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "cybsi-cloud-sdk" -version = "1.0.4" +version = "1.0.5" description = "Cybsi Cloud development kit" authors = ["Cybsi Cloud developers"] license = "Apache License 2.0" @@ -41,7 +41,7 @@ extend_skip = ["__init__.py"] [tool.tbump] [tool.tbump.version] -current = "1.0.4" +current = "1.0.5" regex = ''' ^