From 06a89151c85880bd23d8aab6a44b211ad344beab Mon Sep 17 00:00:00 2001 From: Arnav Saraf Date: Thu, 18 Dec 2025 17:37:16 +0530 Subject: [PATCH 1/8] java sdk for limits-svc --- .../com/cognite/client/CogniteClient.java | 11 + .../java/com/cognite/client/LimitValues.java | 263 ++++++++++++++++++ .../cognite/client/config/ResourceType.java | 3 +- .../client/servicesV1/ConnectorServiceV1.java | 23 ++ .../servicesV1/parser/LimitValueParser.java | 79 ++++++ src/main/proto/limit_value.proto | 16 ++ .../client/LimitValuesIntegrationTest.java | 170 +++++++++++ 7 files changed, 564 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/cognite/client/LimitValues.java create mode 100644 src/main/java/com/cognite/client/servicesV1/parser/LimitValueParser.java create mode 100644 src/main/proto/limit_value.proto create mode 100644 src/test/java/com/cognite/client/LimitValuesIntegrationTest.java diff --git a/src/main/java/com/cognite/client/CogniteClient.java b/src/main/java/com/cognite/client/CogniteClient.java index 0194ba43..e212ba9a 100644 --- a/src/main/java/com/cognite/client/CogniteClient.java +++ b/src/main/java/com/cognite/client/CogniteClient.java @@ -563,6 +563,17 @@ public Transformations transformations() { return Transformations.of(this); } + /** + * Returns {@link LimitValues} representing the Cognite Limit Values API endpoints. + * + * Note: This API is currently in alpha and requires the cdf-version header. + * + * @return The limit values api object. + */ + public LimitValues limitValues() { + return LimitValues.of(this); + } + /** * Returns the services layer mirroring the Cognite Data Fusion API. * @return diff --git a/src/main/java/com/cognite/client/LimitValues.java b/src/main/java/com/cognite/client/LimitValues.java new file mode 100644 index 00000000..7052a25c --- /dev/null +++ b/src/main/java/com/cognite/client/LimitValues.java @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2020 Cognite AS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.cognite.client; + +import com.cognite.client.dto.LimitValue; +import com.cognite.client.servicesV1.ResponseBinary; +import com.cognite.client.servicesV1.parser.LimitValueParser; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.auto.value.AutoValue; +import com.google.common.base.Preconditions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.URI; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * This class represents the Cognite Limit Values API endpoint. + * + * It provides methods for reading {@link LimitValue} objects. + * + * Note: This API is currently in alpha and requires the cdf-version header. + */ +@AutoValue +public abstract class LimitValues extends ApiBase { + + private static final String CDF_VERSION_HEADER = "cdf-version"; + private static final String CDF_VERSION_VALUE = "20230101-alpha"; + private static final ObjectMapper objectMapper = new ObjectMapper(); + + private static Builder builder() { + return new AutoValue_LimitValues.Builder(); + } + + protected static final Logger LOG = LoggerFactory.getLogger(LimitValues.class); + + /** + * Constructs a new {@link LimitValues} object using the provided client configuration. + * + * This method is intended for internal use--SDK clients should always use {@link CogniteClient} + * as the entry point to this class. + * + * @param client The {@link CogniteClient} to use for configuration settings. + * @return the limit values api object. + */ + public static LimitValues of(CogniteClient client) { + return LimitValues.builder() + .setClient(client) + .build(); + } + + /** + * Retrieves a specific limit value by its ID. + * + *

Example:

+ *
+     * {@code
+     *     LimitValue limitValue = client.limitValues().retrieve("my-limit-id");
+     * }
+     * 
+ * + * @param limitId The ID of the limit value to retrieve. + * @return The retrieved {@link LimitValue}. + * @throws Exception if the retrieval fails. + */ + public LimitValue retrieve(String limitId) throws Exception { + Preconditions.checkArgument(limitId != null && !limitId.isBlank(), + "limitId cannot be null or blank."); + + String loggingPrefix = "retrieve() - "; + LOG.debug(loggingPrefix + "Retrieving limit value with id: {}", limitId); + + URI requestUri = buildUri("limits/values/" + limitId); + + ResponseBinary response = getClient().experimental().cdfHttpRequest(requestUri) + .withHeader(CDF_VERSION_HEADER, CDF_VERSION_VALUE) + .get(); + + if (!response.getResponse().isSuccessful()) { + throw new Exception("Failed to retrieve limit value. Response code: " + + response.getResponse().code() + + ", message: " + response.getResponse().message()); + } + + String responseBody = response.getResponseBodyBytes().toStringUtf8(); + return parseLimitValue(responseBody); + } + + /** + * Returns all {@link LimitValue} objects using pagination. + * + *

Example:

+ *
+     * {@code
+     *     List allLimitValues = new ArrayList<>();
+     *     client.limitValues()
+     *             .list()
+     *             .forEachRemaining(allLimitValues::addAll);
+     * }
+     * 
+ * + * @return An {@link Iterator} to page through the results. + * @throws Exception if the list operation fails. + */ + public Iterator> list() throws Exception { + return list(Request.create()); + } + + /** + * Returns {@link LimitValue} objects that match the filters set in the {@link Request}. + * + * The results are paged through / iterated over via an {@link Iterator}--the entire results set is not buffered in + * memory, but streamed in "pages" from the Cognite API. + * + *

Example:

+ *
+     * {@code
+     *     List limitValues = new ArrayList<>();
+     *     client.limitValues()
+     *             .list(Request.create()
+     *                     .withFilterParameter("prefix", Map.of(
+     *                         "property", List.of("limitId"),
+     *                         "value", "atlas.")))
+     *             .forEachRemaining(limitValues::addAll);
+     * }
+     * 
+ * + * @param requestParameters The filters to use for retrieving limit values. + * @return An {@link Iterator} to page through the results. + * @throws Exception if the list operation fails. + */ + public Iterator> list(Request requestParameters) throws Exception { + return new LimitValuesIterator(requestParameters); + } + + /** + * Builds the URI for the limit values API endpoint. + * + * @param pathSegment The path segment to append (e.g., "limits/values/list") + * @return The complete URI + */ + private URI buildUri(String pathSegment) { + String baseUrl = getClient().getBaseUrl(); + String project = getClient().getProject(); + return URI.create(String.format("%s/api/v1/projects/%s/%s", + baseUrl, project, pathSegment)); + } + + /** + * Parses a JSON string to a LimitValue object. + */ + private LimitValue parseLimitValue(String json) { + try { + return LimitValueParser.parseLimitValue(json); + } catch (Exception e) { + throw new RuntimeException("Failed to parse LimitValue from JSON: " + e.getMessage(), e); + } + } + + /** + * Iterator implementation for paginated limit values listing. + */ + private class LimitValuesIterator implements Iterator> { + private final Request requestParameters; + private String cursor = null; + private boolean hasMore = true; + private boolean firstRequest = true; + + public LimitValuesIterator(Request requestParameters) { + this.requestParameters = requestParameters; + } + + @Override + public boolean hasNext() { + return hasMore; + } + + @Override + public List next() { + try { + return fetchNextBatch(); + } catch (Exception e) { + throw new RuntimeException("Failed to fetch next batch of limit values", e); + } + } + + private List fetchNextBatch() throws Exception { + URI requestUri = buildUri("limits/values/list"); + + // Build request body with cursor if available + Map requestBody = new java.util.HashMap<>(requestParameters.getRequestParameters()); + + if (!requestBody.containsKey("limit")) { + requestBody.put("limit", 1000); + } + + if (cursor != null) { + requestBody.put("cursor", cursor); + } + + Request request = Request.create().withRequestParameters(requestBody); + + ResponseBinary response = getClient().experimental().cdfHttpRequest(requestUri) + .withHeader(CDF_VERSION_HEADER, CDF_VERSION_VALUE) + .withRequestBody(request) + .post(); + + if (!response.getResponse().isSuccessful()) { + throw new Exception("Failed to list limit values. Response code: " + + response.getResponse().code() + + ", message: " + response.getResponse().message()); + } + + String responseBody = response.getResponseBodyBytes().toStringUtf8(); + JsonNode root = objectMapper.readTree(responseBody); + + // Parse items + List results = new ArrayList<>(); + JsonNode itemsNode = root.path("items"); + if (itemsNode.isArray()) { + for (JsonNode itemNode : itemsNode) { + results.add(LimitValueParser.parseLimitValue(itemNode.toString())); + } + } + + // Check for next cursor + JsonNode nextCursorNode = root.path("nextCursor"); + if (nextCursorNode.isTextual() && !nextCursorNode.textValue().isEmpty()) { + cursor = nextCursorNode.textValue(); + hasMore = true; + } else { + hasMore = false; + } + + firstRequest = false; + return results; + } + } + + @AutoValue.Builder + abstract static class Builder extends ApiBase.Builder { + abstract LimitValues build(); + } +} + diff --git a/src/main/java/com/cognite/client/config/ResourceType.java b/src/main/java/com/cognite/client/config/ResourceType.java index 29e92109..6742a06b 100644 --- a/src/main/java/com/cognite/client/config/ResourceType.java +++ b/src/main/java/com/cognite/client/config/ResourceType.java @@ -45,5 +45,6 @@ public enum ResourceType { TRANSFORMATIONS_JOBS, TRANSFORMATIONS_JOB_METRICS, TRANSFORMATIONS_SCHEDULES, - TRANSFORMATIONS_NOTIFICATIONS; + TRANSFORMATIONS_NOTIFICATIONS, + LIMIT_VALUE; } diff --git a/src/main/java/com/cognite/client/servicesV1/ConnectorServiceV1.java b/src/main/java/com/cognite/client/servicesV1/ConnectorServiceV1.java index e8a20dca..fbd2003f 100644 --- a/src/main/java/com/cognite/client/servicesV1/ConnectorServiceV1.java +++ b/src/main/java/com/cognite/client/servicesV1/ConnectorServiceV1.java @@ -1440,6 +1440,29 @@ public ItemWriter deleteLabels() { return ItemWriter.of(getClient(), requestProvider); } + /** + * Read limit values from Cognite. + * + * Note: This API requires a custom header (cdf-version). For alpha APIs, + * consider using CdfHttpRequest directly which supports custom headers. + * + * @param queryParameters The parameters for the limit values query. + * @return + */ + public ResultFutureIterator readLimitValues(Request queryParameters) { + LOG.debug(loggingPrefix + "Initiating read limit values service."); + + PostJsonListRequestProvider requestProvider = PostJsonListRequestProvider.builder() + .setEndpoint("limits/values/list") + .setRequest(queryParameters) + .setSdkIdentifier(getClient().getClientConfig().getSdkIdentifier()) + .setAppIdentifier(getClient().getClientConfig().getAppIdentifier()) + .setSessionIdentifier(getClient().getClientConfig().getSessionIdentifier()) + .build(); + + return ResultFutureIterator.of(getClient(), requestProvider, JsonItemResponseParser.create()); + } + /** * Read security categories from Cognite. * diff --git a/src/main/java/com/cognite/client/servicesV1/parser/LimitValueParser.java b/src/main/java/com/cognite/client/servicesV1/parser/LimitValueParser.java new file mode 100644 index 00000000..30e65e65 --- /dev/null +++ b/src/main/java/com/cognite/client/servicesV1/parser/LimitValueParser.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2020 Cognite AS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.cognite.client.servicesV1.parser; + +import com.cognite.client.dto.LimitValue; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.ImmutableMap; + +import java.util.Map; + +import static com.cognite.client.servicesV1.ConnectorConstants.MAX_LOG_ELEMENT_LENGTH; + +/** + * This class contains a set of methods to help parsing limit value objects between Cognite API representations + * (json and proto) and typed objects. + */ +public class LimitValueParser { + static final String logPrefix = "LimitValueParser - "; + static final ObjectMapper objectMapper = new ObjectMapper(); + + /** + * Parses a limit value json string to {@code LimitValue} proto object. + * + * @param json The JSON string to parse + * @return The parsed LimitValue object + * @throws Exception if parsing fails + */ + public static LimitValue parseLimitValue(String json) throws Exception { + JsonNode root = objectMapper.readTree(json); + LimitValue.Builder builder = LimitValue.newBuilder(); + + // limitId is required + if (root.path("limitId").isTextual()) { + builder.setLimitId(root.get("limitId").textValue()); + } else { + throw new Exception(logPrefix + "Unable to parse attribute: limitId. Item excerpt: " + + json.substring(0, Math.min(json.length() - 1, MAX_LOG_ELEMENT_LENGTH))); + } + + // value is required + if (root.path("value").isIntegralNumber()) { + builder.setValue(root.get("value").longValue()); + } else { + throw new Exception(logPrefix + "Unable to parse attribute: value. Item excerpt: " + + json.substring(0, Math.min(json.length() - 1, MAX_LOG_ELEMENT_LENGTH))); + } + + return builder.build(); + } + + /** + * Builds a request item object from {@link LimitValue}. + * + * @param element The LimitValue to convert + * @return A map representing the request body + */ + public static Map toRequestInsertItem(LimitValue element) { + return ImmutableMap.builder() + .put("limitId", element.getLimitId()) + .put("value", element.getValue()) + .build(); + } +} + diff --git a/src/main/proto/limit_value.proto b/src/main/proto/limit_value.proto new file mode 100644 index 00000000..0631cedb --- /dev/null +++ b/src/main/proto/limit_value.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; + +package com.cognite.client.dto; + +option java_package = "com.cognite.client.dto"; +option java_multiple_files = true; + +// LimitValue represents a limit value from the Cognite Limits API. +message LimitValue { + // The unique identifier for this limit (e.g., "functions.running_calls") + string limit_id = 1; + + // The current value of the limit + int64 value = 2; +} + diff --git a/src/test/java/com/cognite/client/LimitValuesIntegrationTest.java b/src/test/java/com/cognite/client/LimitValuesIntegrationTest.java new file mode 100644 index 00000000..2989e333 --- /dev/null +++ b/src/test/java/com/cognite/client/LimitValuesIntegrationTest.java @@ -0,0 +1,170 @@ +package com.cognite.client; + +import com.cognite.client.dto.LimitValue; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Integration tests for the LimitValues API. + * + * Note: These tests require a valid CDF project with the Limits API enabled. + * Set the following environment variables: + * - TEST_PROJECT: Your CDF project name + * - TEST_CLIENT_ID: OAuth client ID + * - TEST_CLIENT_SECRET: OAuth client secret + * - TEST_TENANT_ID: Azure AD tenant ID + * - TEST_HOST: CDF API host (e.g., https://api.cognitedata.com) + */ +class LimitValuesIntegrationTest { + final Logger LOG = LoggerFactory.getLogger(this.getClass()); + + @Test + @Tag("remoteCDP") + void listLimitValues() throws Exception { + Instant startInstant = Instant.now(); + String loggingPrefix = "IntegrationTest - listLimitValues() - "; + LOG.info(loggingPrefix + "Start test. Creating Cognite client."); + + CogniteClient client = TestConfigProvider.getCogniteClient(); + LOG.info(loggingPrefix + "Finished creating the Cognite client. Duration: {}", + Duration.between(startInstant, Instant.now())); + + LOG.info(loggingPrefix + "Start listing limit values."); + List listResults = new ArrayList<>(); + + try { + client.limitValues() + .list() + .forEachRemaining(listResults::addAll); + + LOG.info(loggingPrefix + "Finished listing limit values. Found {} items. Duration: {}", + listResults.size(), + Duration.between(startInstant, Instant.now())); + + // Log some sample data if available + if (!listResults.isEmpty()) { + LimitValue sample = listResults.get(0); + LOG.info(loggingPrefix + "Sample limit value - limitId: {}", sample.getLimitId()); + } + + } catch (Exception e) { + LOG.error(loggingPrefix + "Error listing limit values: {}", e.getMessage()); + throw e; + } + + // The test passes if no exception is thrown + // The actual count depends on your project's data + LOG.info(loggingPrefix + "Test completed successfully."); + } + + @Test + @Tag("remoteCDP") + void listLimitValuesWithFilter() throws Exception { + Instant startInstant = Instant.now(); + String loggingPrefix = "IntegrationTest - listLimitValuesWithFilter() - "; + LOG.info(loggingPrefix + "Start test. Creating Cognite client."); + + CogniteClient client = TestConfigProvider.getCogniteClient(); + LOG.info(loggingPrefix + "Finished creating the Cognite client. Duration: {}", + Duration.between(startInstant, Instant.now())); + + LOG.info(loggingPrefix + "Start listing limit values with prefix filter."); + List listResults = new ArrayList<>(); + + try { + // Filter by prefix on limitId + Request filterRequest = Request.create() + .withFilterParameter("prefix", Map.of( + "property", List.of("limitId"), + "value", "atlas." // Adjust this prefix based on your data + )); + + client.limitValues() + .list(filterRequest) + .forEachRemaining(listResults::addAll); + + LOG.info(loggingPrefix + "Finished listing filtered limit values. Found {} items. Duration: {}", + listResults.size(), + Duration.between(startInstant, Instant.now())); + + } catch (Exception e) { + LOG.error(loggingPrefix + "Error listing limit values with filter: {}", e.getMessage()); + throw e; + } + + LOG.info(loggingPrefix + "Test completed successfully."); + } + + @Test + @Tag("remoteCDP") + void retrieveLimitValueById() throws Exception { + Instant startInstant = Instant.now(); + String loggingPrefix = "IntegrationTest - retrieveLimitValueById() - "; + LOG.info(loggingPrefix + "Start test. Creating Cognite client."); + + CogniteClient client = TestConfigProvider.getCogniteClient(); + LOG.info(loggingPrefix + "Finished creating the Cognite client. Duration: {}", + Duration.between(startInstant, Instant.now())); + + // First, list to get a valid limitId + LOG.info(loggingPrefix + "First, listing limit values to get a valid ID."); + List listResults = new ArrayList<>(); + + try { + client.limitValues() + .list(Request.create().withRootParameter("limit", 1)) + .forEachRemaining(listResults::addAll); + + if (listResults.isEmpty()) { + LOG.warn(loggingPrefix + "No limit values found in project. Skipping retrieve test."); + return; + } + + String limitId = listResults.get(0).getLimitId(); + LOG.info(loggingPrefix + "Found limit value with ID: {}. Now retrieving by ID.", limitId); + + // Retrieve by ID + LimitValue retrieved = client.limitValues().retrieve(limitId); + + assertNotNull(retrieved, "Retrieved limit value should not be null"); + assertEquals(limitId, retrieved.getLimitId(), "Limit IDs should match"); + + LOG.info(loggingPrefix + "Successfully retrieved limit value. Duration: {}", + Duration.between(startInstant, Instant.now())); + + } catch (Exception e) { + LOG.error(loggingPrefix + "Error in test: {}", e.getMessage()); + throw e; + } + + LOG.info(loggingPrefix + "Test completed successfully."); + } + + @Test + @Tag("remoteCDP") + void retrieveNonExistentLimitValueThrowsException() throws Exception { + String loggingPrefix = "IntegrationTest - retrieveNonExistentLimitValueThrowsException() - "; + LOG.info(loggingPrefix + "Start test. Creating Cognite client."); + + CogniteClient client = TestConfigProvider.getCogniteClient(); + + LOG.info(loggingPrefix + "Attempting to retrieve non-existent limit value."); + + assertThrows(Exception.class, () -> { + client.limitValues().retrieve("non-existent-limit-id-12345"); + }, "Should throw exception for non-existent limit ID"); + + LOG.info(loggingPrefix + "Test completed successfully - exception was thrown as expected."); + } +} + From 94ec8a38cd0028e26515621179f34fb998e1e97e Mon Sep 17 00:00:00 2001 From: Arnav Saraf Date: Fri, 19 Dec 2025 11:49:17 +0530 Subject: [PATCH 2/8] gemini suggestion --- .../client/servicesV1/ConnectorServiceV1.java | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/src/main/java/com/cognite/client/servicesV1/ConnectorServiceV1.java b/src/main/java/com/cognite/client/servicesV1/ConnectorServiceV1.java index fbd2003f..e8a20dca 100644 --- a/src/main/java/com/cognite/client/servicesV1/ConnectorServiceV1.java +++ b/src/main/java/com/cognite/client/servicesV1/ConnectorServiceV1.java @@ -1440,29 +1440,6 @@ public ItemWriter deleteLabels() { return ItemWriter.of(getClient(), requestProvider); } - /** - * Read limit values from Cognite. - * - * Note: This API requires a custom header (cdf-version). For alpha APIs, - * consider using CdfHttpRequest directly which supports custom headers. - * - * @param queryParameters The parameters for the limit values query. - * @return - */ - public ResultFutureIterator readLimitValues(Request queryParameters) { - LOG.debug(loggingPrefix + "Initiating read limit values service."); - - PostJsonListRequestProvider requestProvider = PostJsonListRequestProvider.builder() - .setEndpoint("limits/values/list") - .setRequest(queryParameters) - .setSdkIdentifier(getClient().getClientConfig().getSdkIdentifier()) - .setAppIdentifier(getClient().getClientConfig().getAppIdentifier()) - .setSessionIdentifier(getClient().getClientConfig().getSessionIdentifier()) - .build(); - - return ResultFutureIterator.of(getClient(), requestProvider, JsonItemResponseParser.create()); - } - /** * Read security categories from Cognite. * From 872a0919e6dc237da74d62a6ec4f593169432ecc Mon Sep 17 00:00:00 2001 From: Arnav Saraf Date: Fri, 19 Dec 2025 11:51:01 +0530 Subject: [PATCH 3/8] gemini suggestions applied v2 --- .../java/com/cognite/client/LimitValues.java | 2 +- .../servicesV1/parser/LimitValueParser.java | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/cognite/client/LimitValues.java b/src/main/java/com/cognite/client/LimitValues.java index 7052a25c..a33f6f81 100644 --- a/src/main/java/com/cognite/client/LimitValues.java +++ b/src/main/java/com/cognite/client/LimitValues.java @@ -237,7 +237,7 @@ private List fetchNextBatch() throws Exception { JsonNode itemsNode = root.path("items"); if (itemsNode.isArray()) { for (JsonNode itemNode : itemsNode) { - results.add(LimitValueParser.parseLimitValue(itemNode.toString())); + results.add(LimitValueParser.parseLimitValue(itemNode)); } } diff --git a/src/main/java/com/cognite/client/servicesV1/parser/LimitValueParser.java b/src/main/java/com/cognite/client/servicesV1/parser/LimitValueParser.java index 30e65e65..5ec28dfb 100644 --- a/src/main/java/com/cognite/client/servicesV1/parser/LimitValueParser.java +++ b/src/main/java/com/cognite/client/servicesV1/parser/LimitValueParser.java @@ -42,6 +42,20 @@ public class LimitValueParser { */ public static LimitValue parseLimitValue(String json) throws Exception { JsonNode root = objectMapper.readTree(json); + return parseLimitValue(root); + } + + /** + * Parses a limit value JsonNode to {@code LimitValue} proto object. + * + * This overload allows callers that already have a JsonNode to avoid + * the overhead of converting the node to a string and re-parsing. + * + * @param root The JSON node to parse + * @return The parsed LimitValue object + * @throws Exception if parsing fails + */ + public static LimitValue parseLimitValue(JsonNode root) throws Exception { LimitValue.Builder builder = LimitValue.newBuilder(); // limitId is required @@ -49,7 +63,7 @@ public static LimitValue parseLimitValue(String json) throws Exception { builder.setLimitId(root.get("limitId").textValue()); } else { throw new Exception(logPrefix + "Unable to parse attribute: limitId. Item excerpt: " - + json.substring(0, Math.min(json.length() - 1, MAX_LOG_ELEMENT_LENGTH))); + + root.toString().substring(0, Math.min(root.toString().length(), MAX_LOG_ELEMENT_LENGTH))); } // value is required @@ -57,7 +71,7 @@ public static LimitValue parseLimitValue(String json) throws Exception { builder.setValue(root.get("value").longValue()); } else { throw new Exception(logPrefix + "Unable to parse attribute: value. Item excerpt: " - + json.substring(0, Math.min(json.length() - 1, MAX_LOG_ELEMENT_LENGTH))); + + root.toString().substring(0, Math.min(root.toString().length(), MAX_LOG_ELEMENT_LENGTH))); } return builder.build(); From 5e71112206f5f3764fc8e4a28865e419a70ff755 Mon Sep 17 00:00:00 2001 From: Arnav Saraf Date: Fri, 19 Dec 2025 11:55:50 +0530 Subject: [PATCH 4/8] gemini suggestion v3 --- src/main/java/com/cognite/client/LimitValues.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/cognite/client/LimitValues.java b/src/main/java/com/cognite/client/LimitValues.java index a33f6f81..5cf20607 100644 --- a/src/main/java/com/cognite/client/LimitValues.java +++ b/src/main/java/com/cognite/client/LimitValues.java @@ -17,6 +17,7 @@ package com.cognite.client; import com.cognite.client.dto.LimitValue; +import com.cognite.client.servicesV1.ConnectorConstants; import com.cognite.client.servicesV1.ResponseBinary; import com.cognite.client.servicesV1.parser.LimitValueParser; import com.fasterxml.jackson.databind.JsonNode; @@ -118,9 +119,8 @@ public LimitValue retrieve(String limitId) throws Exception { * * * @return An {@link Iterator} to page through the results. - * @throws Exception if the list operation fails. */ - public Iterator> list() throws Exception { + public Iterator> list() { return list(Request.create()); } @@ -145,9 +145,8 @@ public Iterator> list() throws Exception { * * @param requestParameters The filters to use for retrieving limit values. * @return An {@link Iterator} to page through the results. - * @throws Exception if the list operation fails. */ - public Iterator> list(Request requestParameters) throws Exception { + public Iterator> list(Request requestParameters) { return new LimitValuesIterator(requestParameters); } @@ -182,7 +181,6 @@ private class LimitValuesIterator implements Iterator> { private final Request requestParameters; private String cursor = null; private boolean hasMore = true; - private boolean firstRequest = true; public LimitValuesIterator(Request requestParameters) { this.requestParameters = requestParameters; @@ -209,7 +207,7 @@ private List fetchNextBatch() throws Exception { Map requestBody = new java.util.HashMap<>(requestParameters.getRequestParameters()); if (!requestBody.containsKey("limit")) { - requestBody.put("limit", 1000); + requestBody.put("limit", ConnectorConstants.DEFAULT_MAX_BATCH_SIZE); } if (cursor != null) { @@ -250,7 +248,6 @@ private List fetchNextBatch() throws Exception { hasMore = false; } - firstRequest = false; return results; } } From ee27cc56d0c8853eba06156c950ee4b672910ca9 Mon Sep 17 00:00:00 2001 From: Arnav Saraf Date: Fri, 19 Dec 2025 12:11:14 +0530 Subject: [PATCH 5/8] fixes --- src/main/java/com/cognite/client/LimitValues.java | 3 ++- .../com/cognite/client/servicesV1/parser/LimitValueParser.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/cognite/client/LimitValues.java b/src/main/java/com/cognite/client/LimitValues.java index 5cf20607..4a4f8bf7 100644 --- a/src/main/java/com/cognite/client/LimitValues.java +++ b/src/main/java/com/cognite/client/LimitValues.java @@ -20,6 +20,7 @@ import com.cognite.client.servicesV1.ConnectorConstants; import com.cognite.client.servicesV1.ResponseBinary; import com.cognite.client.servicesV1.parser.LimitValueParser; +import com.cognite.client.servicesV1.util.JsonUtil; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.auto.value.AutoValue; @@ -45,7 +46,7 @@ public abstract class LimitValues extends ApiBase { private static final String CDF_VERSION_HEADER = "cdf-version"; private static final String CDF_VERSION_VALUE = "20230101-alpha"; - private static final ObjectMapper objectMapper = new ObjectMapper(); + private static final ObjectMapper objectMapper = JsonUtil.getObjectMapperInstance(); private static Builder builder() { return new AutoValue_LimitValues.Builder(); diff --git a/src/main/java/com/cognite/client/servicesV1/parser/LimitValueParser.java b/src/main/java/com/cognite/client/servicesV1/parser/LimitValueParser.java index 5ec28dfb..f488e4e4 100644 --- a/src/main/java/com/cognite/client/servicesV1/parser/LimitValueParser.java +++ b/src/main/java/com/cognite/client/servicesV1/parser/LimitValueParser.java @@ -17,6 +17,7 @@ package com.cognite.client.servicesV1.parser; import com.cognite.client.dto.LimitValue; +import com.cognite.client.servicesV1.util.JsonUtil; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableMap; @@ -31,7 +32,7 @@ */ public class LimitValueParser { static final String logPrefix = "LimitValueParser - "; - static final ObjectMapper objectMapper = new ObjectMapper(); + static final ObjectMapper objectMapper = JsonUtil.getObjectMapperInstance(); /** * Parses a limit value json string to {@code LimitValue} proto object. From 27f0b98161cb8bb56e9a3f8b9de77a949cd75e94 Mon Sep 17 00:00:00 2001 From: Arnav Saraf Date: Fri, 19 Dec 2025 12:20:07 +0530 Subject: [PATCH 6/8] test improvements --- .../client/LimitValuesIntegrationTest.java | 32 ++++++++++++++++--- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/src/test/java/com/cognite/client/LimitValuesIntegrationTest.java b/src/test/java/com/cognite/client/LimitValuesIntegrationTest.java index 2989e333..3fb0cb75 100644 --- a/src/test/java/com/cognite/client/LimitValuesIntegrationTest.java +++ b/src/test/java/com/cognite/client/LimitValuesIntegrationTest.java @@ -78,25 +78,47 @@ void listLimitValuesWithFilter() throws Exception { LOG.info(loggingPrefix + "Finished creating the Cognite client. Duration: {}", Duration.between(startInstant, Instant.now())); + LOG.info(loggingPrefix + "First, listing limit values to derive a prefix for filtering."); + List initialResults = new ArrayList<>(); + client.limitValues() + .list(Request.create().withRootParameter("limit", 10)) + .forEachRemaining(initialResults::addAll); + + if (initialResults.isEmpty()) { + LOG.warn(loggingPrefix + "No limit values found in project. Skipping filter test."); + return; + } + + String sampleLimitId = initialResults.get(0).getLimitId(); + String prefix = sampleLimitId.contains(".") + ? sampleLimitId.substring(0, sampleLimitId.indexOf('.') + 1) + : sampleLimitId.substring(0, Math.min(5, sampleLimitId.length())); + LOG.info(loggingPrefix + "Using derived prefix: '{}'", prefix); + LOG.info(loggingPrefix + "Start listing limit values with prefix filter."); - List listResults = new ArrayList<>(); + List filteredResults = new ArrayList<>(); try { - // Filter by prefix on limitId Request filterRequest = Request.create() .withFilterParameter("prefix", Map.of( "property", List.of("limitId"), - "value", "atlas." // Adjust this prefix based on your data + "value", prefix )); client.limitValues() .list(filterRequest) - .forEachRemaining(listResults::addAll); + .forEachRemaining(filteredResults::addAll); LOG.info(loggingPrefix + "Finished listing filtered limit values. Found {} items. Duration: {}", - listResults.size(), + filteredResults.size(), Duration.between(startInstant, Instant.now())); + // Check all results match the prefix + for (LimitValue lv : filteredResults) { + assertTrue(lv.getLimitId().startsWith(prefix), + "LimitId should start with prefix: " + prefix); + } + } catch (Exception e) { LOG.error(loggingPrefix + "Error listing limit values with filter: {}", e.getMessage()); throw e; From 217cfb9d247fcef8ceecc950e0b6a232795c3b6a Mon Sep 17 00:00:00 2001 From: Arnav Saraf Date: Fri, 19 Dec 2025 12:30:20 +0530 Subject: [PATCH 7/8] adhering to common util standards --- src/main/java/com/cognite/client/LimitValues.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/cognite/client/LimitValues.java b/src/main/java/com/cognite/client/LimitValues.java index 4a4f8bf7..39b2539b 100644 --- a/src/main/java/com/cognite/client/LimitValues.java +++ b/src/main/java/com/cognite/client/LimitValues.java @@ -121,7 +121,7 @@ public LimitValue retrieve(String limitId) throws Exception { * * @return An {@link Iterator} to page through the results. */ - public Iterator> list() { + public Iterator> list() throws Exception { return list(Request.create()); } From 711e99d32bffb6401c6c96ae87a44e6a499d2183 Mon Sep 17 00:00:00 2001 From: Arnav Saraf Date: Thu, 8 Jan 2026 10:59:26 +0530 Subject: [PATCH 8/8] fix --- src/main/java/com/cognite/client/LimitValues.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/cognite/client/LimitValues.java b/src/main/java/com/cognite/client/LimitValues.java index 39b2539b..1207b1f1 100644 --- a/src/main/java/com/cognite/client/LimitValues.java +++ b/src/main/java/com/cognite/client/LimitValues.java @@ -218,8 +218,8 @@ private List fetchNextBatch() throws Exception { Request request = Request.create().withRequestParameters(requestBody); ResponseBinary response = getClient().experimental().cdfHttpRequest(requestUri) - .withHeader(CDF_VERSION_HEADER, CDF_VERSION_VALUE) .withRequestBody(request) + .withHeader(CDF_VERSION_HEADER, CDF_VERSION_VALUE) .post(); if (!response.getResponse().isSuccessful()) {