Skip to content

Prepare Java server SDK 6.0.0 with stable common v4 - #158

Open
typotter wants to merge 1 commit into
mainfrom
typo/release-v6
Open

typotter wants to merge 1 commit into
mainfrom
typo/release-v6

Conversation

@typotter

@typotter typotter commented Oct 5, 2026 •

Copy link
Copy Markdown
Collaborator

Eppo Internal

Related: #156 and Eppo-exp/sdk-common-jdk#273

Motivation and Context

Prepare Java server SDK 6.0.0 using published common/framework artifacts, including the repair for successful JSON-string assignments that previously threw ClassCastException.

Description

  • Set the server version to 6.0.0 and replace snapshot dependencies with common 4.0.1 and framework 0.1.1 test fixtures.
  • Document the common API migration: DTO imports move from cloud.eppo.ufc.dto to cloud.eppo.api.dto, and EppoHttpClient has been removed.
  • Document inherited assignment-details APIs and malformed-JSON behaviour. Strict mode throws for malformed JSON; graceful mode returns the default. This differs from common 3.13.2 returning a non-null default in strict mode and applies to both parsed and string JSON assignments.

The common API dependency is exposed transitively, so these consumer-facing changes require a major server version. Common 4.0.1 references framework 0.1.1; both are published on Maven Central, including the framework tests classifier.

Publish v6 through the GitHub release workflow after merge: the main-branch snapshot publisher rejects stable project versions. Resume snapshots with a follow-up version bump.

How has this been documented?

README upgrade notes cover dependency versions, breaking API changes, details methods and strict-mode malformed-JSON behaviour.

How has this been tested?

  • October 22: Java 8/11/17/21 each passed 44 server tests plus 24 external framework JSON regression tests, with zero failures/errors/skips; formatting and release-version checks passed.
  • All four runtimes passed the migration probe covering metadata, defaults/type mismatch, assignment logging/deduplication, bandit details, no-actions results and synchronous configuration callbacks.
  • JSON regression coverage includes all four string/value/details overloads, exact raw text preservation, parsed JSON compatibility, defaults/nulls, type mismatch, metadata, logging/deduplication and strict/graceful malformed-JSON behaviour.
  • Maven Local and snapshots excluded from validation. Published framework/common Java sources match the previously matrix-tested fix checkout (74/9 files respectively). Fresh tests use published artifacts.
  • Full local scenario API + Java server v6 Relay + shared runner: 256 passed, zero errors/failures, two expected dynamic-typing skips. All eight formerly failing JSON cases pass. Relay runtime common/framework JAR hashes match Maven Central downloads. This is local end-to-end validation, not hosted-environment validation.
  • git diff --check.
  • Fresh remote CI passed all four Java jobs on the updated PR revision.
  • After the v6.0.0 release, confirm the server artifact resolves from Maven Central.

Use released common 4.0.1 and framework 0.1.1 test fixtures. Document the v6 API migration and strict-mode malformed-JSON behaviour.

Validate Java 8, 11, 17 and 21 with 68 tests per runtime and migration probes; full relay passes 256 cases with two expected skips against published artifacts.
Comment thread README.md
Comment on lines +18 to +35
## Upgrading to 6.0.0

Version 6.0.0 adopts common SDK 4.0.1. The common SDK is exposed as an API
dependency, so applications that use its types must account for these breaking
changes:

- Update imports from `cloud.eppo.ufc.dto` to `cloud.eppo.api.dto`.
- `cloud.eppo.EppoHttpClient` has been removed. Integrations that directly use it
must migrate to the v4 HTTP-client API; this is not a drop-in class rename.

The client also exposes typed `get*AssignmentDetails()` methods and
`getBanditActionDetails()` for inspecting evaluation results.

Starting in common 4.0.0, strict mode (graceful mode disabled) throws when a JSON
flag contains malformed JSON. Common 3.13.2 returned the supplied non-null default
for this case. Graceful mode continues to return the default. This applies to both
parsed JSON and JSON-string assignments. Common 4.0.1 fixes successful JSON-string
assignments while retaining this strict-mode behaviour.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Comment from Claude working with Aaron Silverman:

