From 47ad0260a7e01c28702f685364c793a653f85058 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 14 May 2026 15:53:03 +0200 Subject: [PATCH 1/6] re-use variables --- ayon_api/graphql.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ayon_api/graphql.py b/ayon_api/graphql.py index d377d94b0..2fb75c9e5 100644 --- a/ayon_api/graphql.py +++ b/ayon_api/graphql.py @@ -370,7 +370,7 @@ def query(self, con: ServerAPI) -> dict[str, Any]: variables = self.get_variables_values() response = con.query_graphql( query_str, - self.get_variables_values() + variables ) if response.errors: raise GraphQlQueryFailed(response.errors, query_str, variables) From 2c637eece7de1995d09d158f4f78ff69684a8433 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 14 May 2026 15:53:17 +0200 Subject: [PATCH 2/6] handle first and last properly --- ayon_api/graphql.py | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/ayon_api/graphql.py b/ayon_api/graphql.py index 2fb75c9e5..33ba75413 100644 --- a/ayon_api/graphql.py +++ b/ayon_api/graphql.py @@ -304,7 +304,7 @@ def calculate_query(self) -> str: str: GraphQl string with variables and headers. Raises: - ValueError: Query has no fiels. + ValueError: Query has no fields. """ if not self._children: @@ -396,6 +396,7 @@ def continuous_query( while self.need_query: query_str = self.calculate_query() variables = self.get_variables_values() + response = con.query_graphql(query_str, variables) if response.errors: raise GraphQlQueryFailed( @@ -902,8 +903,12 @@ def parse_result( progress_data[cursor_key] = nodes_by_cursor page_info = value["pageInfo"] - new_cursor = page_info["endCursor"] - self._need_query = page_info["hasNextPage"] + if self._order == SortOrder.ascending: + new_cursor = page_info["endCursor"] + self._need_query = page_info["hasNextPage"] + else: + new_cursor = page_info["startCursor"] + self._need_query = page_info["hasPreviousPage"] edges = value["edges"] # Fake result parse if not edges: @@ -949,9 +954,7 @@ def _get_cursor_key(self) -> str: def get_filters(self) -> dict[str, Any]: filters = super().get_filters() - limit_key = "first" - if self._order == SortOrder.descending: - limit_key = "last" + limit_key = "first" if self._order == SortOrder.ascending else "last" limit_amount = 300 if self._limit: @@ -962,7 +965,10 @@ def get_filters(self) -> dict[str, Any]: filters[limit_key] = limit_amount if self._cursor: - filters["after"] = self._cursor + cursor_key = ( + "after" if self._order == SortOrder.ascending else "before" + ) + filters[cursor_key] = self._cursor return filters def calculate_query(self) -> str: @@ -1001,7 +1007,11 @@ def calculate_query(self) -> str: output.append(edges_offset + "pageInfo {") for page_key in ( "endCursor", - "hasNextPage", + ( + "hasNextPage" + if self._order == SortOrder.ascending + else "hasPreviousPage" + ), ): output.append(node_offset + page_key) output.append(edges_offset + "}") From 422e2f11b9c3ab9748a9a2875be75d87a95c774b Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 15 May 2026 09:30:12 +0200 Subject: [PATCH 3/6] check if cursor is repeated --- ayon_api/exceptions.py | 6 +++++- ayon_api/graphql.py | 7 ++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/ayon_api/exceptions.py b/ayon_api/exceptions.py index c72beb915..513eb88e4 100644 --- a/ayon_api/exceptions.py +++ b/ayon_api/exceptions.py @@ -78,7 +78,11 @@ class HTTPRequestError(RequestError): pass -class GraphQlQueryFailed(Exception): +class GraphQlQueryError(Exception): + pass + + +class GraphQlQueryFailed(GraphQlQueryError): def __init__(self, errors, query, variables): if variables is None: variables = {} diff --git a/ayon_api/graphql.py b/ayon_api/graphql.py index 33ba75413..af5488c61 100644 --- a/ayon_api/graphql.py +++ b/ayon_api/graphql.py @@ -6,7 +6,7 @@ import typing from typing import Optional, Iterable, Any, Generator -from .exceptions import GraphQlQueryFailed +from .exceptions import GraphQlQueryError, GraphQlQueryFailed from .utils import SortOrder if typing.TYPE_CHECKING: @@ -947,6 +947,11 @@ def parse_result( if change_cursor: for child in self._children_iter(): child.reset_cursor() + if new_cursor == self._cursor: + raise GraphQlQueryError( + "Cursor didn't change during pagination." + " This can cause infinite loop." + ) self._cursor = new_cursor def _get_cursor_key(self) -> str: From cec715125dbd737b83d1ef908b9576d6a319fd73 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 15 May 2026 12:18:50 +0200 Subject: [PATCH 4/6] change how cursor is changed --- ayon_api/graphql.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/ayon_api/graphql.py b/ayon_api/graphql.py index af5488c61..e195d9dd7 100644 --- a/ayon_api/graphql.py +++ b/ayon_api/graphql.py @@ -909,6 +909,7 @@ def parse_result( else: new_cursor = page_info["startCursor"] self._need_query = page_info["hasPreviousPage"] + edges = value["edges"] # Fake result parse if not edges: @@ -936,22 +937,14 @@ def parse_result( for child in self._children: child.parse_result(edge["node"], edge_value, progress_data) - if not self._need_query: - return - change_cursor = True for child in self._children_iter(): if child.need_query: change_cursor = False - if change_cursor: + if change_cursor and self._need_query: for child in self._children_iter(): child.reset_cursor() - if new_cursor == self._cursor: - raise GraphQlQueryError( - "Cursor didn't change during pagination." - " This can cause infinite loop." - ) self._cursor = new_cursor def _get_cursor_key(self) -> str: From fd3e50652a3dbf49ac10312221edd72b27b48443 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 15 May 2026 12:20:14 +0200 Subject: [PATCH 5/6] add check of cursor back --- ayon_api/graphql.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ayon_api/graphql.py b/ayon_api/graphql.py index e195d9dd7..1f079bc71 100644 --- a/ayon_api/graphql.py +++ b/ayon_api/graphql.py @@ -943,6 +943,12 @@ def parse_result( change_cursor = False if change_cursor and self._need_query: + if new_cursor == self._cursor: + raise GraphQlQueryError( + "Cursor didn't change during pagination." + " This can cause infinite loop." + ) + for child in self._children_iter(): child.reset_cursor() self._cursor = new_cursor From fea45aa313f86b2b552175998fe6191c33d4c62b Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 15 May 2026 12:22:37 +0200 Subject: [PATCH 6/6] use correct cursor key --- ayon_api/graphql.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ayon_api/graphql.py b/ayon_api/graphql.py index 1f079bc71..8e762eb3d 100644 --- a/ayon_api/graphql.py +++ b/ayon_api/graphql.py @@ -1010,7 +1010,11 @@ def calculate_query(self) -> str: # Add page information output.append(edges_offset + "pageInfo {") for page_key in ( - "endCursor", + ( + "endCursor" + if self._order == SortOrder.ascending + else "startCursor" + ), ( "hasNextPage" if self._order == SortOrder.ascending