diff --git a/.fern/metadata.json b/.fern/metadata.json index 14a0bfd..5320c98 100644 --- a/.fern/metadata.json +++ b/.fern/metadata.json @@ -1,14 +1,14 @@ { - "cliVersion": "5.28.2", + "cliVersion": "5.143.0", "generatorName": "fernapi/fern-java-sdk", - "generatorVersion": "4.8.10", + "generatorVersion": "4.23.1", "generatorConfig": { "publish-to": "central" }, - "originGitCommit": "f44434d1b1da5712ce6098d90232ec46cf645a0d", + "originGitCommit": "6ef7448ab601e5b372caec45717b0390a4150d2c", "originGitCommitIsDirty": true, "invokedBy": "ci", "requestedVersion": "AUTO", "ciProvider": "unknown", - "sdkVersion": "3.0.0" + "sdkVersion": "3.1.0" } \ No newline at end of file diff --git a/.fern/replay.lock b/.fern/replay.lock new file mode 100644 index 0000000..3966eba --- /dev/null +++ b/.fern/replay.lock @@ -0,0 +1,16 @@ +# DO NOT EDIT MANUALLY - Managed by Fern Replay +version: "1.0" +generations: + - commit_sha: 75f537a2fa3ca2e3708cc98bdc39c5adf9517c58 + tree_hash: 1829d53c8a281f635e4c280457069265dadffdea + timestamp: 2026-09-30T10:10:01.906Z + cli_version: unknown + generator_versions: {} + - commit_sha: 0777c023fb0589f1b7e9651db92b05846a020f2b + tree_hash: 6e94e97ce8f556e90f099649e62c3ba6194f75a2 + timestamp: 2026-09-30T10:10:02.351Z + cli_version: unknown + generator_versions: + fernapi/fern-java-sdk: 4.23.1 +current_generation: 0777c023fb0589f1b7e9651db92b05846a020f2b +patches: [] diff --git a/.fernignore b/.fernignore index fe52d68..b6a86aa 100644 --- a/.fernignore +++ b/.fernignore @@ -4,4 +4,7 @@ LICENSE changelog.md # CI -.github/workflows/sdk-release.yml \ No newline at end of file +.github/workflows/sdk-release.yml +.fern/replay.lock +.fern/replay.yml +.gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..74928d6 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +.fern/replay.lock linguist-generated=true diff --git a/README.md b/README.md index e0af6fc..c90bb3f 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ Add the dependency in your `build.gradle` file: ```groovy dependencies { - implementation 'com.newscatcherapi:newscatcher-sdk:3.0.0' + implementation 'com.newscatcherapi:newscatcher-sdk:3.1.0' } ``` @@ -55,7 +55,7 @@ Add the dependency in your `pom.xml` file: com.newscatcherapi newscatcher-sdk - 3.0.0 + 3.1.0 ``` @@ -364,3 +364,4 @@ For release notes and changelog, visit our [GitHub releases page](https://github + diff --git a/build.gradle b/build.gradle index ae2ab76..1da1926 100644 --- a/build.gradle +++ b/build.gradle @@ -1,9 +1,19 @@ +buildscript { + repositories { + gradlePluginPortal() + } + dependencies { + if (JavaVersion.current().isJava11Compatible()) { + classpath 'cl.franciscosolis:SonatypeCentralUpload:1.0.3' + } + } +} + plugins { id 'java-library' id 'maven-publish' id 'com.diffplug.spotless' version '6.11.0' id 'signing' - id 'cl.franciscosolis.sonatype-central-upload' version '1.0.3' } repositories { @@ -47,7 +57,7 @@ java { group = 'com.newscatcherapi' -version = '3.0.0' +version = '3.1.0' jar { dependsOn(":generatePomFileForMavenPublication") @@ -78,7 +88,7 @@ publishing { maven(MavenPublication) { groupId = 'com.newscatcherapi' artifactId = 'newscatcher-sdk' - version = '3.0.0' + version = '3.1.0' from components.java pom { name = 'newscatcher' @@ -105,19 +115,25 @@ publishing { } } -sonatypeCentralUpload { - username = "$System.env.MAVEN_USERNAME" - password = "$System.env.MAVEN_PASSWORD" +if (JavaVersion.current().isJava11Compatible()) { + apply plugin: 'cl.franciscosolis.sonatype-central-upload' - archives = files( - "$buildDir/libs/newscatcher-sdk-" + version + ".jar", - "$buildDir/libs/newscatcher-sdk-" + version + "-sources.jar", - "$buildDir/libs/newscatcher-sdk-" + version + "-javadoc.jar" - ) + sonatypeCentralUpload { + username = "$System.env.MAVEN_USERNAME" + password = "$System.env.MAVEN_PASSWORD" - pom = file("$buildDir/publications/maven/pom-default.xml") - signingKey = "$System.env.MAVEN_SIGNATURE_SECRET_KEY" - signingKeyPassphrase = "$System.env.MAVEN_SIGNATURE_PASSWORD" + archives = files( + "$buildDir/libs/newscatcher-sdk-" + version + ".jar", + "$buildDir/libs/newscatcher-sdk-" + version + "-sources.jar", + "$buildDir/libs/newscatcher-sdk-" + version + "-javadoc.jar" + ) + + pom = file("$buildDir/publications/maven/pom-default.xml") + signingKey = "$System.env.MAVEN_SIGNATURE_SECRET_KEY" + signingKeyPassphrase = "$System.env.MAVEN_SIGNATURE_PASSWORD" + } + + sonatypeCentralUpload.dependsOn build } signing { @@ -127,5 +143,3 @@ signing { useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword) sign publishing.publications.maven } - -sonatypeCentralUpload.dependsOn build diff --git a/changelog.md b/changelog.md index b528e80..14517d2 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,17 @@ +## [3.1.0] - 2026-09-30 +### Added +- **`NewscatcherApiClient` / `AsyncNewscatcherApiClient`** — both now implement `AutoCloseable`, enabling safe use in try-with-resources blocks. +- **`ClientOptions.close()` and `ClientOptions.isClosed()`** — explicit lifecycle management that shuts down the OkHttpClient dispatcher, connection pool, and any tracked WebSocket clients. +- **`RequestOptions.Builder.maxRetries(Integer)`** — overrides the client-wide retry count for a single request across all resource clients (sync and async). +- **`initialRetryDelayMillis()`, `maxRetryDelayMillis()`, and `retryJitterFactor()`** — new builder methods on `NewscatcherApiClientBuilder`, `AsyncNewscatcherApiClientBuilder`, and `ClientOptions.Builder` for fine-grained exponential backoff control. +- **`ResponseDecompressionInterceptor`** — transparently decompresses gzip- and deflate-encoded response bodies, including responses to requests with an explicit `Accept-Encoding` header. + +### Fixed +- **`setAuthentication()`** — no longer sends a null `x-api-token` header when `apiKey` is `null` in both builder classes. +- **`DateTimeDeserializer`** — now falls back to space-separated ISO 8601 and RFC 1123 date formats when standard ISO 8601 parsing fails. +- **`RetryInterceptor`** — response bodies are buffered before retry, call timeouts are suspended during backoff sleeps, and transport errors during retry no longer discard the last successful response. +- **Deserialization error surfacing** — all sync and async resource clients now catch `JsonProcessingException` and throw a descriptive `NewscatcherApiException` instead of routing failures through the generic error handler. + ## 3.0.0 - 2026-05-19 ### Breaking Changes * **`NlpDataEntity.getSummaryTranslated()`** — renamed to `getTranslationSummary()`; the underlying JSON field also changed from `summary_translated` to `translation_summary`. Update all call sites to use `getTranslationSummary()` and `Builder.translationSummary(...)`. diff --git a/src/main/java/com/newscatcher/api/AsyncNewscatcherApiClient.java b/src/main/java/com/newscatcher/api/AsyncNewscatcherApiClient.java index 6026e6c..9351dbd 100644 --- a/src/main/java/com/newscatcher/api/AsyncNewscatcherApiClient.java +++ b/src/main/java/com/newscatcher/api/AsyncNewscatcherApiClient.java @@ -15,7 +15,7 @@ import com.newscatcher.api.resources.subscription.AsyncSubscriptionClient; import java.util.function.Supplier; -public class AsyncNewscatcherApiClient { +public class AsyncNewscatcherApiClient implements AutoCloseable { protected final ClientOptions clientOptions; protected final Supplier searchClient; @@ -78,6 +78,16 @@ public AsyncSubscriptionClient subscription() { return this.subscriptionClient.get(); } + /** + * Releases resources owned by this client: any WebSocket clients still connected through + * it are disconnected first, then the SDK-owned HTTP client is shut down. See + * {@code ClientOptions.close()} for what is and is not released. + */ + @Override + public void close() { + this.clientOptions.close(); + } + public static AsyncNewscatcherApiClientBuilder builder() { return new AsyncNewscatcherApiClientBuilder(); } diff --git a/src/main/java/com/newscatcher/api/AsyncNewscatcherApiClientBuilder.java b/src/main/java/com/newscatcher/api/AsyncNewscatcherApiClientBuilder.java index fd69583..66c4022 100644 --- a/src/main/java/com/newscatcher/api/AsyncNewscatcherApiClientBuilder.java +++ b/src/main/java/com/newscatcher/api/AsyncNewscatcherApiClientBuilder.java @@ -16,6 +16,12 @@ public class AsyncNewscatcherApiClientBuilder { private Optional maxRetries = Optional.empty(); + private Optional initialRetryDelayMillis = Optional.empty(); + + private Optional maxRetryDelayMillis = Optional.empty(); + + private Optional retryJitterFactor = Optional.empty(); + private final Map customHeaders = new HashMap<>(); private String apiKey = null; @@ -60,6 +66,30 @@ public AsyncNewscatcherApiClientBuilder maxRetries(int maxRetries) { return this; } + /** + * Sets the initial delay (in milliseconds) used for exponential backoff between retries. Defaults to 1000 milliseconds. + */ + public AsyncNewscatcherApiClientBuilder initialRetryDelayMillis(long initialRetryDelayMillis) { + this.initialRetryDelayMillis = Optional.of(initialRetryDelayMillis); + return this; + } + + /** + * Sets the maximum delay (in milliseconds) between retries. Defaults to 60000 milliseconds. + */ + public AsyncNewscatcherApiClientBuilder maxRetryDelayMillis(long maxRetryDelayMillis) { + this.maxRetryDelayMillis = Optional.of(maxRetryDelayMillis); + return this; + } + + /** + * Sets the jitter factor (between 0 and 1) applied to retry delays. Defaults to 0.2. + */ + public AsyncNewscatcherApiClientBuilder retryJitterFactor(double retryJitterFactor) { + this.retryJitterFactor = Optional.of(retryJitterFactor); + return this; + } + /** * Sets the underlying OkHttp client */ @@ -130,7 +160,9 @@ protected void setEnvironment(ClientOptions.Builder builder) { * } */ protected void setAuthentication(ClientOptions.Builder builder) { - builder.addHeader("x-api-token", this.apiKey); + if (this.apiKey != null) { + builder.addHeader("x-api-token", this.apiKey); + } } /** @@ -155,6 +187,15 @@ protected void setRetries(ClientOptions.Builder builder) { if (this.maxRetries.isPresent()) { builder.maxRetries(this.maxRetries.get()); } + if (this.initialRetryDelayMillis.isPresent()) { + builder.initialRetryDelayMillis(this.initialRetryDelayMillis.get()); + } + if (this.maxRetryDelayMillis.isPresent()) { + builder.maxRetryDelayMillis(this.maxRetryDelayMillis.get()); + } + if (this.retryJitterFactor.isPresent()) { + builder.retryJitterFactor(this.retryJitterFactor.get()); + } } /** diff --git a/src/main/java/com/newscatcher/api/NewscatcherApiClient.java b/src/main/java/com/newscatcher/api/NewscatcherApiClient.java index 81801da..b0c4a68 100644 --- a/src/main/java/com/newscatcher/api/NewscatcherApiClient.java +++ b/src/main/java/com/newscatcher/api/NewscatcherApiClient.java @@ -15,7 +15,7 @@ import com.newscatcher.api.resources.subscription.SubscriptionClient; import java.util.function.Supplier; -public class NewscatcherApiClient { +public class NewscatcherApiClient implements AutoCloseable { protected final ClientOptions clientOptions; protected final Supplier searchClient; @@ -78,6 +78,16 @@ public SubscriptionClient subscription() { return this.subscriptionClient.get(); } + /** + * Releases resources owned by this client: any WebSocket clients still connected through + * it are disconnected first, then the SDK-owned HTTP client is shut down. See + * {@code ClientOptions.close()} for what is and is not released. + */ + @Override + public void close() { + this.clientOptions.close(); + } + public static NewscatcherApiClientBuilder builder() { return new NewscatcherApiClientBuilder(); } diff --git a/src/main/java/com/newscatcher/api/NewscatcherApiClientBuilder.java b/src/main/java/com/newscatcher/api/NewscatcherApiClientBuilder.java index b78089d..38fa042 100644 --- a/src/main/java/com/newscatcher/api/NewscatcherApiClientBuilder.java +++ b/src/main/java/com/newscatcher/api/NewscatcherApiClientBuilder.java @@ -16,6 +16,12 @@ public class NewscatcherApiClientBuilder { private Optional maxRetries = Optional.empty(); + private Optional initialRetryDelayMillis = Optional.empty(); + + private Optional maxRetryDelayMillis = Optional.empty(); + + private Optional retryJitterFactor = Optional.empty(); + private final Map customHeaders = new HashMap<>(); private String apiKey = null; @@ -60,6 +66,30 @@ public NewscatcherApiClientBuilder maxRetries(int maxRetries) { return this; } + /** + * Sets the initial delay (in milliseconds) used for exponential backoff between retries. Defaults to 1000 milliseconds. + */ + public NewscatcherApiClientBuilder initialRetryDelayMillis(long initialRetryDelayMillis) { + this.initialRetryDelayMillis = Optional.of(initialRetryDelayMillis); + return this; + } + + /** + * Sets the maximum delay (in milliseconds) between retries. Defaults to 60000 milliseconds. + */ + public NewscatcherApiClientBuilder maxRetryDelayMillis(long maxRetryDelayMillis) { + this.maxRetryDelayMillis = Optional.of(maxRetryDelayMillis); + return this; + } + + /** + * Sets the jitter factor (between 0 and 1) applied to retry delays. Defaults to 0.2. + */ + public NewscatcherApiClientBuilder retryJitterFactor(double retryJitterFactor) { + this.retryJitterFactor = Optional.of(retryJitterFactor); + return this; + } + /** * Sets the underlying OkHttp client */ @@ -130,7 +160,9 @@ protected void setEnvironment(ClientOptions.Builder builder) { * } */ protected void setAuthentication(ClientOptions.Builder builder) { - builder.addHeader("x-api-token", this.apiKey); + if (this.apiKey != null) { + builder.addHeader("x-api-token", this.apiKey); + } } /** @@ -155,6 +187,15 @@ protected void setRetries(ClientOptions.Builder builder) { if (this.maxRetries.isPresent()) { builder.maxRetries(this.maxRetries.get()); } + if (this.initialRetryDelayMillis.isPresent()) { + builder.initialRetryDelayMillis(this.initialRetryDelayMillis.get()); + } + if (this.maxRetryDelayMillis.isPresent()) { + builder.maxRetryDelayMillis(this.maxRetryDelayMillis.get()); + } + if (this.retryJitterFactor.isPresent()) { + builder.retryJitterFactor(this.retryJitterFactor.get()); + } } /** diff --git a/src/main/java/com/newscatcher/api/core/ClientOptions.java b/src/main/java/com/newscatcher/api/core/ClientOptions.java index 8455eaf..7dde855 100644 --- a/src/main/java/com/newscatcher/api/core/ClientOptions.java +++ b/src/main/java/com/newscatcher/api/core/ClientOptions.java @@ -3,10 +3,14 @@ */ package com.newscatcher.api.core; +import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Supplier; import okhttp3.OkHttpClient; @@ -19,10 +23,22 @@ public final class ClientOptions { private final OkHttpClient httpClient; + private final boolean ownsHttpClient; + + private final AtomicBoolean closed; + + private final Set openWebSockets; + private final int timeout; private final int maxRetries; + private final Optional initialRetryDelayMillis; + + private final Optional maxRetryDelayMillis; + + private final Optional retryJitterFactor; + private final Optional logging; private ClientOptions( @@ -30,24 +46,36 @@ private ClientOptions( Map headers, Map> headerSuppliers, OkHttpClient httpClient, + boolean ownsHttpClient, + AtomicBoolean closed, + Set openWebSockets, int timeout, int maxRetries, + Optional initialRetryDelayMillis, + Optional maxRetryDelayMillis, + Optional retryJitterFactor, Optional logging) { this.environment = environment; this.headers = new HashMap<>(); this.headers.putAll(headers); this.headers.putAll(new HashMap() { { - put("User-Agent", "com.newscatcherapi:newscatcher-sdk/3.0.0"); + put("User-Agent", "com.newscatcherapi.newscatcher-sdk/3.1.0"); put("X-Fern-Language", "JAVA"); - put("X-Fern-SDK-Name", "com.newscatcher.fern:api-sdk"); - put("X-Fern-SDK-Version", "3.0.0"); + put("X-Fern-SDK-Name", "com.newscatcherapi:newscatcher-sdk"); + put("X-Fern-SDK-Version", "3.1.0"); } }); this.headerSuppliers = headerSuppliers; this.httpClient = httpClient; + this.ownsHttpClient = ownsHttpClient; + this.closed = closed; + this.openWebSockets = openWebSockets; this.timeout = timeout; this.maxRetries = maxRetries; + this.initialRetryDelayMillis = initialRetryDelayMillis; + this.maxRetryDelayMillis = maxRetryDelayMillis; + this.retryJitterFactor = retryJitterFactor; this.logging = logging; } @@ -94,6 +122,80 @@ public int maxRetries() { return this.maxRetries; } + public Optional initialRetryDelayMillis() { + return this.initialRetryDelayMillis; + } + + public Optional maxRetryDelayMillis() { + return this.maxRetryDelayMillis; + } + + public Optional retryJitterFactor() { + return this.retryJitterFactor; + } + + /** + * Releases resources owned by this client. Only shuts down the underlying OkHttpClient's + * dispatcher executor and evicts its connection pool when this client created that + * OkHttpClient itself; an OkHttpClient supplied via httpClient is left running, since the + * caller owns its lifecycle. + *