[P1] Please replace this section. Its import rule can break an import, and it misses several breaking changes.

  • Applied as a package-prefix replacement, the import rule also rewrites cloud.eppo.ufc.dto.adapters.EppoModule, which did not move.
  • Missing: the DTOs are now interfaces, cloud.eppo.model.ShardRange moved, five public methods on Configuration and Attributes were removed, and four adapters are no longer public.
  • onConfigurationChange no longer fires when a poll gets a 304. In 5.3.4 it fired after every successful poll.
  • The text uses common versions. Server users know 5.x and 6.0.0.
What I checked — against the published 3.13.2, 4.0.1 and framework 0.1.1 jars

I compared javap -public output and the class lists of common 3.13.2 with common 4.0.1 plus framework 0.1.1.

  • "Update imports from cloud.eppo.ufc.dto to cloud.eppo.api.dto." Common 4.0.1 still ships cloud.eppo.ufc.dto.adapters. EppoModule and EppoValueDeserializer stay public there; the other four adapters are now package-private.
  • Every moved DTO except the three enums is now an interface with a Default implementation, for example FlagConfig.Default. BanditAttributeCoefficients has no Default.
  • cloud.eppo.model.ShardRange is now cloud.eppo.api.dto.ShardRange.
  • Removed: Configuration.builder(byte[]), builder(byte[], boolean), serializeFlagConfigToBytes(), serializeBanditParamsToBytes() and Attributes.serializeNonNullAttributesToJSONString(). Configuration.builder(FlagConfigResponse) infers the obfuscation flag from the payload format; new Configuration.Builder(FlagConfigResponse, boolean) takes it explicitly. Both replacements in the block below compiled and parsed flags-v1.json (22 of 22 flags).
  • "Integrations that directly use it must migrate to the v4 HTTP-client API" names no type. The new interface is cloud.eppo.http.EppoConfigurationClient, but EppoClient.Builder has no way to supply one.
  • The v5.3.4 testConfigurationChangeListener asserted a third callback after an unchanged reload, with the comment "Java doesn't check eTag (yet)". Upgrade sdk-common-jvm to v4.0.0 (major version bump to 6.0.0) #156 removed that assertion. 5.3.4 never sent If-None-Match, so every successful poll saved and notified. In 6.0.0, a poll that gets a 304 returns before the store saves, so no callback fires.
  • "Common 4.0.1 fixes successful JSON-string assignments while retaining this strict-mode behaviour." No server release shipped common 4.0.0, so 5.x readers never had that bug.

The strict-mode paragraph is correct, including the non-null qualifier for getJSONAssignment. The block keeps it.

Suggested replacement for lines 18–35
## Upgrading from 5.x to 6.0.0

6.0.0 moves the `cloud.eppo:sdk-common-jvm` API dependency from 3.13.2 to 4.0.1.
Common 4.0.1 adds `cloud.eppo:eppo-sdk-framework` 0.1.1 as an API dependency.
Recompile code that you built against 5.x.

If your code uses common SDK types directly:

- DTOs moved from `cloud.eppo.ufc.dto` to `cloud.eppo.api.dto`. Except for the
  enums, they are now interfaces. Replace constructors such as
  `new FlagConfig(...)` with `new FlagConfig.Default(...)`.
- `cloud.eppo.ufc.dto.adapters.EppoModule` and `EppoValueDeserializer` did not
  move. The other adapters are no longer public.
- `cloud.eppo.model.ShardRange` is now `cloud.eppo.api.dto.ShardRange`.
- `Configuration.builder(byte[])` and `builder(byte[], boolean)` were removed. Use
  `Configuration.builder(new JacksonConfigurationParser().parseFlagConfig(bytes))`,
  or `new Configuration.Builder(flagConfigResponse, isConfigObfuscated)` to set the
  obfuscation flag yourself.
- `Configuration.serializeFlagConfigToBytes()`,
  `Configuration.serializeBanditParamsToBytes()` and
  `Attributes.serializeNonNullAttributesToJSONString()` were removed.
- `cloud.eppo.EppoHttpClient` was removed. `EppoClient` always uses its bundled
  OkHttp client. To point tests at a stub server, use `apiBaseUrl(...)`.

Behaviour changes:

- With `isGracefulMode(false)`, `getJSONAssignment()`, `getJSONStringAssignment()`
  and their `*Details()` variants throw when the assigned variation is not valid
  JSON. 5.x returned the default (for `getJSONAssignment()`, only a non-null
  default). Graceful mode still returns the default.
- Once the client saves a configuration that came with an ETag, polls send
  `If-None-Match`. When the server answers 304 Not Modified, the client keeps its
  configuration and `onConfigurationChange` does not fire. In 5.x it fired after
  every successful poll.

