From edecc77649973d8bc50cdf98fb47fb52fba7e348 Mon Sep 17 00:00:00 2001 From: David Tapiador Date: Mon, 4 May 2026 11:07:45 +0200 Subject: [PATCH 1/2] Continue cursor pagination until results are empty Mirror DataDog/datadog-api-client-go#3769: when an endpoint uses cursor-based pagination, keep requesting pages until the server returns an empty result set instead of stopping at the first page smaller than the requested limit. Co-Authored-By: Claude Opus 4.7 --- .generator/src/generator/templates/api_client.j2 | 16 ++++++++++++---- src/datadog_api_client/api_client.py | 16 ++++++++++++---- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/.generator/src/generator/templates/api_client.j2 b/.generator/src/generator/templates/api_client.j2 index 4357a5c46e..a3ff6670a6 100644 --- a/.generator/src/generator/templates/api_client.j2 +++ b/.generator/src/generator/templates/api_client.j2 @@ -350,9 +350,13 @@ class ApiClient: host=host, collection_formats=params["collection_format"], ) - for item in get_attribute_from_path(response, pagination.get("results_path")): + results = get_attribute_from_path(response, pagination.get("results_path")) + for item in results: yield item - if len(get_attribute_from_path(response, pagination.get("results_path"))) < pagination["limit_value"]: + if "cursor_param" in pagination: + if len(results) == 0: + break + elif len(results) < pagination["limit_value"]: break params = self._update_paginated_params(pagination, response) @@ -651,9 +655,13 @@ class AsyncApiClient(ApiClient): host=host, collection_formats=params["collection_format"], ) - for item in get_attribute_from_path(response, pagination.get("results_path")): + results = get_attribute_from_path(response, pagination.get("results_path")) + for item in results: yield item - if len(get_attribute_from_path(response, pagination.get("results_path"))) < pagination["limit_value"]: + if "cursor_param" in pagination: + if len(results) == 0: + break + elif len(results) < pagination["limit_value"]: break params = self._update_paginated_params(pagination, response) diff --git a/src/datadog_api_client/api_client.py b/src/datadog_api_client/api_client.py index 5471b20b3f..683cf866d8 100644 --- a/src/datadog_api_client/api_client.py +++ b/src/datadog_api_client/api_client.py @@ -357,9 +357,13 @@ def call_api_paginated( host=host, collection_formats=params["collection_format"], ) - for item in get_attribute_from_path(response, pagination.get("results_path")): + results = get_attribute_from_path(response, pagination.get("results_path")) + for item in results: yield item - if len(get_attribute_from_path(response, pagination.get("results_path"))) < pagination["limit_value"]: + if "cursor_param" in pagination: + if len(results) == 0: + break + elif len(results) < pagination["limit_value"]: break params = self._update_paginated_params(pagination, response) @@ -659,9 +663,13 @@ async def call_api_paginated( host=host, collection_formats=params["collection_format"], ) - for item in get_attribute_from_path(response, pagination.get("results_path")): + results = get_attribute_from_path(response, pagination.get("results_path")) + for item in results: yield item - if len(get_attribute_from_path(response, pagination.get("results_path"))) < pagination["limit_value"]: + if "cursor_param" in pagination: + if len(results) == 0: + break + elif len(results) < pagination["limit_value"]: break params = self._update_paginated_params(pagination, response) From 8ea790388b279e69923bac92dcfe2f3baece8e0f Mon Sep 17 00:00:00 2001 From: David Tapiador Date: Mon, 4 May 2026 15:38:36 +0200 Subject: [PATCH 2/2] Also stop cursor pagination when next cursor is missing If the response omits the next-cursor field (or returns it empty/null), there is no way to fetch a further page, so the loop should terminate even when the current page still has results. Co-Authored-By: Claude Opus 4.7 --- .generator/src/generator/templates/api_client.j2 | 4 ++-- src/datadog_api_client/api_client.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.generator/src/generator/templates/api_client.j2 b/.generator/src/generator/templates/api_client.j2 index a3ff6670a6..c9e5170574 100644 --- a/.generator/src/generator/templates/api_client.j2 +++ b/.generator/src/generator/templates/api_client.j2 @@ -354,7 +354,7 @@ class ApiClient: for item in results: yield item if "cursor_param" in pagination: - if len(results) == 0: + if len(results) == 0 or not get_attribute_from_path(response, pagination["cursor_path"], default=""): break elif len(results) < pagination["limit_value"]: break @@ -659,7 +659,7 @@ class AsyncApiClient(ApiClient): for item in results: yield item if "cursor_param" in pagination: - if len(results) == 0: + if len(results) == 0 or not get_attribute_from_path(response, pagination["cursor_path"], default=""): break elif len(results) < pagination["limit_value"]: break diff --git a/src/datadog_api_client/api_client.py b/src/datadog_api_client/api_client.py index 683cf866d8..3ada61834a 100644 --- a/src/datadog_api_client/api_client.py +++ b/src/datadog_api_client/api_client.py @@ -361,7 +361,7 @@ def call_api_paginated( for item in results: yield item if "cursor_param" in pagination: - if len(results) == 0: + if len(results) == 0 or not get_attribute_from_path(response, pagination["cursor_path"], default=""): break elif len(results) < pagination["limit_value"]: break @@ -667,7 +667,7 @@ async def call_api_paginated( for item in results: yield item if "cursor_param" in pagination: - if len(results) == 0: + if len(results) == 0 or not get_attribute_from_path(response, pagination["cursor_path"], default=""): break elif len(results) < pagination["limit_value"]: break