fix: write error handling - #425
Conversation
ca5ff0d to
78953cb
Compare
There was a problem hiding this comment.
Pull request overview
This PR refactors how RestClient formats and classifies write-related HTTP error responses (notably InfluxDB 3 write/partial-write formats), and updates unit/integration tests to reflect the new behavior.
Changes:
- Added an overloaded
RestClient.request(...)that propagatesacceptPartial/useV2Apiflags so partial-write handling can be gated by client options. - Reworked write-error parsing to build
InfluxDBPartialWriteExceptiondetails from JSON responses (and adjusted expected messages in tests). - Introduced a small
Utils.isNumerichelper and expanded unit test coverage with a table-driven set of partial-write cases.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/main/java/com/influxdb/v3/client/internal/RestClient.java | Refactors error parsing/exception selection for write endpoints; adds partial-write gating logic. |
| src/main/java/com/influxdb/v3/client/internal/InfluxDBClientImpl.java | Passes acceptPartial / useV2Api flags into RestClient.request(...) for write calls. |
| src/main/java/com/influxdb/v3/client/internal/Utils.java | Adds numeric-check helper used by error parsing. |
| src/main/java/com/influxdb/v3/client/InfluxDBPartialWriteException.java | Adjusts LineError nullability annotations. |
| src/test/java/com/influxdb/v3/client/internal/RestClientTest.java | Updates and expands tests for partial-write parsing/message formatting. |
| src/test/java/com/influxdb/v3/client/integration/E2ETest.java | Updates integration expectation for non-accept-partial write errors. |
Suppressed comments (2)
src/main/java/com/influxdb/v3/client/InfluxDBPartialWriteException.java:83
- Changing
LineError.errorMessageto@Nullableis a public API contract change and it’s inconsistent with current callers (tests andRestClient.createErrorMsgDetails) that treat it as always present. If the intent is still that a line error always has an error message, keep it@Nonnulland avoid propagating nulls to API consumers.
This issue also appears on line 95 of the same file.
public LineError(@Nullable final Integer lineNumber,
@Nullable final String errorMessage,
@Nullable final String originalLine) {
this.lineNumber = lineNumber;
this.errorMessage = errorMessage;
this.originalLine = originalLine;
src/main/java/com/influxdb/v3/client/InfluxDBPartialWriteException.java:100
- If
errorMessage()is intended to be always present for aLineError, its accessor should stay@Nonnullto match the API contract and existing usage.
* @return line-level error message
*/
@Nullable
public String errorMessage() {
return errorMessage;
}
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
d4a06d2 to
9e7e4cd
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Suppressed comments (6)
Previously missed (3) — in code that hasn't changed since the last review.
src/main/java/com/influxdb/v3/client/InfluxDBPartialWriteException.java:82
InfluxDBPartialWriteException.LineError#errorMessagewas previously non-null in the public API contract. Making it@Nullableis a breaking/loosening change for callers and also increases the risk of NPEs in formatting code that assumes a message exists. Unless null is a valid state that callers must handle, keep this@Nonnull.
This issue also appears on line 97 of the same file.
public LineError(@Nullable final Integer lineNumber,
@Nullable final String errorMessage,
@Nullable final String originalLine) {
this.lineNumber = lineNumber;
this.errorMessage = errorMessage;
src/main/java/com/influxdb/v3/client/internal/RestClient.java:263
if (root == null || root.toString().isEmpty())is effectively dead code (Jackson returns a non-null node, andtoString()won’t be empty). As a result, header-based fallbacks won’t execute anymore for empty bodies. Switching this to abodyemptiness check restores the intended fallback behavior.
if (root == null || root.toString().isEmpty()) {
reason = Stream.of("X-Platform-Error-Code", "X-Influx-Error", "X-InfluxDb-Error")
.map(name -> response.headers().firstValue(name).orElse(null))
.filter(message -> message != null && !message.isEmpty()).findFirst()
.orElse("");
src/main/java/com/influxdb/v3/client/internal/RestClient.java:243
- The response
Content-Typecan include parameters (e.g.text/plain; charset=utf-8). Using strict equality here can cause non-JSON text responses to be routed into JSON parsing unnecessarily. Consider a case-insensitive prefix match instead.
if (contentType != null && contentType.equals("text/plain")) {
var message = String.format("HTTP status code: %d; Message: %s", statusCode, body);
throw new InfluxDBApiHttpException(message, response.headers(), response.statusCode());
src/main/java/com/influxdb/v3/client/internal/RestClient.java:336
- This method return type uses
@NonNull(jspecify), which will fail to compile if jspecify isn’t on the classpath and is also inconsistent with the rest of this class’javax.annotation.Nonnullusage. Use@Nonnull(or omit the redundant annotation) instead.
@Nonnull
private @NonNull List<String> createErrorMsgDetails(
@Nullable final ParseLineErrorResult result,
@Nullable final JsonNode root
) {
src/test/java/com/influxdb/v3/client/internal/RestClientTest.java:1185
@NonNullontoString()requires the jspecify dependency (and isn’t providing much value here). Removing the annotation keeps the test independent of that external annotation library.
@Override
public @NonNull String toString() {
src/main/java/com/influxdb/v3/client/InfluxDBPartialWriteException.java:99
- For consistency with the constructor contract and to avoid forcing callers to handle nulls that should never occur,
errorMessage()should remain@Nonnull.
@Nullable
public String errorMessage() {
return errorMessage;
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #425 +/- ##
==========================================
- Coverage 88.73% 87.90% -0.83%
==========================================
Files 21 22 +1
Lines 1553 1546 -7
Branches 281 277 -4
==========================================
- Hits 1378 1359 -19
- Misses 77 83 +6
- Partials 98 104 +6 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
| if ("text/plain".equals(contentType)) { | ||
| var message = String.format("HTTP status code: %d; Message: %s", statusCode, body); | ||
| throw new InfluxDBApiHttpException(message, response.headers(), response.statusCode()); | ||
| } |
| JsonNode root; | ||
| try { | ||
| root = objectMapper.readTree(body); | ||
| } catch (JsonProcessingException e) { | ||
| var message = String.format("HTTP status code: %d; Message: %s", statusCode, body); | ||
| throw new InfluxDBApiHttpException(message, response.headers(), response.statusCode()); | ||
| } |
| Assertions.assertThat(testCase.expectedMsg()).as(testCase.name()).isEqualTo(thrown.getMessage()); | ||
| if (testCase.expectPartial()) { | ||
| Assertions.assertThat(thrown).as(testCase.name()).isInstanceOf(InfluxDBPartialWriteException.class); | ||
| } else { | ||
| Assertions.assertThat(thrown).as(testCase.name()).isInstanceOf(InfluxDBApiHttpException.class); | ||
| } | ||
| } |
|
|
||
| record ParseLineErrorResult(List<InfluxDBPartialWriteException.LineError> lineErrors, boolean allTyped) { | ||
| } No newline at end of file |
8d12b40 to
4428d3b
Compare
4428d3b to
5a70263
Compare
Closes #
Proposed Changes
Briefly describe your proposed changes:
Changes
objectMapper.readTree(body)function to only one time.Checklist