New: `get*AssignmentDetails()` and `getBanditActionDetails()` return evaluation
details with the value.

If 6.0.0 pins common 4.0.2 (see the comment on build.gradle line 33), change 4.0.1 to 4.0.2 and 0.1.1 to 0.1.2 in the first paragraph.

Comment thread build.gradle

// Logback classic 1.3.x is compatible with java 8 - only needed for tests
testImplementation 'cloud.eppo:eppo-sdk-framework:0.1.0-SNAPSHOT:tests'
testImplementation 'cloud.eppo:eppo-sdk-framework:0.1.1:tests'

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Comment from Claude working with Aaron Silverman:

[P2] Please drop the version from the tests-jar dependency, so the tests run against the framework version that ships.

Gradle resolves one eppo-sdk-framework version on the test classpath. If this pin is higher than the version common requires, the tests run on the higher version. The published POM still ships the lower one. Dependabot can bump this pin on its own.

Evidence — a local mutation passed the suite while the POM shipped the buggy framework

I pinned common 4.0.0, which requires framework 0.1.0 with the JSON-string ClassCastException, and kept this line at 0.1.1.

Classpath Framework
runtimeClasspath (what the POM ships) 0.1.0
testRuntimeClasspath 0.1.0 → 0.1.1

The suite passed 44/44.

Suggested change — replaces lines 41–42
  // No version: tests use the framework version sdk-common-jvm resolves, which is what ships.
  testImplementation 'cloud.eppo:eppo-sdk-framework::tests'
  // Logback classic 1.3.x is compatible with java 8 - only needed for tests

With common 4.0.1, both classpaths resolve framework 0.1.1 and the suite passes 44/44. With common 4.0.0, both resolve 0.1.0. Spotless passed on a build.gradle that includes this change.

This makes the tests match what ships. It does not add coverage that an older test harness lacks.

Comment thread build.gradle
api 'cloud.eppo:sdk-common-jvm:4.0.0-SNAPSHOT'
api 'cloud.eppo:sdk-common-jvm:4.0.1'

implementation 'com.github.zafarkhaja:java-semver:0.10.2'

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Comment from Claude working with Aaron Silverman:

[P2] Please remove java-semver (this line) and ehcache (line 37). Nothing in src/ uses either one.

  • The framework already brings java-semver at runtime.
  • Removing ehcache takes nine jars off the runtime classpath that this SDK brings to consumers.
  • The removal can break a consumer that gets ehcache only through this SDK. A major release is the right time for that.
Evidence
  • No file under src/main or src/test/java references ehcache, zafarkhaja or semver.
  • With both lines removed, this PR's suite passes 44/44.
  • These jars leave the runtime classpath: ehcache, cache-api, jaxb-api, jaxb-runtime, txw2, istack-commons-runtime, stax-ex, FastInfoset and javax.activation-api.
  • The framework 0.1.1 POM declares java-semver 0.10.2 at runtime scope.
Lines to delete — line 35 and line 37

Line 35:

  implementation 'com.github.zafarkhaja:java-semver:0.10.2'

Line 37:

  implementation 'org.ehcache:ehcache:3.11.1'

Comment thread build.gradle

group = 'cloud.eppo'
version = '6.0.0-SNAPSHOT'
version = '6.0.0'

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Comment from Claude working with Aaron Silverman:

Reminder: release steps for 6.0.0. No code change is needed.

  • Merging turns main's snapshot publish red. Update version from 5.3.3-SNAPSHOT to 5.3.3 #142 did the same.
  • publish-sdk.yml does not check that the release tag matches build.gradle. Release v3.5.2 published 5.3.2. Publish the GitHub release with tag v6.0.0, targeting the merge commit.
  • In the 6.0.1-SNAPSHOT bump, also update README.md line 80. v5.3.4's README still says 4.0.1-SNAPSHOT.
  • A failed publish run may still have published. The v5.3.3 run concluded failure, yet Central's 5.3.3 POM is stamped 82 seconds after that run was created. Check Central before a re-run.

A release checklist in the PR body would capture these.

