Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## Unreleased

- `TypeSafePaymentRequiredException` (402) and `TypeSafePayloadTooLargeException` (413) join the other status-specific exceptions; both statuses previously fell through to the generic `TypeSafeApiException` (#8).

## 0.3.0 - 2026-09-19

- Async API: `systemOneAsync` mirroring every `systemOne` overload and `models().listAsync()` return `CompletableFuture`s driven by `HttpClient.sendAsync`, so retries back off without holding a thread, honoring the same per-call `RequestOptions` and failing with the same `TypeSafeException` subclasses as the blocking calls (#4).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,10 @@ public static TypeSafeApiException fromResponse(int status, @Nullable String bod
return switch (status) {
case 400 -> new TypeSafeBadRequestException(status, body, headers);
case 401 -> new TypeSafeAuthenticationException(status, body, headers);
case 402 -> new TypeSafePaymentRequiredException(status, body, headers);
case 403 -> new TypeSafePermissionDeniedException(status, body, headers);
case 404 -> new TypeSafeNotFoundException(status, body, headers);
case 413 -> new TypeSafePayloadTooLargeException(status, body, headers);
case 422 -> new TypeSafeUnprocessableEntityException(status, body, headers);
case 429 -> new TypeSafeRateLimitException(status, body, headers);
default -> status >= 500 ? new TypeSafeInternalServerException(status, body, headers) : new TypeSafeApiException(status, body, headers);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.github.premocloud.typesafe;

import org.jspecify.annotations.Nullable;

import java.net.http.HttpHeaders;

/** HTTP 413: the request body exceeded the API's size limit. */
public class TypeSafePayloadTooLargeException extends TypeSafeApiException {

TypeSafePayloadTooLargeException(int status, @Nullable String body, HttpHeaders headers) {
super(status, body, headers);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.github.premocloud.typesafe;

import org.jspecify.annotations.Nullable;

import java.net.http.HttpHeaders;

/** HTTP 402: payment is required, e.g. the account is out of credits. */
public class TypeSafePaymentRequiredException extends TypeSafeApiException {

TypeSafePaymentRequiredException(int status, @Nullable String body, HttpHeaders headers) {
super(status, body, headers);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,14 @@ void errorsMapToStatusSpecificExceptionsWithExtractedMessages() {
server.reply(400, "");
assertEquals("400 status code (no body)", assertThrows(TypeSafeBadRequestException.class, () -> client.systemOne(spamRequest())).getMessage());

server.reply(402, "{\"error\":\"out of credits\"}");
assertEquals("402 out of credits",
assertThrows(TypeSafePaymentRequiredException.class, () -> client.systemOne(spamRequest())).getMessage());

server.reply(413, "{\"error\":\"payload too large\"}");
assertEquals("413 payload too large",
assertThrows(TypeSafePayloadTooLargeException.class, () -> client.systemOne(spamRequest())).getMessage());

server.reply(503, "upstream down");
assertEquals("503 upstream down", assertThrows(TypeSafeInternalServerException.class, () -> client.systemOne(spamRequest())).getMessage());

Expand Down
Loading