From b15324de29de406acfb5ec2fd3458593913818b6 Mon Sep 17 00:00:00 2001 From: Anton Shutik Date: Fri, 8 Nov 2024 08:52:11 +0100 Subject: [PATCH 1/3] Fixed graphql pagination --- README.md | 4 ++-- shopify_client/graphql.py | 2 ++ tests/test_graphql.py | 12 ++++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7528ab7..c902b65 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ response = client.query(query_name="listProducts") response = client.query(query, variables={"page_size": 20}) # Use pagination. -# Note that "pageIngo" block with at least "hasNextPage" & "startCursor" is required +# Note that "pageIngo" block with at least "hasNextPage" & "endCursor" is required # $cursor value should be passed as "after" parameter query = ''' query products($page_size: Int = 100, $cursor: String) { @@ -106,7 +106,7 @@ query products($page_size: Int = 100, $cursor: String) { } pageInfo { hasNextPage - startCursor + endCursor } } } diff --git a/shopify_client/graphql.py b/shopify_client/graphql.py index f656189..a9e46dc 100644 --- a/shopify_client/graphql.py +++ b/shopify_client/graphql.py @@ -50,6 +50,8 @@ def __query(self, query=None, query_name=None, variables=None, operation_name=No def __paginate(self, query, variables=None, operation_name=None, page_size=100): assert "pageInfo" in query, "Query must contain a 'pageInfo' object to be paginated" + assert "hasNextPage" in query[query.find("pageInfo"):], "Query must contain a 'hasNextPage' field in 'pageInfo' object" + assert "endCursor" in query[query.find("pageInfo"):], "Query must contain a 'endCursor' field in 'pageInfo' object" variables = variables or {} variables["page_size"] = page_size diff --git a/tests/test_graphql.py b/tests/test_graphql.py index ea3d13e..c1a05c0 100644 --- a/tests/test_graphql.py +++ b/tests/test_graphql.py @@ -98,3 +98,15 @@ def test_graphql_query_paginated(graphql, mock_client, mocker): call("graphql.json", json={"query": "query { items { id } pageInfo { hasNextPage, endCursor } }", "variables": {"cursor": None, "page_size": 100}, "operationName": None}), call("graphql.json", json={"query": "query { items { id } pageInfo { hasNextPage, endCursor } }", "variables": {"cursor": "cursor-1", "page_size": 100}, "operationName": None}), ]) + +def test_paginated_query_requires_page_info(graphql, mock_client): + with pytest.raises(AssertionError): + list(graphql(query="query { items { id } }", paginate=True)) + +def test_paginated_query_requires_has_next_page(graphql, mock_client): + with pytest.raises(AssertionError): + list(graphql(query="query { pageInfo { endCursor } }", paginate=True)) + +def test_paginated_query_requires_end_cursor(graphql, mock_client): + with pytest.raises(AssertionError): + list(graphql(query="query { pageInfo { hasNextPage } }", paginate=True)) From e67cda743019a65cdedff3b3c42c63f4f34be0bf Mon Sep 17 00:00:00 2001 From: Anton Shutik Date: Fri, 8 Nov 2024 08:35:38 +0100 Subject: [PATCH 2/3] Reraise graphql errors --- shopify_client/graphql.py | 4 ++-- tests/test_graphql.py | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/shopify_client/graphql.py b/shopify_client/graphql.py index a9e46dc..17004f0 100644 --- a/shopify_client/graphql.py +++ b/shopify_client/graphql.py @@ -43,10 +43,10 @@ def __query(self, query=None, query_name=None, variables=None, operation_name=No return self.client.parse_response(response) except requests.exceptions.HTTPError as e: logger.warning(f"Failed to execute GraphQL query: {repr(e)}") - return {} + raise e except json.JSONDecodeError as e: logger.warning(f"Failed to parse JSON response: {repr(e)}") - return {} + raise e def __paginate(self, query, variables=None, operation_name=None, page_size=100): assert "pageInfo" in query, "Query must contain a 'pageInfo' object to be paginated" diff --git a/tests/test_graphql.py b/tests/test_graphql.py index c1a05c0..93f0b44 100644 --- a/tests/test_graphql.py +++ b/tests/test_graphql.py @@ -45,15 +45,15 @@ def test_query_paginated(graphql, mock_client): assert results[0] == {"data": {"pageInfo": {"hasNextPage": True, "endCursor": "cursor1"}}} assert results[1] == {"data": {"pageInfo": {"hasNextPage": False}}} -def test_query_handles_http_error(graphql, mock_client, mocker): +def test_query_reraises_http_error(graphql, mock_client): mock_client.post.side_effect = requests.exceptions.HTTPError("HTTP Error") - response = graphql(query="query { key }") - assert response == {} + with pytest.raises(requests.exceptions.HTTPError): + graphql(query="query { key }") -def test_query_handles_json_error(graphql, mock_client): +def test_query_reraises_json_error(graphql, mock_client): mock_client.post.side_effect = json.JSONDecodeError("JSON Decode Error", "", 0) - response = graphql(query="query { key }") - assert response == {} + with pytest.raises(json.JSONDecodeError): + graphql(query="query { key }") def test_graphql_call(graphql, mock_client): mock_query_response = {"data": {"exampleField": "exampleValue"}} From 761ebcc6817b0455938e0f6ff8ffba32e6d0f8da Mon Sep 17 00:00:00 2001 From: Anton Shutik Date: Fri, 8 Nov 2024 08:35:58 +0100 Subject: [PATCH 3/3] v0.0.5 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 81340c7..bbdeab6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.0.4 +0.0.5