+ * In-flight calls are not cancelled or awaited, and any request issued after this method + * returns fails with a {@code RejectedExecutionException}. Options derived from this one via + * {@code Builder.from(...)} share the same dispatcher and connection pool, so closing either + * releases them for both. Calling this method more than once has no further effect. + *

+ * WebSocket clients created from this client that are still connected are disconnected + * first (whether or not the OkHttpClient is owned), so they stop reconnecting before the + * dispatcher goes away. Any WebSocket connect or reconnect attempted afterwards fails with + * an {@code IllegalStateException} explaining that the client has been closed. + */ + public void close() { + if (!this.closed.compareAndSet(false, true)) { + return; + } + for (AutoCloseable webSocket : new ArrayList<>(this.openWebSockets)) { + try { + webSocket.close(); + } catch (Exception e) { + // best effort; keep closing the remaining sockets and the HTTP client + } + } + this.openWebSockets.clear(); + if (!this.ownsHttpClient) { + return; + } + this.httpClient.dispatcher().executorService().shutdown(); + this.httpClient.connectionPool().evictAll(); + } + + /** + * Returns whether {@link #close()} has been called on this client. + */ + public boolean isClosed() { + return this.closed.get(); + } + + /** + * Tracks a connected WebSocket client so that {@link #close()} disconnects it. + * + * @throws IllegalStateException if this client has already been closed + */ + public void registerWebSocket(AutoCloseable webSocket) { + this.openWebSockets.add(webSocket); + if (this.closed.get()) { + this.openWebSockets.remove(webSocket); + throw new IllegalStateException("root client has been closed"); + } + } + + /** + * Stops tracking a WebSocket client that has been disconnected. + */ + public void unregisterWebSocket(AutoCloseable webSocket) { + this.openWebSockets.remove(webSocket); + } + public Optional logging() { return this.logging; } @@ -111,10 +213,22 @@ public static class Builder { private int maxRetries = 2; + private Optional initialRetryDelayMillis = Optional.empty(); + + private Optional maxRetryDelayMillis = Optional.empty(); + + private Optional retryJitterFactor = Optional.empty(); + private Optional timeout = Optional.empty(); private OkHttpClient httpClient = null; + private boolean ownsHttpClient = true; + + private AtomicBoolean closed; + + private Set openWebSockets; + private Optional logging = Optional.empty(); public Builder environment(Environment environment) { @@ -123,7 +237,9 @@ public Builder environment(Environment environment) { } public Builder addHeader(String key, String value) { - this.headers.put(key, value); + if (value != null) { + this.headers.put(key, value); + } return this; } @@ -156,8 +272,37 @@ public Builder maxRetries(int maxRetries) { return this; } + /** + * Override the initial delay (in milliseconds) used for exponential backoff between retries. Defaults to 1000 milliseconds. + */ + public Builder initialRetryDelayMillis(long initialRetryDelayMillis) { + this.initialRetryDelayMillis = Optional.of(initialRetryDelayMillis); + return this; + } + + /** + * Override the maximum delay (in milliseconds) between retries. Defaults to 60000 milliseconds. + */ + public Builder maxRetryDelayMillis(long maxRetryDelayMillis) { + this.maxRetryDelayMillis = Optional.of(maxRetryDelayMillis); + return this; + } + + /** + * Override the jitter factor (between 0 and 1) applied to retry delays. Defaults to 0.2. + */ + public Builder retryJitterFactor(double retryJitterFactor) { + this.retryJitterFactor = Optional.of(retryJitterFactor); + return this; + } + + /** + * Sets the underlying OkHttp client. The caller retains ownership of its lifecycle: + * close() will not shut down its dispatcher executor or evict its connection pool. + */ public Builder httpClient(OkHttpClient httpClient) { this.httpClient = httpClient; + this.ownsHttpClient = httpClient == null; return this; } @@ -185,22 +330,37 @@ public ClientOptions build() { .connectTimeout(0, TimeUnit.SECONDS) .writeTimeout(0, TimeUnit.SECONDS) .readTimeout(0, TimeUnit.SECONDS) - .addInterceptor(new RetryInterceptor(this.maxRetries)); + .addInterceptor(new RetryInterceptor( + this.maxRetries, + this.initialRetryDelayMillis, + this.maxRetryDelayMillis, + this.retryJitterFactor)); } Logger logger = Logger.from(this.logging); httpClientBuilder.addInterceptor(new LoggingInterceptor(logger)); + httpClientBuilder.addInterceptor(new ResponseDecompressionInterceptor()); this.httpClient = httpClientBuilder.build(); this.timeout = Optional.of(httpClient.callTimeoutMillis() / 1000); + AtomicBoolean closedToUse = this.closed != null ? this.closed : new AtomicBoolean(false); + Set openWebSocketsToUse = + this.openWebSockets != null ? this.openWebSockets : ConcurrentHashMap.newKeySet(); + return new ClientOptions( environment, headers, headerSuppliers, httpClient, + this.ownsHttpClient, + closedToUse, + openWebSocketsToUse, this.timeout.get(), this.maxRetries, + this.initialRetryDelayMillis, + this.maxRetryDelayMillis, + this.retryJitterFactor, this.logging); } @@ -212,9 +372,15 @@ public static Builder from(ClientOptions clientOptions) { builder.environment = clientOptions.environment(); builder.timeout = Optional.of(clientOptions.timeout(null)); builder.httpClient = clientOptions.httpClient(); + builder.ownsHttpClient = clientOptions.ownsHttpClient; + builder.closed = clientOptions.closed; + builder.openWebSockets = clientOptions.openWebSockets; builder.headers.putAll(clientOptions.headers); builder.headerSuppliers.putAll(clientOptions.headerSuppliers); builder.maxRetries = clientOptions.maxRetries(); + builder.initialRetryDelayMillis = clientOptions.initialRetryDelayMillis(); + builder.maxRetryDelayMillis = clientOptions.maxRetryDelayMillis(); + builder.retryJitterFactor = clientOptions.retryJitterFactor(); builder.logging = clientOptions.logging(); return builder; } diff --git a/src/main/java/com/newscatcher/api/core/ConsoleLogger.java b/src/main/java/com/newscatcher/api/core/ConsoleLogger.java index b0c1252..6d7f416 100644 --- a/src/main/java/com/newscatcher/api/core/ConsoleLogger.java +++ b/src/main/java/com/newscatcher/api/core/ConsoleLogger.java @@ -23,6 +23,7 @@ public String format(java.util.logging.LogRecord record) { return record.getLevel() + " - " + record.getMessage() + System.lineSeparator(); } }); + handler.setLevel(Level.ALL); logger.addHandler(handler); logger.setUseParentHandlers(false); logger.setLevel(Level.ALL); diff --git a/src/main/java/com/newscatcher/api/core/DateTimeDeserializer.java b/src/main/java/com/newscatcher/api/core/DateTimeDeserializer.java index f9e016d..6c64f22 100644 --- a/src/main/java/com/newscatcher/api/core/DateTimeDeserializer.java +++ b/src/main/java/com/newscatcher/api/core/DateTimeDeserializer.java @@ -14,11 +14,13 @@ import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalQueries; /** - * Custom deserializer that handles converting ISO8601 dates into {@link OffsetDateTime} objects. + * Custom deserializer that handles converting date-time strings into {@link OffsetDateTime} objects. + * Supports ISO 8601 format, space-separated variants, and RFC 1123 (RFC 2822) format. */ class DateTimeDeserializer extends JsonDeserializer { private static final SimpleModule MODULE; @@ -42,8 +44,21 @@ public OffsetDateTime deserialize(JsonParser parser, DeserializationContext cont if (token == JsonToken.VALUE_NUMBER_INT) { return OffsetDateTime.ofInstant(Instant.ofEpochSecond(parser.getValueAsLong()), ZoneOffset.UTC); } else { - TemporalAccessor temporal = DateTimeFormatter.ISO_DATE_TIME.parseBest( - parser.getValueAsString(), OffsetDateTime::from, LocalDateTime::from); + String value = parser.getValueAsString(); + TemporalAccessor temporal; + try { + temporal = DateTimeFormatter.ISO_DATE_TIME.parseBest(value, OffsetDateTime::from, LocalDateTime::from); + } catch (DateTimeParseException e) { + try { + // Fall back to space-separated format (e.g. "2025-02-15 10:30:00+00:00"). + temporal = DateTimeFormatter.ISO_DATE_TIME.parseBest( + value.replace(' ', 'T'), OffsetDateTime::from, LocalDateTime::from); + } catch (DateTimeParseException e2) { + // Fall back to RFC 1123 format (e.g. "Thu, 07 May 2026 14:23:38 +0000"). + temporal = DateTimeFormatter.RFC_1123_DATE_TIME.parseBest( + value, OffsetDateTime::from, LocalDateTime::from); + } + } if (temporal.query(TemporalQueries.offset()) == null) { return LocalDateTime.from(temporal).atOffset(ZoneOffset.UTC); diff --git a/src/main/java/com/newscatcher/api/core/RequestOptions.java b/src/main/java/com/newscatcher/api/core/RequestOptions.java index a6cb9f7..53e0980 100644 --- a/src/main/java/com/newscatcher/api/core/RequestOptions.java +++ b/src/main/java/com/newscatcher/api/core/RequestOptions.java @@ -16,6 +16,8 @@ public final class RequestOptions { private final TimeUnit timeoutTimeUnit; + private final Optional maxRetries; + private final Map headers; private final Map> headerSuppliers; @@ -28,6 +30,7 @@ private RequestOptions( String apiKey, Optional timeout, TimeUnit timeoutTimeUnit, + Optional maxRetries, Map headers, Map> headerSuppliers, Map queryParameters, @@ -35,6 +38,7 @@ private RequestOptions( this.apiKey = apiKey; this.timeout = timeout; this.timeoutTimeUnit = timeoutTimeUnit; + this.maxRetries = maxRetries; this.headers = headers; this.headerSuppliers = headerSuppliers; this.queryParameters = queryParameters; @@ -49,6 +53,10 @@ public TimeUnit getTimeoutTimeUnit() { return timeoutTimeUnit; } + public Optional getMaxRetries() { + return maxRetries; + } + public Map getHeaders() { Map headers = new HashMap<>(); if (this.apiKey != null) { @@ -80,6 +88,8 @@ public static class Builder { private TimeUnit timeoutTimeUnit = TimeUnit.SECONDS; + private Optional maxRetries = Optional.empty(); + private final Map headers = new HashMap<>(); private final Map> headerSuppliers = new HashMap<>(); @@ -104,6 +114,11 @@ public Builder timeout(Integer timeout, TimeUnit timeoutTimeUnit) { return this; } + public Builder maxRetries(Integer maxRetries) { + this.maxRetries = Optional.of(maxRetries); + return this; + } + public Builder addHeader(String key, String value) { this.headers.put(key, value); return this; @@ -129,6 +144,7 @@ public RequestOptions build() { apiKey, timeout, timeoutTimeUnit, + maxRetries, headers, headerSuppliers, queryParameters, diff --git a/src/main/java/com/newscatcher/api/core/ResponseDecompressionInterceptor.java b/src/main/java/com/newscatcher/api/core/ResponseDecompressionInterceptor.java new file mode 100644 index 0000000..0e75733 --- /dev/null +++ b/src/main/java/com/newscatcher/api/core/ResponseDecompressionInterceptor.java @@ -0,0 +1,56 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.newscatcher.api.core; + +import java.io.IOException; +import java.util.zip.Inflater; +import okhttp3.Interceptor; +import okhttp3.Response; +import okhttp3.ResponseBody; +import okio.GzipSource; +import okio.InflaterSource; +import okio.Okio; +import okio.Source; + +/** + * Decompresses gzip and deflate encoded response bodies. OkHttp only performs + * transparent decompression when it adds the Accept-Encoding header itself, so + * responses to requests with an explicitly set Accept-Encoding header would + * otherwise be returned as raw compressed bytes. + */ +public class ResponseDecompressionInterceptor implements Interceptor { + + @Override + public Response intercept(Chain chain) throws IOException { + return decompress(chain.proceed(chain.request())); + } + + private Response decompress(Response response) { + ResponseBody body = response.body(); + if (body == null) { + return response; + } + String encoding = response.header("Content-Encoding"); + if (encoding == null) { + return response; + } + Source decompressedSource; + if (encoding.equalsIgnoreCase("gzip") || encoding.equalsIgnoreCase("x-gzip")) { + decompressedSource = new GzipSource(body.source()); + } else if (encoding.equalsIgnoreCase("deflate")) { + decompressedSource = new InflaterSource(body.source(), new Inflater()); + } else { + return response; + } + ResponseBody decompressedBody = ResponseBody.create(body.contentType(), -1L, Okio.buffer(decompressedSource)); + return response.newBuilder() + .headers(response.headers() + .newBuilder() + .removeAll("Content-Encoding") + .removeAll("Content-Length") + .build()) + .body(decompressedBody) + .build(); + } +} diff --git a/src/main/java/com/newscatcher/api/core/RetryInterceptor.java b/src/main/java/com/newscatcher/api/core/RetryInterceptor.java index 6a782b7..3855dc7 100644 --- a/src/main/java/com/newscatcher/api/core/RetryInterceptor.java +++ b/src/main/java/com/newscatcher/api/core/RetryInterceptor.java @@ -10,44 +10,100 @@ import java.time.format.DateTimeParseException; import java.util.Optional; import java.util.Random; +import okhttp3.Call; import okhttp3.Interceptor; +import okhttp3.Request; import okhttp3.Response; +import okhttp3.ResponseBody; +import okio.AsyncTimeout; +import okio.Timeout; public class RetryInterceptor implements Interceptor { - private static final Duration INITIAL_RETRY_DELAY = Duration.ofMillis(1000); - private static final Duration MAX_RETRY_DELAY = Duration.ofMillis(60000); - private static final double JITTER_FACTOR = 0.2; + private static final Duration DEFAULT_INITIAL_RETRY_DELAY = Duration.ofMillis(1000); + private static final Duration DEFAULT_MAX_RETRY_DELAY = Duration.ofMillis(60000); + private static final double DEFAULT_JITTER_FACTOR = 0.2; private final int maxRetries; + private final Duration initialRetryDelay; + private final Duration maxRetryDelay; + private final double jitterFactor; private final Random random = new Random(); public RetryInterceptor(int maxRetries) { + this(maxRetries, Optional.empty(), Optional.empty(), Optional.empty()); + } + + public RetryInterceptor( + int maxRetries, + Optional initialRetryDelayMillis, + Optional maxRetryDelayMillis, + Optional jitterFactor) { + initialRetryDelayMillis.ifPresent(delay -> { + if (delay < 0) { + throw new IllegalArgumentException("initialRetryDelayMillis must be non-negative"); + } + }); + maxRetryDelayMillis.ifPresent(delay -> { + if (delay < 0) { + throw new IllegalArgumentException("maxRetryDelayMillis must be non-negative"); + } + }); + jitterFactor.ifPresent(factor -> { + if (factor < 0 || factor > 1) { + throw new IllegalArgumentException("jitterFactor must be between 0 and 1"); + } + }); this.maxRetries = maxRetries; + this.initialRetryDelay = initialRetryDelayMillis.map(Duration::ofMillis).orElse(DEFAULT_INITIAL_RETRY_DELAY); + this.maxRetryDelay = maxRetryDelayMillis.map(Duration::ofMillis).orElse(DEFAULT_MAX_RETRY_DELAY); + this.jitterFactor = jitterFactor.orElse(DEFAULT_JITTER_FACTOR); } @Override public Response intercept(Chain chain) throws IOException { - Response response = chain.proceed(chain.request()); + Request request = chain.request(); + int effectiveMaxRetries = resolveMaxRetries(request); + Response response = chain.proceed(request); if (shouldRetry(response.code())) { - return retryChain(response, chain); + return retryChain(response, chain, effectiveMaxRetries); } return response; } - private Response retryChain(Response response, Chain chain) throws IOException { - ExponentialBackoff backoff = new ExponentialBackoff(this.maxRetries); + private int resolveMaxRetries(Request request) { + MaxRetriesOverride override = request.tag(MaxRetriesOverride.class); + return override != null ? override.getValue() : this.maxRetries; + } + + private Response retryChain(Response response, Chain chain, int maxRetries) throws IOException { + ExponentialBackoff backoff = new ExponentialBackoff(maxRetries); Optional nextBackoff = backoff.nextBackoff(response); while (nextBackoff.isPresent()) { + response = bufferResponse(response); + Optional callTimeout = suspendCallTimeout(chain); try { Thread.sleep(nextBackoff.get().toMillis()); } catch (InterruptedException e) { + response.close(); throw new IOException("Interrupted while trying request", e); + } finally { + callTimeout.ifPresent(AsyncTimeout::enter); + } + Response nextResponse; + try { + nextResponse = chain.proceed(chain.request()); + } catch (IOException e) { + // A retry attempt failed with a transport error (connection reset, unreachable host, ...). The + // error is intentionally dropped: the response already received from the API is more actionable. + // Note that a call-timeout expiry or an explicit cancel() also ends up here, but OkHttp then + // discards whatever the interceptor chain returns and reports the cancellation itself. + return response; } response.close(); - response = chain.proceed(chain.request()); + response = nextResponse; if (shouldRetry(response.code())) { nextBackoff = backoff.nextBackoff(response); } else { @@ -58,6 +114,51 @@ private Response retryChain(Response response, Chain chain) throws IOException { return response; } + /** + * Pauses the OkHttp call timeout so that it applies to each attempt individually rather than to the whole + * retry loop: the backoff wait (e.g. a {@code Retry-After} the size of the call timeout) does not consume the + * budget of the next attempt, and each attempt starts with a fresh budget once {@link AsyncTimeout#enter()} + * is called again. + * + *

{@link AsyncTimeout#exit()} and {@link AsyncTimeout#enter()} must stay strictly paired: {@code exit()} on + * a timeout that is not armed is a no-op, but {@code enter()} on one that is already armed throws + * {@code IllegalStateException}. Every caller must re-enter the returned timeout exactly once, on all paths. + */ + private static Optional suspendCallTimeout(Chain chain) { + Call call = chain.call(); + if (call == null) { + return Optional.empty(); + } + Timeout timeout = call.timeout(); + if (!(timeout instanceof AsyncTimeout)) { + return Optional.empty(); + } + AsyncTimeout callTimeout = (AsyncTimeout) timeout; + callTimeout.exit(); + return Optional.of(callTimeout); + } + + /** + * Reads the body into memory so the response stays usable after the underlying connection + * is released or cancelled by a later attempt. + */ + private static Response bufferResponse(Response response) { + ResponseBody body = response.body(); + if (body == null) { + return response; + } + try { + byte[] bytes = body.bytes(); + Response buffered = response.newBuilder() + .body(ResponseBody.create(bytes, body.contentType())) + .build(); + response.close(); + return buffered; + } catch (IOException e) { + return response; + } + } + /** * Calculates the retry delay from response headers, with fallback to exponential backoff. * Priority: Retry-After > X-RateLimit-Reset > Exponential Backoff @@ -70,7 +171,7 @@ private Duration getRetryDelayFromHeaders(Response response, int retryAttempt) { Optional secondsDelay = tryParseLong(retryAfter) .map(seconds -> seconds * 1000) .filter(delayMs -> delayMs > 0) - .map(delayMs -> Math.min(delayMs, MAX_RETRY_DELAY.toMillis())) + .map(delayMs -> Math.min(delayMs, maxRetryDelay.toMillis())) .map(Duration::ofMillis); if (secondsDelay.isPresent()) { return secondsDelay.get(); @@ -80,7 +181,7 @@ private Duration getRetryDelayFromHeaders(Response response, int retryAttempt) { Optional dateDelay = tryParseHttpDate(retryAfter) .map(resetTime -> resetTime.toInstant().toEpochMilli() - System.currentTimeMillis()) .filter(delayMs -> delayMs > 0) - .map(delayMs -> Math.min(delayMs, MAX_RETRY_DELAY.toMillis())) + .map(delayMs -> Math.min(delayMs, maxRetryDelay.toMillis())) .map(Duration::ofMillis); if (dateDelay.isPresent()) { return dateDelay.get(); @@ -94,7 +195,7 @@ private Duration getRetryDelayFromHeaders(Response response, int retryAttempt) { Optional rateLimitDelay = tryParseLong(rateLimitReset) .map(resetTimeSeconds -> (resetTimeSeconds * 1000) - System.currentTimeMillis()) .filter(delayMs -> delayMs > 0) - .map(delayMs -> Math.min(delayMs, MAX_RETRY_DELAY.toMillis())) + .map(delayMs -> Math.min(delayMs, maxRetryDelay.toMillis())) .map(this::addPositiveJitter) .map(Duration::ofMillis); if (rateLimitDelay.isPresent()) { @@ -103,8 +204,15 @@ private Duration getRetryDelayFromHeaders(Response response, int retryAttempt) { } // Fall back to exponential backoff, with symmetric jitter - long baseDelay = INITIAL_RETRY_DELAY.toMillis() * (1L << retryAttempt); // 2^retryAttempt - long cappedDelay = Math.min(baseDelay, MAX_RETRY_DELAY.toMillis()); + long initialDelayMillis = initialRetryDelay.toMillis(); + long maxDelayMillis = maxRetryDelay.toMillis(); + long cappedDelay; + if (retryAttempt >= Long.SIZE - 1 || initialDelayMillis > (maxDelayMillis >> retryAttempt)) { + // initialDelayMillis * 2^retryAttempt would exceed maxDelayMillis (or overflow) + cappedDelay = maxDelayMillis; + } else { + cappedDelay = Math.min(initialDelayMillis << retryAttempt, maxDelayMillis); // 2^retryAttempt + } return Duration.ofMillis(addSymmetricJitter(cappedDelay)); } @@ -141,7 +249,7 @@ private Optional tryParseHttpDate(String value) { * Used for X-RateLimit-Reset header delays. */ private long addPositiveJitter(long delayMs) { - double jitterMultiplier = 1.0 + (random.nextDouble() * JITTER_FACTOR); + double jitterMultiplier = 1.0 + (random.nextDouble() * jitterFactor); return (long) (delayMs * jitterMultiplier); } @@ -150,7 +258,7 @@ private long addPositiveJitter(long delayMs) { * Used for exponential backoff delays. */ private long addSymmetricJitter(long delayMs) { - double jitterMultiplier = 1.0 + ((random.nextDouble() - 0.5) * JITTER_FACTOR); + double jitterMultiplier = 1.0 + ((random.nextDouble() - 0.5) * jitterFactor); return (long) (delayMs * jitterMultiplier); } @@ -158,6 +266,23 @@ private static boolean shouldRetry(int statusCode) { return statusCode == 408 || statusCode == 429 || statusCode >= 500; } + /** + * Per-request override carried on the OkHttp {@link Request} as a tag. + * When present, the interceptor uses this value instead of the client-wide + * {@code maxRetries} configured at construction time. + */ + public static final class MaxRetriesOverride { + private final int value; + + public MaxRetriesOverride(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + } + private final class ExponentialBackoff { private final int maxNumRetries; diff --git a/src/main/java/com/newscatcher/api/resources/aggregationcount/AsyncRawAggregationCountClient.java b/src/main/java/com/newscatcher/api/resources/aggregationcount/AsyncRawAggregationCountClient.java index ea9757f..3a649c6 100644 --- a/src/main/java/com/newscatcher/api/resources/aggregationcount/AsyncRawAggregationCountClient.java +++ b/src/main/java/com/newscatcher/api/resources/aggregationcount/AsyncRawAggregationCountClient.java @@ -12,6 +12,7 @@ import com.newscatcher.api.core.ObjectMappers; import com.newscatcher.api.core.QueryStringMapper; import com.newscatcher.api.core.RequestOptions; +import com.newscatcher.api.core.RetryInterceptor; import com.newscatcher.api.errors.BadRequestError; import com.newscatcher.api.errors.ForbiddenError; import com.newscatcher.api.errors.InternalServerError; @@ -269,6 +270,15 @@ public CompletableFuture if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -327,6 +337,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new NewscatcherApiApiException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new NewscatcherApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally( new NewscatcherApiException("Network error executing HTTP request", e)); @@ -380,6 +393,15 @@ public CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -438,6 +460,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new NewscatcherApiApiException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new NewscatcherApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally( new NewscatcherApiException("Network error executing HTTP request", e)); diff --git a/src/main/java/com/newscatcher/api/resources/aggregationcount/RawAggregationCountClient.java b/src/main/java/com/newscatcher/api/resources/aggregationcount/RawAggregationCountClient.java index 90a62e0..8e9cbb9 100644 --- a/src/main/java/com/newscatcher/api/resources/aggregationcount/RawAggregationCountClient.java +++ b/src/main/java/com/newscatcher/api/resources/aggregationcount/RawAggregationCountClient.java @@ -12,6 +12,7 @@ import com.newscatcher.api.core.ObjectMappers; import com.newscatcher.api.core.QueryStringMapper; import com.newscatcher.api.core.RequestOptions; +import com.newscatcher.api.core.RetryInterceptor; import com.newscatcher.api.errors.BadRequestError; import com.newscatcher.api.errors.ForbiddenError; import com.newscatcher.api.errors.InternalServerError; @@ -264,6 +265,15 @@ public NewscatcherApiHttpResponse get( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -302,6 +312,8 @@ public NewscatcherApiHttpResponse get( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new NewscatcherApiApiException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new NewscatcherApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new NewscatcherApiException("Network error executing HTTP request", e); } @@ -345,6 +357,15 @@ public NewscatcherApiHttpResponse post( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -383,6 +404,8 @@ public NewscatcherApiHttpResponse post( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new NewscatcherApiApiException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new NewscatcherApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new NewscatcherApiException("Network error executing HTTP request", e); } diff --git a/src/main/java/com/newscatcher/api/resources/authors/AsyncRawAuthorsClient.java b/src/main/java/com/newscatcher/api/resources/authors/AsyncRawAuthorsClient.java index 45ac4e0..5d30d62 100644 --- a/src/main/java/com/newscatcher/api/resources/authors/AsyncRawAuthorsClient.java +++ b/src/main/java/com/newscatcher/api/resources/authors/AsyncRawAuthorsClient.java @@ -12,6 +12,7 @@ import com.newscatcher.api.core.ObjectMappers; import com.newscatcher.api.core.QueryStringMapper; import com.newscatcher.api.core.RequestOptions; +import com.newscatcher.api.core.RetryInterceptor; import com.newscatcher.api.errors.BadRequestError; import com.newscatcher.api.errors.ForbiddenError; import com.newscatcher.api.errors.InternalServerError; @@ -267,6 +268,15 @@ public CompletableFuture> get( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -324,6 +334,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new NewscatcherApiApiException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new NewscatcherApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally( new NewscatcherApiException("Network error executing HTTP request", e)); @@ -376,6 +389,15 @@ public CompletableFuture> post( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -433,6 +455,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new NewscatcherApiApiException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new NewscatcherApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally( new NewscatcherApiException("Network error executing HTTP request", e)); diff --git a/src/main/java/com/newscatcher/api/resources/authors/RawAuthorsClient.java b/src/main/java/com/newscatcher/api/resources/authors/RawAuthorsClient.java index 44c3ee6..607c8d3 100644 --- a/src/main/java/com/newscatcher/api/resources/authors/RawAuthorsClient.java +++ b/src/main/java/com/newscatcher/api/resources/authors/RawAuthorsClient.java @@ -12,6 +12,7 @@ import com.newscatcher.api.core.ObjectMappers; import com.newscatcher.api.core.QueryStringMapper; import com.newscatcher.api.core.RequestOptions; +import com.newscatcher.api.core.RetryInterceptor; import com.newscatcher.api.errors.BadRequestError; import com.newscatcher.api.errors.ForbiddenError; import com.newscatcher.api.errors.InternalServerError; @@ -263,6 +264,15 @@ public NewscatcherApiHttpResponse get( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -300,6 +310,8 @@ public NewscatcherApiHttpResponse get( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new NewscatcherApiApiException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new NewscatcherApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new NewscatcherApiException("Network error executing HTTP request", e); } @@ -343,6 +355,15 @@ public NewscatcherApiHttpResponse post( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -380,6 +401,8 @@ public NewscatcherApiHttpResponse post( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new NewscatcherApiApiException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new NewscatcherApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new NewscatcherApiException("Network error executing HTTP request", e); } diff --git a/src/main/java/com/newscatcher/api/resources/breakingnews/AsyncRawBreakingNewsClient.java b/src/main/java/com/newscatcher/api/resources/breakingnews/AsyncRawBreakingNewsClient.java index c649336..341ca89 100644 --- a/src/main/java/com/newscatcher/api/resources/breakingnews/AsyncRawBreakingNewsClient.java +++ b/src/main/java/com/newscatcher/api/resources/breakingnews/AsyncRawBreakingNewsClient.java @@ -12,6 +12,7 @@ import com.newscatcher.api.core.ObjectMappers; import com.newscatcher.api.core.QueryStringMapper; import com.newscatcher.api.core.RequestOptions; +import com.newscatcher.api.core.RetryInterceptor; import com.newscatcher.api.errors.BadRequestError; import com.newscatcher.api.errors.ForbiddenError; import com.newscatcher.api.errors.InternalServerError; @@ -182,6 +183,15 @@ public CompletableFuture> ge if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -239,6 +249,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new NewscatcherApiApiException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new NewscatcherApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally( new NewscatcherApiException("Network error executing HTTP request", e)); @@ -306,6 +319,15 @@ public CompletableFuture> po if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -363,6 +385,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new NewscatcherApiApiException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new NewscatcherApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally( new NewscatcherApiException("Network error executing HTTP request", e)); diff --git a/src/main/java/com/newscatcher/api/resources/breakingnews/RawBreakingNewsClient.java b/src/main/java/com/newscatcher/api/resources/breakingnews/RawBreakingNewsClient.java index 5f03a20..0fcd6b6 100644 --- a/src/main/java/com/newscatcher/api/resources/breakingnews/RawBreakingNewsClient.java +++ b/src/main/java/com/newscatcher/api/resources/breakingnews/RawBreakingNewsClient.java @@ -12,6 +12,7 @@ import com.newscatcher.api.core.ObjectMappers; import com.newscatcher.api.core.QueryStringMapper; import com.newscatcher.api.core.RequestOptions; +import com.newscatcher.api.core.RetryInterceptor; import com.newscatcher.api.errors.BadRequestError; import com.newscatcher.api.errors.ForbiddenError; import com.newscatcher.api.errors.InternalServerError; @@ -178,6 +179,15 @@ public NewscatcherApiHttpResponse get( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -216,6 +226,8 @@ public NewscatcherApiHttpResponse get( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new NewscatcherApiApiException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new NewscatcherApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new NewscatcherApiException("Network error executing HTTP request", e); } @@ -273,6 +285,15 @@ public NewscatcherApiHttpResponse post( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -311,6 +332,8 @@ public NewscatcherApiHttpResponse post( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new NewscatcherApiApiException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new NewscatcherApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new NewscatcherApiException("Network error executing HTTP request", e); } diff --git a/src/main/java/com/newscatcher/api/resources/latestheadlines/AsyncRawLatestHeadlinesClient.java b/src/main/java/com/newscatcher/api/resources/latestheadlines/AsyncRawLatestHeadlinesClient.java index 4e46f2b..12ae6a3 100644 --- a/src/main/java/com/newscatcher/api/resources/latestheadlines/AsyncRawLatestHeadlinesClient.java +++ b/src/main/java/com/newscatcher/api/resources/latestheadlines/AsyncRawLatestHeadlinesClient.java @@ -12,6 +12,7 @@ import com.newscatcher.api.core.ObjectMappers; import com.newscatcher.api.core.QueryStringMapper; import com.newscatcher.api.core.RequestOptions; +import com.newscatcher.api.core.RetryInterceptor; import com.newscatcher.api.errors.BadRequestError; import com.newscatcher.api.errors.ForbiddenError; import com.newscatcher.api.errors.InternalServerError; @@ -297,6 +298,15 @@ public CompletableFuture> if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -355,6 +365,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new NewscatcherApiApiException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new NewscatcherApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally( new NewscatcherApiException("Network error executing HTTP request", e)); @@ -423,6 +436,15 @@ public CompletableFuture if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -481,6 +503,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new NewscatcherApiApiException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new NewscatcherApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally( new NewscatcherApiException("Network error executing HTTP request", e)); diff --git a/src/main/java/com/newscatcher/api/resources/latestheadlines/RawLatestHeadlinesClient.java b/src/main/java/com/newscatcher/api/resources/latestheadlines/RawLatestHeadlinesClient.java index 48f3bcd..15f735e 100644 --- a/src/main/java/com/newscatcher/api/resources/latestheadlines/RawLatestHeadlinesClient.java +++ b/src/main/java/com/newscatcher/api/resources/latestheadlines/RawLatestHeadlinesClient.java @@ -12,6 +12,7 @@ import com.newscatcher.api.core.ObjectMappers; import com.newscatcher.api.core.QueryStringMapper; import com.newscatcher.api.core.RequestOptions; +import com.newscatcher.api.core.RetryInterceptor; import com.newscatcher.api.errors.BadRequestError; import com.newscatcher.api.errors.ForbiddenError; import com.newscatcher.api.errors.InternalServerError; @@ -291,6 +292,15 @@ public NewscatcherApiHttpResponse get( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -329,6 +339,8 @@ public NewscatcherApiHttpResponse get( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new NewscatcherApiApiException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new NewscatcherApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new NewscatcherApiException("Network error executing HTTP request", e); } @@ -386,6 +398,15 @@ public NewscatcherApiHttpResponse post( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -424,6 +445,8 @@ public NewscatcherApiHttpResponse post( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new NewscatcherApiApiException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new NewscatcherApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new NewscatcherApiException("Network error executing HTTP request", e); } diff --git a/src/main/java/com/newscatcher/api/resources/search/AsyncRawSearchClient.java b/src/main/java/com/newscatcher/api/resources/search/AsyncRawSearchClient.java index b1d4b03..732283b 100644 --- a/src/main/java/com/newscatcher/api/resources/search/AsyncRawSearchClient.java +++ b/src/main/java/com/newscatcher/api/resources/search/AsyncRawSearchClient.java @@ -12,6 +12,7 @@ import com.newscatcher.api.core.ObjectMappers; import com.newscatcher.api.core.QueryStringMapper; import com.newscatcher.api.core.RequestOptions; +import com.newscatcher.api.core.RetryInterceptor; import com.newscatcher.api.errors.BadRequestError; import com.newscatcher.api.errors.ForbiddenError; import com.newscatcher.api.errors.InternalServerError; @@ -334,6 +335,15 @@ public CompletableFuture> get( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -391,6 +401,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new NewscatcherApiApiException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new NewscatcherApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally( new NewscatcherApiException("Network error executing HTTP request", e)); @@ -443,6 +456,15 @@ public CompletableFuture> post( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -500,6 +522,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new NewscatcherApiApiException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new NewscatcherApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally( new NewscatcherApiException("Network error executing HTTP request", e)); diff --git a/src/main/java/com/newscatcher/api/resources/search/RawSearchClient.java b/src/main/java/com/newscatcher/api/resources/search/RawSearchClient.java index 615b8ba..b033684 100644 --- a/src/main/java/com/newscatcher/api/resources/search/RawSearchClient.java +++ b/src/main/java/com/newscatcher/api/resources/search/RawSearchClient.java @@ -12,6 +12,7 @@ import com.newscatcher.api.core.ObjectMappers; import com.newscatcher.api.core.QueryStringMapper; import com.newscatcher.api.core.RequestOptions; +import com.newscatcher.api.core.RetryInterceptor; import com.newscatcher.api.errors.BadRequestError; import com.newscatcher.api.errors.ForbiddenError; import com.newscatcher.api.errors.InternalServerError; @@ -329,6 +330,15 @@ public NewscatcherApiHttpResponse get(GetSearchRequest reques if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -366,6 +376,8 @@ public NewscatcherApiHttpResponse get(GetSearchRequest reques Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new NewscatcherApiApiException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new NewscatcherApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new NewscatcherApiException("Network error executing HTTP request", e); } @@ -409,6 +421,15 @@ public NewscatcherApiHttpResponse post( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -446,6 +467,8 @@ public NewscatcherApiHttpResponse post( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new NewscatcherApiApiException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new NewscatcherApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new NewscatcherApiException("Network error executing HTTP request", e); } diff --git a/src/main/java/com/newscatcher/api/resources/searchbylink/AsyncRawSearchByLinkClient.java b/src/main/java/com/newscatcher/api/resources/searchbylink/AsyncRawSearchByLinkClient.java index 275f1f3..38afea2 100644 --- a/src/main/java/com/newscatcher/api/resources/searchbylink/AsyncRawSearchByLinkClient.java +++ b/src/main/java/com/newscatcher/api/resources/searchbylink/AsyncRawSearchByLinkClient.java @@ -12,6 +12,7 @@ import com.newscatcher.api.core.ObjectMappers; import com.newscatcher.api.core.QueryStringMapper; import com.newscatcher.api.core.RequestOptions; +import com.newscatcher.api.core.RetryInterceptor; import com.newscatcher.api.errors.BadRequestError; import com.newscatcher.api.errors.ForbiddenError; import com.newscatcher.api.errors.InternalServerError; @@ -113,6 +114,15 @@ public CompletableFuture> get( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -170,6 +180,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new NewscatcherApiApiException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new NewscatcherApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally( new NewscatcherApiException("Network error executing HTTP request", e)); @@ -236,6 +249,15 @@ public CompletableFuture> post( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -293,6 +315,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new NewscatcherApiApiException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new NewscatcherApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally( new NewscatcherApiException("Network error executing HTTP request", e)); diff --git a/src/main/java/com/newscatcher/api/resources/searchbylink/RawSearchByLinkClient.java b/src/main/java/com/newscatcher/api/resources/searchbylink/RawSearchByLinkClient.java index f09ef72..395c2e4 100644 --- a/src/main/java/com/newscatcher/api/resources/searchbylink/RawSearchByLinkClient.java +++ b/src/main/java/com/newscatcher/api/resources/searchbylink/RawSearchByLinkClient.java @@ -12,6 +12,7 @@ import com.newscatcher.api.core.ObjectMappers; import com.newscatcher.api.core.QueryStringMapper; import com.newscatcher.api.core.RequestOptions; +import com.newscatcher.api.core.RetryInterceptor; import com.newscatcher.api.errors.BadRequestError; import com.newscatcher.api.errors.ForbiddenError; import com.newscatcher.api.errors.InternalServerError; @@ -109,6 +110,15 @@ public NewscatcherApiHttpResponse get( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -146,6 +156,8 @@ public NewscatcherApiHttpResponse get( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new NewscatcherApiApiException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new NewscatcherApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new NewscatcherApiException("Network error executing HTTP request", e); } @@ -203,6 +215,15 @@ public NewscatcherApiHttpResponse post( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -240,6 +261,8 @@ public NewscatcherApiHttpResponse post( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new NewscatcherApiApiException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new NewscatcherApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new NewscatcherApiException("Network error executing HTTP request", e); } diff --git a/src/main/java/com/newscatcher/api/resources/sources/AsyncRawSourcesClient.java b/src/main/java/com/newscatcher/api/resources/sources/AsyncRawSourcesClient.java index 0a06241..328855f 100644 --- a/src/main/java/com/newscatcher/api/resources/sources/AsyncRawSourcesClient.java +++ b/src/main/java/com/newscatcher/api/resources/sources/AsyncRawSourcesClient.java @@ -12,6 +12,7 @@ import com.newscatcher.api.core.ObjectMappers; import com.newscatcher.api.core.QueryStringMapper; import com.newscatcher.api.core.RequestOptions; +import com.newscatcher.api.core.RetryInterceptor; import com.newscatcher.api.errors.BadRequestError; import com.newscatcher.api.errors.ForbiddenError; import com.newscatcher.api.errors.InternalServerError; @@ -137,6 +138,15 @@ public CompletableFuture> get( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -194,6 +204,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new NewscatcherApiApiException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new NewscatcherApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally( new NewscatcherApiException("Network error executing HTTP request", e)); @@ -260,6 +273,15 @@ public CompletableFuture> post( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -317,6 +339,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new NewscatcherApiApiException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new NewscatcherApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally( new NewscatcherApiException("Network error executing HTTP request", e)); diff --git a/src/main/java/com/newscatcher/api/resources/sources/RawSourcesClient.java b/src/main/java/com/newscatcher/api/resources/sources/RawSourcesClient.java index a553cc3..32c68bf 100644 --- a/src/main/java/com/newscatcher/api/resources/sources/RawSourcesClient.java +++ b/src/main/java/com/newscatcher/api/resources/sources/RawSourcesClient.java @@ -12,6 +12,7 @@ import com.newscatcher.api.core.ObjectMappers; import com.newscatcher.api.core.QueryStringMapper; import com.newscatcher.api.core.RequestOptions; +import com.newscatcher.api.core.RetryInterceptor; import com.newscatcher.api.errors.BadRequestError; import com.newscatcher.api.errors.ForbiddenError; import com.newscatcher.api.errors.InternalServerError; @@ -133,6 +134,15 @@ public NewscatcherApiHttpResponse get( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -170,6 +180,8 @@ public NewscatcherApiHttpResponse get( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new NewscatcherApiApiException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new NewscatcherApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new NewscatcherApiException("Network error executing HTTP request", e); } @@ -227,6 +239,15 @@ public NewscatcherApiHttpResponse post( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -264,6 +285,8 @@ public NewscatcherApiHttpResponse post( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new NewscatcherApiApiException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new NewscatcherApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new NewscatcherApiException("Network error executing HTTP request", e); } diff --git a/src/main/java/com/newscatcher/api/resources/subscription/AsyncRawSubscriptionClient.java b/src/main/java/com/newscatcher/api/resources/subscription/AsyncRawSubscriptionClient.java index 10a59c2..be1ef33 100644 --- a/src/main/java/com/newscatcher/api/resources/subscription/AsyncRawSubscriptionClient.java +++ b/src/main/java/com/newscatcher/api/resources/subscription/AsyncRawSubscriptionClient.java @@ -10,6 +10,7 @@ import com.newscatcher.api.core.NewscatcherApiHttpResponse; import com.newscatcher.api.core.ObjectMappers; import com.newscatcher.api.core.RequestOptions; +import com.newscatcher.api.core.RetryInterceptor; import com.newscatcher.api.errors.BadRequestError; import com.newscatcher.api.errors.ForbiddenError; import com.newscatcher.api.errors.InternalServerError; @@ -68,6 +69,15 @@ public CompletableFuture> ge if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -125,6 +135,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new NewscatcherApiApiException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new NewscatcherApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally( new NewscatcherApiException("Network error executing HTTP request", e)); @@ -168,6 +181,15 @@ public CompletableFuture> po if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -225,6 +247,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new NewscatcherApiApiException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new NewscatcherApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally( new NewscatcherApiException("Network error executing HTTP request", e)); diff --git a/src/main/java/com/newscatcher/api/resources/subscription/RawSubscriptionClient.java b/src/main/java/com/newscatcher/api/resources/subscription/RawSubscriptionClient.java index f705b64..913b9e3 100644 --- a/src/main/java/com/newscatcher/api/resources/subscription/RawSubscriptionClient.java +++ b/src/main/java/com/newscatcher/api/resources/subscription/RawSubscriptionClient.java @@ -10,6 +10,7 @@ import com.newscatcher.api.core.NewscatcherApiHttpResponse; import com.newscatcher.api.core.ObjectMappers; import com.newscatcher.api.core.RequestOptions; +import com.newscatcher.api.core.RetryInterceptor; import com.newscatcher.api.errors.BadRequestError; import com.newscatcher.api.errors.ForbiddenError; import com.newscatcher.api.errors.InternalServerError; @@ -64,6 +65,15 @@ public NewscatcherApiHttpResponse get(RequestOptions re if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -102,6 +112,8 @@ public NewscatcherApiHttpResponse get(RequestOptions re Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new NewscatcherApiApiException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new NewscatcherApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new NewscatcherApiException("Network error executing HTTP request", e); } @@ -136,6 +148,15 @@ public NewscatcherApiHttpResponse post(RequestOptions r if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -174,6 +195,8 @@ public NewscatcherApiHttpResponse post(RequestOptions r Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new NewscatcherApiApiException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new NewscatcherApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new NewscatcherApiException("Network error executing HTTP request", e); } diff --git a/src/main/java/com/newscatcher/api/types/AggregationCountResponseDto.java b/src/main/java/com/newscatcher/api/types/AggregationCountResponseDto.java index 0a7c5d9..5f05e48 100644 --- a/src/main/java/com/newscatcher/api/types/AggregationCountResponseDto.java +++ b/src/main/java/com/newscatcher/api/types/AggregationCountResponseDto.java @@ -247,7 +247,6 @@ public Builder from(AggregationCountResponseDto other) { } /** - *

The status of the response.

*

The status of the response.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -259,7 +258,6 @@ public TotalHitsStage status(@NotNull String status) { } /** - *

The total number of articles matching the search criteria.

*

The total number of articles matching the search criteria.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -271,7 +269,6 @@ public PageStage totalHits(int totalHits) { } /** - *

The current page number of the results.

*

The current page number of the results.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -283,7 +280,6 @@ public TotalPagesStage page(int page) { } /** - *

The total number of pages available for the given search criteria.

*

The total number of pages available for the given search criteria.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -295,7 +291,6 @@ public PageSizeStage totalPages(int totalPages) { } /** - *

The number of articles per page.

*

The number of articles per page.

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/newscatcher/api/types/AllLinksDataItem.java b/src/main/java/com/newscatcher/api/types/AllLinksDataItem.java index f514954..97abc19 100644 --- a/src/main/java/com/newscatcher/api/types/AllLinksDataItem.java +++ b/src/main/java/com/newscatcher/api/types/AllLinksDataItem.java @@ -140,7 +140,6 @@ public Builder from(AllLinksDataItem other) { } /** - *

The domain of the linked URL.

*

The domain of the linked URL.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -152,7 +151,6 @@ public LinkStage domainUrl(@NotNull String domainUrl) { } /** - *

The complete URL of the link.

*

The complete URL of the link.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -164,7 +162,6 @@ public TextStage link(@NotNull String link) { } /** - *

The anchor text of the link.

*

The anchor text of the link.

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/newscatcher/api/types/ArticleEntity.java b/src/main/java/com/newscatcher/api/types/ArticleEntity.java index ab7d4ee..6e48e58 100644 --- a/src/main/java/com/newscatcher/api/types/ArticleEntity.java +++ b/src/main/java/com/newscatcher/api/types/ArticleEntity.java @@ -1045,7 +1045,6 @@ public Builder from(ArticleEntity other) { } /** - *

The title of the article.

*

The title of the article.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -1057,7 +1056,6 @@ public LinkStage title(@NotNull String title) { } /** - *

The URL link to the article.

*

The URL link to the article.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -1069,7 +1067,6 @@ public DomainUrlStage link(@NotNull String link) { } /** - *

The domain URL of the article.

*

The domain URL of the article.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -1081,7 +1078,6 @@ public FullDomainUrlStage domainUrl(@NotNull String domainUrl) { } /** - *

The full domain URL of the article.

*

The full domain URL of the article.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -1093,7 +1089,6 @@ public ParentUrlStage fullDomainUrl(@NotNull String fullDomainUrl) { } /** - *

The categorical URL of the article.

*

The categorical URL of the article.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -1105,7 +1100,6 @@ public RankStage parentUrl(@NotNull String parentUrl) { } /** - *

The rank of the article's source.

*

The rank of the article's source.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -1117,7 +1111,6 @@ public IdStage rank(int rank) { } /** - *

The unique identifier for the article.

*

The unique identifier for the article.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -1129,7 +1122,6 @@ public ScoreStage id(@NotNull String id) { } /** - *

The relevance score of the article.

*

The relevance score of the article.

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/newscatcher/api/types/BaseSearchResponseDto.java b/src/main/java/com/newscatcher/api/types/BaseSearchResponseDto.java index 634553c..6ff57d1 100644 --- a/src/main/java/com/newscatcher/api/types/BaseSearchResponseDto.java +++ b/src/main/java/com/newscatcher/api/types/BaseSearchResponseDto.java @@ -198,7 +198,6 @@ public Builder from(BaseSearchResponseDto other) { } /** - *

The status of the response.

*

The status of the response.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -210,7 +209,6 @@ public TotalHitsStage status(@NotNull String status) { } /** - *

The total number of articles matching the search criteria.

*

The total number of articles matching the search criteria.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -222,7 +220,6 @@ public PageStage totalHits(int totalHits) { } /** - *

The current page number of the results.

*

The current page number of the results.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -234,7 +231,6 @@ public TotalPagesStage page(int page) { } /** - *

The total number of pages available for the given search criteria.

*

The total number of pages available for the given search criteria.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -246,7 +242,6 @@ public PageSizeStage totalPages(int totalPages) { } /** - *

The number of articles per page.

*

The number of articles per page.

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/newscatcher/api/types/BreakingNewsArticleEntity.java b/src/main/java/com/newscatcher/api/types/BreakingNewsArticleEntity.java index 27b6cdb..4f65bc4 100644 --- a/src/main/java/com/newscatcher/api/types/BreakingNewsArticleEntity.java +++ b/src/main/java/com/newscatcher/api/types/BreakingNewsArticleEntity.java @@ -954,7 +954,6 @@ public Builder from(BreakingNewsArticleEntity other) { } /** - *

The title of the article.

*

The title of the article.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -966,7 +965,6 @@ public LinkStage title(@NotNull String title) { } /** - *

The URL link to the article.

*

The URL link to the article.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -978,7 +976,6 @@ public DomainUrlStage link(@NotNull String link) { } /** - *

The domain URL of the article.

*

The domain URL of the article.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -990,7 +987,6 @@ public FullDomainUrlStage domainUrl(@NotNull String domainUrl) { } /** - *

The full domain URL of the article.

*

The full domain URL of the article.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -1002,7 +998,6 @@ public ParentUrlStage fullDomainUrl(@NotNull String fullDomainUrl) { } /** - *

The categorical URL of the article.

*

The categorical URL of the article.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -1014,7 +1009,6 @@ public RankStage parentUrl(@NotNull String parentUrl) { } /** - *

The rank of the article's source.

*

The rank of the article's source.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -1026,7 +1020,6 @@ public IdStage rank(int rank) { } /** - *

The unique identifier for the article.

*

The unique identifier for the article.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -1038,7 +1031,6 @@ public ScoreStage id(@NotNull String id) { } /** - *

The relevance score of the article.

*

The relevance score of the article.

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/newscatcher/api/types/BreakingNewsEventEntity.java b/src/main/java/com/newscatcher/api/types/BreakingNewsEventEntity.java index 595c78b..8105aa3 100644 --- a/src/main/java/com/newscatcher/api/types/BreakingNewsEventEntity.java +++ b/src/main/java/com/newscatcher/api/types/BreakingNewsEventEntity.java @@ -149,7 +149,6 @@ public Builder from(BreakingNewsEventEntity other) { } /** - *

Unique identifier for the breaking news event/cluster.

*

Unique identifier for the breaking news event/cluster.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -161,7 +160,6 @@ public ArticlesCountStage eventId(@NotNull String eventId) { } /** - *

Number of articles in this breaking news cluster.

*

Number of articles in this breaking news cluster.

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/newscatcher/api/types/BreakingNewsResponseDto.java b/src/main/java/com/newscatcher/api/types/BreakingNewsResponseDto.java index 27ca1b8..fc7041e 100644 --- a/src/main/java/com/newscatcher/api/types/BreakingNewsResponseDto.java +++ b/src/main/java/com/newscatcher/api/types/BreakingNewsResponseDto.java @@ -248,7 +248,6 @@ public Builder from(BreakingNewsResponseDto other) { } /** - *

The status of the response.

*

The status of the response.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -260,7 +259,6 @@ public TotalHitsStage status(@NotNull String status) { } /** - *

The total number of articles matching the search criteria.

*

The total number of articles matching the search criteria.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -272,7 +270,6 @@ public PageStage totalHits(int totalHits) { } /** - *

The current page number of the results.

*

The current page number of the results.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -284,7 +281,6 @@ public TotalPagesStage page(int page) { } /** - *

The total number of pages available for the given search criteria.

*

The total number of pages available for the given search criteria.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -296,7 +292,6 @@ public PageSizeStage totalPages(int totalPages) { } /** - *

The number of articles per page.

*

The number of articles per page.

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/newscatcher/api/types/ClusterEntity.java b/src/main/java/com/newscatcher/api/types/ClusterEntity.java index a647b16..a80357f 100644 --- a/src/main/java/com/newscatcher/api/types/ClusterEntity.java +++ b/src/main/java/com/newscatcher/api/types/ClusterEntity.java @@ -146,7 +146,6 @@ public Builder from(ClusterEntity other) { } /** - *

The unique identifier for the cluster.

*

The unique identifier for the cluster.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -158,7 +157,6 @@ public ClusterSizeStage clusterId(@NotNull String clusterId) { } /** - *

The number of articles in the cluster.

*

The number of articles in the cluster.

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/newscatcher/api/types/ClusteredArticlesDto.java b/src/main/java/com/newscatcher/api/types/ClusteredArticlesDto.java index a3c40d2..2c77eaa 100644 --- a/src/main/java/com/newscatcher/api/types/ClusteredArticlesDto.java +++ b/src/main/java/com/newscatcher/api/types/ClusteredArticlesDto.java @@ -126,7 +126,6 @@ public Builder from(ClusteredArticlesDto other) { } /** - *

The number of clusters in the search results.

*

The number of clusters in the search results.

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/newscatcher/api/types/ClusteredSearchResponseDto.java b/src/main/java/com/newscatcher/api/types/ClusteredSearchResponseDto.java index 3bae523..8ae6e11 100644 --- a/src/main/java/com/newscatcher/api/types/ClusteredSearchResponseDto.java +++ b/src/main/java/com/newscatcher/api/types/ClusteredSearchResponseDto.java @@ -283,7 +283,6 @@ public Builder from(ClusteredSearchResponseDto other) { } /** - *

The status of the response.

*

The status of the response.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -295,7 +294,6 @@ public TotalHitsStage status(@NotNull String status) { } /** - *

The total number of articles matching the search criteria.

*

The total number of articles matching the search criteria.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -307,7 +305,6 @@ public PageStage totalHits(int totalHits) { } /** - *

The current page number of the results.

*

The current page number of the results.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -319,7 +316,6 @@ public TotalPagesStage page(int page) { } /** - *

The total number of pages available for the given search criteria.

*

The total number of pages available for the given search criteria.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -331,7 +327,6 @@ public PageSizeStage totalPages(int totalPages) { } /** - *

The number of articles per page.

*

The number of articles per page.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -343,7 +338,6 @@ public ClustersCountStage pageSize(int pageSize) { } /** - *

The number of clusters in the search results.

*

The number of clusters in the search results.

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/newscatcher/api/types/Error.java b/src/main/java/com/newscatcher/api/types/Error.java index 0681e39..86439d5 100644 --- a/src/main/java/com/newscatcher/api/types/Error.java +++ b/src/main/java/com/newscatcher/api/types/Error.java @@ -140,7 +140,6 @@ public Builder from(Error other) { } /** - *

A detailed description of the error.

*

A detailed description of the error.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -152,7 +151,6 @@ public StatusCodeStage message(@NotNull String message) { } /** - *

The HTTP status code of the error.

*

The HTTP status code of the error.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -164,7 +162,6 @@ public StatusStage statusCode(int statusCode) { } /** - *

A short description of the status code.

*

A short description of the status code.

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/newscatcher/api/types/FailedAggregationCountResponseDto.java b/src/main/java/com/newscatcher/api/types/FailedAggregationCountResponseDto.java index f9aa009..956735d 100644 --- a/src/main/java/com/newscatcher/api/types/FailedAggregationCountResponseDto.java +++ b/src/main/java/com/newscatcher/api/types/FailedAggregationCountResponseDto.java @@ -217,7 +217,6 @@ public Builder from(FailedAggregationCountResponseDto other) { } /** - *

The status of the response.

*

The status of the response.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -229,7 +228,6 @@ public TotalHitsStage status(@NotNull String status) { } /** - *

The total number of articles matching the search criteria.

*

The total number of articles matching the search criteria.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -241,7 +239,6 @@ public PageStage totalHits(int totalHits) { } /** - *

The current page number of the results.

*

The current page number of the results.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -253,7 +250,6 @@ public TotalPagesStage page(int page) { } /** - *

The total number of pages available for the given search criteria.

*

The total number of pages available for the given search criteria.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -265,7 +261,6 @@ public PageSizeStage totalPages(int totalPages) { } /** - *

The number of articles per page.

*

The number of articles per page.

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/newscatcher/api/types/FailedAuthorsResponseDto.java b/src/main/java/com/newscatcher/api/types/FailedAuthorsResponseDto.java index 29ebf63..070310a 100644 --- a/src/main/java/com/newscatcher/api/types/FailedAuthorsResponseDto.java +++ b/src/main/java/com/newscatcher/api/types/FailedAuthorsResponseDto.java @@ -242,7 +242,6 @@ public Builder from(FailedAuthorsResponseDto other) { } /** - *

The status of the response.

*

The status of the response.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -254,7 +253,6 @@ public TotalHitsStage status(@NotNull String status) { } /** - *

The total number of articles matching the search criteria.

*

The total number of articles matching the search criteria.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -266,7 +264,6 @@ public PageStage totalHits(int totalHits) { } /** - *

The current page number of the results.

*

The current page number of the results.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -278,7 +275,6 @@ public TotalPagesStage page(int page) { } /** - *

The total number of pages available for the given search criteria.

*

The total number of pages available for the given search criteria.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -290,7 +286,6 @@ public PageSizeStage totalPages(int totalPages) { } /** - *

The number of articles per page.

*

The number of articles per page.

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/newscatcher/api/types/SearchResponseDto.java b/src/main/java/com/newscatcher/api/types/SearchResponseDto.java index c9450e2..1cd8280 100644 --- a/src/main/java/com/newscatcher/api/types/SearchResponseDto.java +++ b/src/main/java/com/newscatcher/api/types/SearchResponseDto.java @@ -236,7 +236,6 @@ public Builder from(SearchResponseDto other) { } /** - *

The status of the response.

*

The status of the response.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -248,7 +247,6 @@ public TotalHitsStage status(@NotNull String status) { } /** - *

The total number of articles matching the search criteria.

*

The total number of articles matching the search criteria.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -260,7 +258,6 @@ public PageStage totalHits(int totalHits) { } /** - *

The current page number of the results.

*

The current page number of the results.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -272,7 +269,6 @@ public TotalPagesStage page(int page) { } /** - *

The total number of pages available for the given search criteria.

*

The total number of pages available for the given search criteria.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -284,7 +280,6 @@ public PageSizeStage totalPages(int totalPages) { } /** - *

The number of articles per page.

*

The number of articles per page.

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/newscatcher/api/types/SourceInfo.java b/src/main/java/com/newscatcher/api/types/SourceInfo.java index 0e8b869..ebc33f8 100644 --- a/src/main/java/com/newscatcher/api/types/SourceInfo.java +++ b/src/main/java/com/newscatcher/api/types/SourceInfo.java @@ -165,7 +165,6 @@ public Builder from(SourceInfo other) { } /** - *

The domain URL of the news source.

*

The domain URL of the news source.

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/newscatcher/api/types/SourcesResponseDto.java b/src/main/java/com/newscatcher/api/types/SourcesResponseDto.java index c079f71..3e336f2 100644 --- a/src/main/java/com/newscatcher/api/types/SourcesResponseDto.java +++ b/src/main/java/com/newscatcher/api/types/SourcesResponseDto.java @@ -152,7 +152,6 @@ public Builder from(SourcesResponseDto other) { } /** - *

A message indicating the result of the request.

*

A message indicating the result of the request.

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/newscatcher/api/types/SubscriptionResponseDto.java b/src/main/java/com/newscatcher/api/types/SubscriptionResponseDto.java index e1c1e02..a72502a 100644 --- a/src/main/java/com/newscatcher/api/types/SubscriptionResponseDto.java +++ b/src/main/java/com/newscatcher/api/types/SubscriptionResponseDto.java @@ -223,7 +223,6 @@ public Builder from(SubscriptionResponseDto other) { } /** - *

Indicates whether the subscription is currently active.

*

Indicates whether the subscription is currently active.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -235,7 +234,6 @@ public ConcurrentCallsStage active(boolean active) { } /** - *

The number of API calls allowed per second allowed in the current plan.

*

The number of API calls allowed per second allowed in the current plan.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -247,7 +245,6 @@ public PlanStage concurrentCalls(int concurrentCalls) { } /** - *

The name of the subscription plan.

*

The name of the subscription plan.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -259,7 +256,6 @@ public PlanCallsStage plan(@NotNull String plan) { } /** - *

The total number of API calls assigned to the current subscription.

*

The total number of API calls assigned to the current subscription.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -271,7 +267,6 @@ public RemainingCallsStage planCalls(int planCalls) { } /** - *

The number of API calls remaining for the current subscription period.

*

The number of API calls remaining for the current subscription period.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -283,7 +278,6 @@ public HistoricalDaysStage remainingCalls(int remainingCalls) { } /** - *

The number of historical days accessible under the current subscription plan.

*

The number of historical days accessible under the current subscription plan.

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/newscatcher/api/types/TimeFrameCount.java b/src/main/java/com/newscatcher/api/types/TimeFrameCount.java index 5646c72..e25644c 100644 --- a/src/main/java/com/newscatcher/api/types/TimeFrameCount.java +++ b/src/main/java/com/newscatcher/api/types/TimeFrameCount.java @@ -120,7 +120,6 @@ public Builder from(TimeFrameCount other) { } /** - *

The timestamp for the aggregation period in format "YYYY-MM-DD HH:mm:ss"

*

The timestamp for the aggregation period in format "YYYY-MM-DD HH:mm:ss"

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -132,7 +131,6 @@ public ArticleCountStage timeFrame(@NotNull OffsetDateTime timeFrame) { } /** - *

The number of articles published during this time frame

*

The number of articles published during this time frame

* @return Reference to {@code this} so that method calls can be chained together. */