Evidence
  • checkVersion rejects -Psnapshot when the version does not end in SNAPSHOT. In the last 12 publish-snapshot.yml runs, every success ran on a -SNAPSHOT version.
  • Release v3.5.2 targeted main at 46266cd, where build.gradle says version = '5.3.2'. Its publish-sdk.yml run succeeded. Maven Central lists 5.3.2 and has no 3.5.2.
  • The publish-sdk.yml run for v5.3.3 has created_at 20:13:16 UTC, updated_at 20:48:57 UTC and conclusion failure. Central serves eppo-server-sdk-5.3.3.pom with Last-Modified: 20:14:38 GMT the same day. The timestamps do not show which process uploaded it.

Comment thread build.gradle

dependencies {
api 'cloud.eppo:sdk-common-jvm:4.0.0-SNAPSHOT'
api 'cloud.eppo:sdk-common-jvm:4.0.1'

@aarsilv aarsilv Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Comment from Claude working with Aaron Silverman:

[P1] Please hold 6.0.0 until common 4.0.2 ships Eppo-exp/sdk-common-jdk#274, then pin sdk-common-jvm:4.0.2 here.

Framework 0.1.1 sends If-None-Match with the last ETag it saved. When the bandit request returns an error status, or models that do not match the flags, it still saves the flags under the new ETag. Later polls get a 304 and return before the bandit fetch. So bandit models stay missing, or stale, until the flags change or the client restarts. In 5.3.4, a bandit failure failed the whole fetch, and the next poll retried.

Evidence — the same test on three builds

The test is in the last section. Flags send ETag: "v1" and answer 304 to If-None-Match: "v1". Bandits answer 500 once, then 200. The client initializes, then calls loadConfiguration() once.

Build Result
server v5.3.4 pass
this PR at 40b0ac5 fail: Expected exactly 2 requests matching … /bandits … but received 1
this PR with the framework from sdk-common-jdk#274 pass (45/45 with the rest of this PR's suite)

The live config endpoint sends ETags: even a 401 from fscdn.eppo.cloud carries etag: W/"2b-…". I did not confirm a 304 on a real 200, because that needs an SDK key.

The fix — sdk-common-jdk#274, EppoConfigurationRequestor

A bandit outage no longer stops flag updates. A failed bandit fetch applies the new flags with the models already loaded, or none on a cold start. While a referenced model version is missing, the configuration is saved without the ETag, so the next poll gets the flags again and retries the bandits. Once the models match, polls return to 304.

  // A failed bandit fetch returns null, so the save keeps the loaded models and flag updates still
  // apply.
  @Nullable private byte[] fetchBanditParameters() {
    EppoConfigurationRequest banditRequest = requestFactory.createBanditParamsRequest();
    try {
      return requireBanditBody(configurationClient.execute(banditRequest).get());
    } catch (InterruptedException e) {
      log.error("Bandit parameter fetch interrupted", e);
      Thread.currentThread().interrupt();
      throw new RuntimeException(e);
    } catch (ExecutionException | RuntimeException e) {
      log.warn("Failed to fetch bandit parameters; keeping the loaded models", e);
      return null;
    }
  }

  // Saves without the ETag while a referenced model version is missing. The next poll then gets the
  // flags again instead of a 304, and retries the bandits.
  private ConfigurationType buildConfigWithBanditCheck(
      FlagConfigResponse flagConfigResponse,
      @Nullable String flagsSnapshotId,
      ConfigurationType lastConfig,
      @Nullable byte[] banditBytes) {
    ConfigurationType config;
    try {
      config =
          configurationParser.buildConfig(
              flagConfigResponse, flagsSnapshotId, lastConfig, banditBytes);
    } catch (ConfigurationParseException e) {
      if (banditBytes == null) {
        throw e;
      }
      log.warn("Failed to parse bandit parameters; keeping the loaded models", e);
      banditBytes = null;
      config =
          configurationParser.buildConfig(flagConfigResponse, flagsSnapshotId, lastConfig, null);
    }
    if (flagsSnapshotId != null && needsFreshBandits(flagConfigResponse, config)) {
      log.warn(
          "Bandit models {} do not match the flags; saving without the ETag so the next poll"
              + " retries them",
          staleBandits(flagConfigResponse, config));
      return configurationParser.buildConfig(flagConfigResponse, null, lastConfig, banditBytes);
    }
    return config;
  }

#274 also bumps framework to 0.1.2 and common to 4.0.2. Its ./gradlew check passes 400 tests.

Trade-off: while a model is missing, each poll downloads the full flags and notifies subscribers, as 5.3.4 did on every successful poll.

Regression test for EppoClientTest — add with the re-pin

It uses only imports this file already has.

  @Test
  public void testBanditFetchRetriedWhenFlagsNotModified() {
    mockServer.resetAll();
    mockServer.stubFor(
        WireMock.get(WireMock.urlMatching(".*flag-config/v1/config.*"))
            .willReturn(
                WireMock.okJson(readConfig("src/test/resources/shared/ufc/bandit-flags-v1.json"))
                    .withHeader("ETag", "\"v1\"")));
    mockServer.stubFor(
        WireMock.get(WireMock.urlMatching(".*flag-config/v1/config.*"))
            .withHeader("If-None-Match", WireMock.equalTo("\"v1\""))
            .willReturn(WireMock.aResponse().withStatus(304)));
    mockServer.stubFor(
        WireMock.get(WireMock.urlMatching(".*flag-config/v1/bandits.*"))
            .inScenario("bandits")
            .whenScenarioStateIs(Scenario.STARTED)
            .willReturn(WireMock.serverError())
            .willSetStateTo("recovered"));
    mockServer.stubFor(
        WireMock.get(WireMock.urlMatching(".*flag-config/v1/bandits.*"))
            .inScenario("bandits")
            .whenScenarioStateIs("recovered")
            .willReturn(
                WireMock.okJson(
                    readConfig("src/test/resources/shared/ufc/bandit-models-v1.json"))));

    EppoClient eppoClient =
        EppoClient.builder(DUMMY_BANDIT_API_KEY)
            .apiBaseUrl(TEST_HOST)
            .pollingIntervalMs(0)
            .forceReinitialize(true)
            .buildAndInit();
    assertNull(eppoClient.getConfiguration().getBanditParameters("banner_bandit"));

    // Next poll: flags unchanged, bandit endpoint healthy again.
    eppoClient.loadConfiguration();

    mockServer.verify(
        2, WireMock.getRequestedFor(WireMock.urlMatching(".*flag-config/v1/bandits.*")));
    assertNotNull(eppoClient.getConfiguration().getBanditParameters("banner_bandit"));
  }

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Comment from Claude working with Aaron Silverman:

Update: sdk-common-jdk#274 changed approach after a second review. A failed bandit fetch no longer fails the poll: the flags keep updating, and the next poll retries the bandits. I edited the comment above to describe that. The ask is unchanged: hold 6.0.0 until common 4.0.2 ships #274, then pin sdk-common-jvm:4.0.2.

Comment thread build.gradle

dependencies {
api 'cloud.eppo:sdk-common-jvm:4.0.0-SNAPSHOT'
api 'cloud.eppo:sdk-common-jvm:4.0.1'

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Comment from Claude working with Aaron Silverman:

[P2] Please consider shipping Eppo-exp/sdk-common-jdk#275 with #274, so plain assignment calls skip the details work they discard.

In framework 0.1.1, each plain get*Assignment() returns get*AssignmentDetails(...).getVariation(). Each call builds the full details, then discards them. On a simple matched flag, a call takes about 2.7 times as long as in 5.3.4.

Measurements — 5.3.4, this PR, and this PR with #275

I compiled the same source against each version and ran a plain loop on Java 21, one thread, with the assignment cache off. Each figure averages four rounds of 4M calls after two warm-up rounds.

Path Server 5.3.4 This PR This PR + #275
Split matched (numeric_flag, integer-flag) ~135 ns, ~410 B per call ~370 ns, ~1,790 B ~204 ns, 800 B
Flags with targeting rules (new-user-onboarding, kill-switch) ~380 ns, ~1,500 B ~618 ns, ~3,190 B ~495 ns, ~2,040 B
*AssignmentDetails calls n/a 627–678 ns, 3,163–3,187 B 627–628 ns, 3,163–3,211 B

These are loop timings that include building the subject string. Compare them across columns rather than reading them as absolute latency. I measured #275 at its first commit; its second commit changes only tests, Javadoc and visibility.

A JFR allocation profile of this PR put the matched-rule set, the String.format description and the details builders at the top.

The fix — sdk-common-jdk#275

Plain calls skip the allocation details, the matched rule and the match description. They still build a small EvaluationDetails, and the fallback descriptions for a missing, disabled or mistyped flag remain. For callers, evaluation and assignment logging stay the same, and the *Details methods are unchanged. In graceful mode, a plain call now also returns the default when reading the configuration throws.

A subclass that overrides the four-argument evaluateAndMaybeLog or a get*AssignmentDetails method no longer intercepts plain calls. EppoClient and CommonEppoClient override neither.

  /**
   * Value-only counterpart of {@link #getTypedAssignmentWithDetails}: same evaluation and logging,
   * without the allocation lists, matched rule or match description.
   */
  private <T> T getTypedAssignment(
      String flagKey,
      String subjectKey,
      Attributes subjectAttributes,
      T defaultValue,
      VariationType expectedType) {
    try {
      EvaluationDetails details =
          evaluateAndMaybeLog(flagKey, subjectKey, subjectAttributes, expectedType, false);
      return details.evaluationSuccessful()
          ? details.getVariationValue().unwrap(expectedType, configurationParser::parseJsonValue)
          : defaultValue;
    } catch (Exception e) {
      return throwIfNotGraceful(e, defaultValue);
    }
  }

#275 has no version bump. If it merges before #274 is released, it ships in common 4.0.2. Its ./gradlew check passes 390 tests, and 394 when merged with #274.

Comment thread build.gradle

dependencies {
api 'cloud.eppo:sdk-common-jvm:4.0.0-SNAPSHOT'
api 'cloud.eppo:sdk-common-jvm:4.0.1'

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Comment from Claude working with Aaron Silverman:

[P2] Please consider three overrides in EppoClient, so code compiled against 5.3.4 that calls these EppoClient methods still links against 6.0.0.

With common 4.0.1, EppoClient extends BaseEppoClient<Configuration, JsonNode>. That erases the compiled signatures of both getJSONAssignment overloads (return type and default parameter) and the return type of getConfiguration(). A jar compiled against 5.3.4 that calls them on an EppoClient throws NoSuchMethodError on 6.0.0. That is an Error, so graceful mode does not catch it. Code that uses only these calls recompiles cleanly, so the failure shows up at runtime.

The likely case is a wrapper library compiled against 5.x. By default, Gradle resolves the highest requested version, so an app on 6.0.0 can run that wrapper without recompiling it. GitHub's public code search finds no published Java library built on eppo-server-sdk; I did not check private or prebuilt components.

Evidence — one class file compiled against 5.3.4, run on three classpaths
Call 5.3.4 This PR at 40b0ac5 This PR + the overrides
getBooleanAssignment works works works
getJSONAssignment works NoSuchMethodError works
getConfiguration works NoSuchMethodError works

javap lists 18 public method descriptors on 5.3.4's EppoClient and BaseEppoClient. This PR is missing 3 of them; with the overrides, none are missing.

Suggested fix — EppoClient.java, plus a regression test

Add import cloud.eppo.api.Attributes; and, after the constructor:

  // Concrete return types keep callers compiled against 5.x linking: generics erased these
  // descriptors in 6.0.0.
  @Override
  public Configuration getConfiguration() {
    return super.getConfiguration();
  }

  @Override
  public JsonNode getJSONAssignment(String flagKey, String subjectKey, JsonNode defaultValue) {
    return super.getJSONAssignment(flagKey, subjectKey, defaultValue);
  }

  @Override
  public JsonNode getJSONAssignment(
      String flagKey, String subjectKey, Attributes subjectAttributes, JsonNode defaultValue) {
    return super.getJSONAssignment(flagKey, subjectKey, subjectAttributes, defaultValue);
  }

In EppoClientTest, add import com.fasterxml.jackson.databind.JsonNode; and:

  @Test
  public void testKeepsVersion5MethodDescriptors() throws NoSuchMethodException {
    assertEquals(
        Configuration.class, EppoClient.class.getMethod("getConfiguration").getReturnType());
    assertEquals(
        JsonNode.class,
        EppoClient.class
            .getMethod("getJSONAssignment", String.class, String.class, JsonNode.class)
            .getReturnType());
    assertEquals(
        JsonNode.class,
        EppoClient.class
            .getMethod(
                "getJSONAssignment", String.class, String.class, Attributes.class, JsonNode.class)
            .getReturnType());
  }

With both, this PR's 44 tests and the new test pass. The new test fails without the overrides: expected: <cloud.eppo.api.Configuration> but was: <cloud.eppo.api.SerializableEppoConfiguration>. Spotless passes.

With these overrides, every public method descriptor of 5.3.4's EppoClient and BaseEppoClient resolves again through EppoClient. Calls compiled against BaseEppoClient directly, and code that uses moved DTO types, still need a recompile, which the README block in the comment on line 18 asks for.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants