From b4378ca8c02cfed0bba6cc8917d6f921732c82d0 Mon Sep 17 00:00:00 2001 From: "fern-api[bot]" <115122769+fern-api[bot]@users.noreply.github.com> Date: Mon, 9 Mar 2026 14:05:59 +0000 Subject: [PATCH] feat: add HTTP 429 and 503 error handling to FHIR client MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enhance the FHIR client with comprehensive error handling for rate limiting (429) and service unavailability (503) scenarios. This improves the SDK's resilience and provides better error feedback to developers. Key changes: - Add TooManyRequestsError for HTTP 429 responses with structured error handling - Add ServiceUnavailableError for HTTP 503 responses with generic error handling - Update both synchronous and asynchronous FHIR clients with new error cases - Import new error classes in client implementations 🌿 Generated with Fern --- build.gradle | 4 +- changelog.md | 10 +++ .../com/phenoml/api/core/ClientOptions.java | 4 +- .../resources/fhir/AsyncRawFhirClient.java | 62 +++++++++++++++++++ .../api/resources/fhir/RawFhirClient.java | 38 ++++++++++++ .../fhir/errors/ServiceUnavailableError.java | 32 ++++++++++ .../fhir/errors/TooManyRequestsError.java | 33 ++++++++++ 7 files changed, 179 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/phenoml/api/resources/fhir/errors/ServiceUnavailableError.java create mode 100644 src/main/java/com/phenoml/api/resources/fhir/errors/TooManyRequestsError.java diff --git a/build.gradle b/build.gradle index 321ab61..a23fba2 100644 --- a/build.gradle +++ b/build.gradle @@ -47,7 +47,7 @@ java { group = 'com.phenoml.maven' -version = '9.0.1' +version = '9.1.0' jar { dependsOn(":generatePomFileForMavenPublication") @@ -78,7 +78,7 @@ publishing { maven(MavenPublication) { groupId = 'com.phenoml.maven' artifactId = 'phenoml-java-sdk' - version = '9.0.1' + version = '9.1.0' from components.java pom { name = 'phenoml' diff --git a/changelog.md b/changelog.md index 69a9752..336f813 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,13 @@ +## 9.1.0 - 2026-03-09 +* feat: add HTTP 429 and 503 error handling to FHIR client +* Enhance the FHIR client with comprehensive error handling for rate limiting (429) and service unavailability (503) scenarios. This improves the SDK's resilience and provides better error feedback to developers. +* Key changes: +* Add TooManyRequestsError for HTTP 429 responses with structured error handling +* Add ServiceUnavailableError for HTTP 503 responses with generic error handling +* Update both synchronous and asynchronous FHIR clients with new error cases +* Import new error classes in client implementations +* 🌿 Generated with Fern + ## 9.0.0 - 2026-03-04 ### Breaking Changes diff --git a/src/main/java/com/phenoml/api/core/ClientOptions.java b/src/main/java/com/phenoml/api/core/ClientOptions.java index bb970cf..b74b9a0 100644 --- a/src/main/java/com/phenoml/api/core/ClientOptions.java +++ b/src/main/java/com/phenoml/api/core/ClientOptions.java @@ -32,10 +32,10 @@ private ClientOptions( this.headers.putAll(headers); this.headers.putAll(new HashMap() { { - put("User-Agent", "com.phenoml.maven:phenoml-java-sdk/9.0.1"); + put("User-Agent", "com.phenoml.maven:phenoml-java-sdk/9.1.0"); put("X-Fern-Language", "JAVA"); put("X-Fern-SDK-Name", "com.phenoml.fern:api-sdk"); - put("X-Fern-SDK-Version", "9.0.1"); + put("X-Fern-SDK-Version", "9.1.0"); } }); this.headerSuppliers = headerSuppliers; diff --git a/src/main/java/com/phenoml/api/resources/fhir/AsyncRawFhirClient.java b/src/main/java/com/phenoml/api/resources/fhir/AsyncRawFhirClient.java index bd71ab2..3b1661a 100644 --- a/src/main/java/com/phenoml/api/resources/fhir/AsyncRawFhirClient.java +++ b/src/main/java/com/phenoml/api/resources/fhir/AsyncRawFhirClient.java @@ -16,6 +16,8 @@ import com.phenoml.api.resources.fhir.errors.BadRequestError; import com.phenoml.api.resources.fhir.errors.InternalServerError; import com.phenoml.api.resources.fhir.errors.NotFoundError; +import com.phenoml.api.resources.fhir.errors.ServiceUnavailableError; +import com.phenoml.api.resources.fhir.errors.TooManyRequestsError; import com.phenoml.api.resources.fhir.errors.UnauthorizedError; import com.phenoml.api.resources.fhir.requests.FhirCreateRequest; import com.phenoml.api.resources.fhir.requests.FhirDeleteRequest; @@ -130,6 +132,11 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), response)); return; + case 429: + future.completeExceptionally(new TooManyRequestsError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), + response)); + return; case 500: future.completeExceptionally(new InternalServerError( ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), @@ -140,6 +147,11 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), response)); return; + case 503: + future.completeExceptionally(new ServiceUnavailableError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), + response)); + return; } } catch (JsonProcessingException ignored) { // unable to map error response, throwing generic error @@ -236,6 +248,11 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), response)); return; + case 429: + future.completeExceptionally(new TooManyRequestsError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), + response)); + return; case 500: future.completeExceptionally(new InternalServerError( ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), @@ -246,6 +263,11 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), response)); return; + case 503: + future.completeExceptionally(new ServiceUnavailableError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), + response)); + return; } } catch (JsonProcessingException ignored) { // unable to map error response, throwing generic error @@ -342,6 +364,11 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), response)); return; + case 429: + future.completeExceptionally(new TooManyRequestsError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), + response)); + return; case 500: future.completeExceptionally(new InternalServerError( ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), @@ -352,6 +379,11 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), response)); return; + case 503: + future.completeExceptionally(new ServiceUnavailableError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), + response)); + return; } } catch (JsonProcessingException ignored) { // unable to map error response, throwing generic error @@ -454,6 +486,11 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), response)); return; + case 429: + future.completeExceptionally(new TooManyRequestsError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), + response)); + return; case 500: future.completeExceptionally(new InternalServerError( ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), @@ -464,6 +501,11 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), response)); return; + case 503: + future.completeExceptionally(new ServiceUnavailableError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), + response)); + return; } } catch (JsonProcessingException ignored) { // unable to map error response, throwing generic error @@ -577,6 +619,11 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), response)); return; + case 429: + future.completeExceptionally(new TooManyRequestsError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), + response)); + return; case 500: future.completeExceptionally(new InternalServerError( ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), @@ -587,6 +634,11 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), response)); return; + case 503: + future.completeExceptionally(new ServiceUnavailableError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), + response)); + return; } } catch (JsonProcessingException ignored) { // unable to map error response, throwing generic error @@ -684,6 +736,11 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), response)); return; + case 429: + future.completeExceptionally(new TooManyRequestsError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), + response)); + return; case 500: future.completeExceptionally(new InternalServerError( ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), @@ -694,6 +751,11 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), response)); return; + case 503: + future.completeExceptionally(new ServiceUnavailableError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), + response)); + return; } } catch (JsonProcessingException ignored) { // unable to map error response, throwing generic error diff --git a/src/main/java/com/phenoml/api/resources/fhir/RawFhirClient.java b/src/main/java/com/phenoml/api/resources/fhir/RawFhirClient.java index fec50f9..381ce7b 100644 --- a/src/main/java/com/phenoml/api/resources/fhir/RawFhirClient.java +++ b/src/main/java/com/phenoml/api/resources/fhir/RawFhirClient.java @@ -16,6 +16,8 @@ import com.phenoml.api.resources.fhir.errors.BadRequestError; import com.phenoml.api.resources.fhir.errors.InternalServerError; import com.phenoml.api.resources.fhir.errors.NotFoundError; +import com.phenoml.api.resources.fhir.errors.ServiceUnavailableError; +import com.phenoml.api.resources.fhir.errors.TooManyRequestsError; import com.phenoml.api.resources.fhir.errors.UnauthorizedError; import com.phenoml.api.resources.fhir.requests.FhirCreateRequest; import com.phenoml.api.resources.fhir.requests.FhirDeleteRequest; @@ -114,12 +116,18 @@ public PhenomlClientHttpResponse search( case 404: throw new NotFoundError( ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), response); + case 429: + throw new TooManyRequestsError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), response); case 500: throw new InternalServerError( ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), response); case 502: throw new BadGatewayError( ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), response); + case 503: + throw new ServiceUnavailableError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), response); } } catch (JsonProcessingException ignored) { // unable to map error response, throwing generic error @@ -198,12 +206,18 @@ public PhenomlClientHttpResponse create( case 401: throw new UnauthorizedError( ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), response); + case 429: + throw new TooManyRequestsError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), response); case 500: throw new InternalServerError( ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), response); case 502: throw new BadGatewayError( ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), response); + case 503: + throw new ServiceUnavailableError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), response); } } catch (JsonProcessingException ignored) { // unable to map error response, throwing generic error @@ -282,12 +296,18 @@ public PhenomlClientHttpResponse upsert( case 401: throw new UnauthorizedError( ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), response); + case 429: + throw new TooManyRequestsError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), response); case 500: throw new InternalServerError( ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), response); case 502: throw new BadGatewayError( ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), response); + case 503: + throw new ServiceUnavailableError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), response); } } catch (JsonProcessingException ignored) { // unable to map error response, throwing generic error @@ -370,12 +390,18 @@ public PhenomlClientHttpResponse> delete( case 404: throw new NotFoundError( ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), response); + case 429: + throw new TooManyRequestsError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), response); case 500: throw new InternalServerError( ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), response); case 502: throw new BadGatewayError( ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), response); + case 503: + throw new ServiceUnavailableError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), response); } } catch (JsonProcessingException ignored) { // unable to map error response, throwing generic error @@ -469,12 +495,18 @@ public PhenomlClientHttpResponse patch( case 404: throw new NotFoundError( ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), response); + case 429: + throw new TooManyRequestsError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), response); case 500: throw new InternalServerError( ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), response); case 502: throw new BadGatewayError( ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), response); + case 503: + throw new ServiceUnavailableError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), response); } } catch (JsonProcessingException ignored) { // unable to map error response, throwing generic error @@ -554,12 +586,18 @@ public PhenomlClientHttpResponse executeBundle( case 401: throw new UnauthorizedError( ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), response); + case 429: + throw new TooManyRequestsError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), response); case 500: throw new InternalServerError( ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), response); case 502: throw new BadGatewayError( ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ErrorResponse.class), response); + case 503: + throw new ServiceUnavailableError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), response); } } catch (JsonProcessingException ignored) { // unable to map error response, throwing generic error diff --git a/src/main/java/com/phenoml/api/resources/fhir/errors/ServiceUnavailableError.java b/src/main/java/com/phenoml/api/resources/fhir/errors/ServiceUnavailableError.java new file mode 100644 index 0000000..593e29b --- /dev/null +++ b/src/main/java/com/phenoml/api/resources/fhir/errors/ServiceUnavailableError.java @@ -0,0 +1,32 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.phenoml.api.resources.fhir.errors; + +import com.phenoml.api.core.PhenomlClientApiException; +import okhttp3.Response; + +public final class ServiceUnavailableError extends PhenomlClientApiException { + /** + * The body of the response that triggered the exception. + */ + private final Object body; + + public ServiceUnavailableError(Object body) { + super("ServiceUnavailableError", 503, body); + this.body = body; + } + + public ServiceUnavailableError(Object body, Response rawResponse) { + super("ServiceUnavailableError", 503, body, rawResponse); + this.body = body; + } + + /** + * @return the body + */ + @java.lang.Override + public Object body() { + return this.body; + } +} diff --git a/src/main/java/com/phenoml/api/resources/fhir/errors/TooManyRequestsError.java b/src/main/java/com/phenoml/api/resources/fhir/errors/TooManyRequestsError.java new file mode 100644 index 0000000..b40aef0 --- /dev/null +++ b/src/main/java/com/phenoml/api/resources/fhir/errors/TooManyRequestsError.java @@ -0,0 +1,33 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.phenoml.api.resources.fhir.errors; + +import com.phenoml.api.core.PhenomlClientApiException; +import com.phenoml.api.resources.fhir.types.ErrorResponse; +import okhttp3.Response; + +public final class TooManyRequestsError extends PhenomlClientApiException { + /** + * The body of the response that triggered the exception. + */ + private final ErrorResponse body; + + public TooManyRequestsError(ErrorResponse body) { + super("TooManyRequestsError", 429, body); + this.body = body; + } + + public TooManyRequestsError(ErrorResponse body, Response rawResponse) { + super("TooManyRequestsError", 429, body, rawResponse); + this.body = body; + } + + /** + * @return the body + */ + @java.lang.Override + public ErrorResponse body() { + return this.body; + } +}