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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .fern/metadata.json
Original file line number Diff line number Diff line change
@@ -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"
}
16 changes: 16 additions & 0 deletions .fern/replay.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion .fernignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,7 @@ LICENSE
changelog.md

# CI
.github/workflows/sdk-release.yml
.github/workflows/sdk-release.yml
.fern/replay.lock
.fern/replay.yml
.gitattributes
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.fern/replay.lock linguist-generated=true
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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'
}
```

Expand All @@ -55,7 +55,7 @@ Add the dependency in your `pom.xml` file:
<dependency>
<groupId>com.newscatcherapi</groupId>
<artifactId>newscatcher-sdk</artifactId>
<version>3.0.0</version>
<version>3.1.0</version>
</dependency>
```

Expand Down Expand Up @@ -364,3 +364,4 @@ For release notes and changelog, visit our [GitHub releases page](https://github




46 changes: 30 additions & 16 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -47,7 +57,7 @@ java {

group = 'com.newscatcherapi'

version = '3.0.0'
version = '3.1.0'

jar {
dependsOn(":generatePomFileForMavenPublication")
Expand Down Expand Up @@ -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'
Expand All @@ -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 {
Expand All @@ -127,5 +143,3 @@ signing {
useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword)
sign publishing.publications.maven
}

sonatypeCentralUpload.dependsOn build
14 changes: 14 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -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(...)`.
Expand Down
12 changes: 11 additions & 1 deletion src/main/java/com/newscatcher/api/AsyncNewscatcherApiClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<AsyncSearchClient> searchClient;
Expand Down Expand Up @@ -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();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ public class AsyncNewscatcherApiClientBuilder {

private Optional<Integer> maxRetries = Optional.empty();

private Optional<Long> initialRetryDelayMillis = Optional.empty();

private Optional<Long> maxRetryDelayMillis = Optional.empty();

private Optional<Double> retryJitterFactor = Optional.empty();

private final Map<String, String> customHeaders = new HashMap<>();

private String apiKey = null;
Expand Down Expand Up @@ -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
*/
Expand Down Expand Up @@ -130,7 +160,9 @@ protected void setEnvironment(ClientOptions.Builder builder) {
* }</pre>
*/
protected void setAuthentication(ClientOptions.Builder builder) {
builder.addHeader("x-api-token", this.apiKey);
if (this.apiKey != null) {
builder.addHeader("x-api-token", this.apiKey);
}
}

/**
Expand All @@ -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());
}
}

/**
Expand Down
12 changes: 11 additions & 1 deletion src/main/java/com/newscatcher/api/NewscatcherApiClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -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> searchClient;
Expand Down Expand Up @@ -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();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ public class NewscatcherApiClientBuilder {

private Optional<Integer> maxRetries = Optional.empty();

private Optional<Long> initialRetryDelayMillis = Optional.empty();

private Optional<Long> maxRetryDelayMillis = Optional.empty();

private Optional<Double> retryJitterFactor = Optional.empty();

private final Map<String, String> customHeaders = new HashMap<>();

private String apiKey = null;
Expand Down Expand Up @@ -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
*/
Expand Down Expand Up @@ -130,7 +160,9 @@ protected void setEnvironment(ClientOptions.Builder builder) {
* }</pre>
*/
protected void setAuthentication(ClientOptions.Builder builder) {
builder.addHeader("x-api-token", this.apiKey);
if (this.apiKey != null) {
builder.addHeader("x-api-token", this.apiKey);
}
}

/**
Expand All @@ -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());
}
}

/**
Expand Down
Loading
Loading