Custom Strategies#
New in version 1.0.0
Subgrounds allows developers to create their own pagination strategy by creating a class that implements the PaginationStrategy
protocol:
class PaginationStrategy(Protocol):
def __init__(self, schema: SchemaMeta, document: Document) -> None:
"""Initializes the pagination strategy. If there is no need for pagination given
the provided :class:`Document` ``document``, then the constructor should raise a
:class:`SkipPagination` exception.
Args:
schema: The schema of the API against which ``document`` will be executed
document: The query document
"""
...
def step(
self, page_data: dict[str, Any] | None = None
) -> tuple[Document, dict[str, Any]]:
"""Returns the new query document and its variables which will be executed to
get the next page of data.
If this is the first query made as part of the pagination strategy, then
``page_data`` will be ``None``.
If pagination should be interupted (e.g.: if enough entities have been queried),
then this method should raise a :class:`StopPagination` exception.
Args:
page_data: The previous query's response data. If this is the first query
(i.e.: the first page of data), then it will be `None`. Defaults to None.
Returns:
tuple: A tuple `(doc, vars)` where `doc` is the query document that will be
executed to fetch the next page of data and `vars` are the variables for
that document.
"""
...
The class's constructor should accept a SchemaMeta
argument which represents the schema of the subgraph API that the query is directed to and a Document
argument which represents the query to be paginated on. If no pagination is required for the given document, then the constructor should raise a SkipPagination
exception.
The class's step
method is where the main logic of the pagination strategy is located. The method accepts a single argument, page_data
which is a dictionary containing the response data of the previous query (i.e.: the previous page of data). The step
method should return a tuple (doc, vars)
, where doc
is a Document
representing the query to be made to fetch the next page of data. When pagination is over (e.g.: when all pages of data have been fetched), the step
method should raise a StopPagination
exception.
Below is the algorithm used by Subgrounds
to paginate over a query document given a pagination strategy:
def paginate(
schema: SchemaMeta, doc: Document, pagination_strategy: type[PaginationStrategy]
) -> PaginateGenerator:
"""Evaluates a :class:`PaginationStrategy` against a document based upon a schema.
Produces a stream of :class:`Document`s until pagination is completed. Documents are
executed outside the this function and data is transfered via the generator scheme.
Args:
schema (SchemaMeta): The GraphQL schema on which the request document is based
doc (Document): The request document
Returns:
dict[str, Any]: The response data as a JSON dictionary
"""
try:
strategy = pagination_strategy(schema, doc)
doc, args = strategy.step()
while True:
page = yield replace(doc, variables=doc.variables | args)
try:
doc, args = strategy.step(page.data)
except StopPagination:
break
except Exception as exn:
raise PaginationError(exn.args[0], strategy) from exn
except SkipPagination:
_ = yield doc # consistency