Skip to main content
The nexus-kotlin-openfeature artifact wraps NexusClient behind the OpenFeature Java SDK standard. It is published separately so teams that do not use OpenFeature do not pull in the extra dependency. Latest release: v0.15.1 (OpenFeature Java SDK 1.22.0)

Installation

The provider declares a real dependency on nexus-kotlin-sdk, so the SDK resolves with it - you do not have to list it separately (though doing so is harmless).

Registration

The provider wraps an existing client and opens no second connection. The client must have completed its initial sync - which NexusClient.create does before returning.

Usage

Evaluation behaviour

Boolean flags report a missing flag

A flag that is not in the snapshot yields FLAG_NOT_FOUND with the caller’s default, rather than the default with a STATIC reason. NexusClient.getFlag alone could not express this - it collapses “absent” and “off” into one boolean, so every miss looked like a deliberate false. The provider uses findFlag, which returns null for a flag that does not exist.

Object configs are structured

A JSON object becomes an OpenFeature Structure and an array a List<Value>, recursively:
Returning the serialized JSON text instead - as earlier versions did - type-checked, but it pushed the parsing back onto the application and made asStructure() return null for every composite config, which is the one thing an object evaluation is for. Within a structure, an integer that fits stays an integer, so reading 3 back does not give 3.0. An integer too large for Value’s 32-bit slot is carried as its exact decimal text (read it with asString()): the type’s only other numeric slot is a Double, which cannot hold every larger integer exactly - it would answer 9007199254740992 for 9007199254740993 with a success reason. A genuinely fractional number is a Double. JSON null becomes an empty Value.

An Integer resolution requires an exact Int

A fractional value and one outside Int range are both reported as TYPE_MISMATCH, for the same reason: 3.14 is not 3, and Int.MAX_VALUE is not 9007199254740993 - either, returned with a success reason, is a wrong number the caller has no way to detect. You get the default you passed, plus the error code saying the config’s type is what to look at. A whole-numbered value such as 5.0 is an integer, because JSON has a single number type. Read a config too large for an Int with NexusClient.getLong, which carries it exactly.

Per-user targeting

(v0.12.0) When the EvaluationContext carries a targeting key, a boolean evaluation is resolved per user through the Nexus AB Testing add-on: the flag’s rollout percentage and cohort rules are applied to that identity. Without a targeting key the provider reads the synced snapshot, which is a pure in-memory lookup.
The targeting key is sent as the user id; the remaining context attributes become cohort attributes.

Attributes are strings

Cohort conditions compare attribute values as strings (eq, neq, in), so only string attributes are sent. A non-string one is left out of the evaluation and reported through the provider’s logger at debug level, naming the attribute.
A value is not stringified for you on purpose: a coerced attribute that matches no rule is harder to diagnose than an absent one, and the format the rule expects is yours to choose.

Only boolean evaluation is targeted

String, double, integer and object values come from configs, and a config has no per-user dimension in Nexus.

Requests and caching

A targeted evaluation is a network call, where the snapshot path is a map read. Two things keep that affordable, and neither needs configuring:
  • One request serves every flag the snapshot knows for that user, so reading five flags while handling a request costs one round trip. A snapshot larger than the service’s 200-key limit is split into successive calls.
  • Results are memoised per user and flag for 30 seconds, and concurrent evaluations for the same user coalesce into a single request. A memo hit reports the CACHED reason.

The targeted path blocks the calling thread

getBooleanEvaluation is synchronous in the OpenFeature API while NexusClient.evaluateAB is a suspend function, so the bridge runs it in runBlocking(Dispatchers.IO). The IO dispatcher is deliberate: the call must not occupy a thread from the caller’s own dispatcher, which on a saturated Dispatchers.Default could deadlock. Calling this from inside a coroutine still blocks that coroutine’s thread - the OpenFeature Java API offers no suspending entry point - so call NexusClient.evaluateAB directly if you need a non-blocking targeted evaluation.

Options

Each option is validated in the constructor, naming the option and the value, so an unusable window surfaces where it is set instead of turning into unexplained load later. A targetingTtl of zero is rejected rather than read as “no caching” - it would make every evaluation a network call.

Reason codes

A failed targeted request never surfaces as an error. The OpenFeature specification requires that evaluation not throw and that an abnormal execution return the caller’s default, so reporting an error would hand back your default for a flag that is genuinely on - instead the snapshot value is served with STALE: real, but not freshly targeted for this user. No error message is populated, because a STALE fallback is a normal execution. A project without the AB Testing add-on gets STATIC from the snapshot, which is authoritative for it, and the provider waits five minutes before asking again. That pause is a throttle, not a latch: it expires on its own and any success clears it, so buying the add-on takes effect without a restart. SPLIT is never reported: the endpoint returns booleans only, so a rollout bucket and a cohort match are indistinguishable here, and TARGETING_MATCH - “the result of a dynamic evaluation, such as a rule or specific user-targeting” - covers both. variant carries the string form of the resolved value for boolean, string, double and integer resolutions, which is the convention across the Westyx Nexus SDK suite. An object resolution has none.

Notes

  • String type-checking uses JsonPrimitive.isString, so a numeric config stored as a JSON number returns TYPE_MISMATCH for a string evaluation rather than a coerced string.