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; + } +}