Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
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
63 changes: 31 additions & 32 deletions src/main/java/dev/openfeature/sdk/HookContext.java
Original file line number Diff line number Diff line change
@@ -1,32 +1,13 @@
package dev.openfeature.sdk;

import lombok.Builder;
import lombok.NonNull;
import lombok.Value;
import lombok.With;
import dev.openfeature.sdk.HookContextWithoutData.HookContextWithoutDataBuilder;

/**
* A data class to hold immutable context that {@link Hook} instances use.
*
* @param <T> the type for the flag being evaluated
* A interface to hold immutable context that {@link Hook} instances use.
*/
@Value
@Builder
@With
public class HookContext<T> {
@NonNull String flagKey;

@NonNull FlagValueType type;

@NonNull T defaultValue;

@NonNull EvaluationContext ctx;

ClientMetadata clientMetadata;
Metadata providerMetadata;

public interface HookContext<T> {
/**
* Builds a {@link HookContext} instances from request data.
* Builds a {@link HookContextWithoutData} instances from request data.
*
* @param key feature flag key
* @param type flag value type
Expand All @@ -36,21 +17,39 @@ public class HookContext<T> {
* @param defaultValue Fallback value
* @param <T> type that the flag is evaluating against
* @return resulting context for hook
* @deprecated this should not be instantiated outside the SDK anymore
*/
public static <T> HookContext<T> from(
@Deprecated
static <T> HookContext<T> from(
String key,
FlagValueType type,
ClientMetadata clientMetadata,
Metadata providerMetadata,
EvaluationContext ctx,
T defaultValue) {
return HookContext.<T>builder()
.flagKey(key)
.type(type)
.clientMetadata(clientMetadata)
.providerMetadata(providerMetadata)
.ctx(ctx)
.defaultValue(defaultValue)
.build();
return new HookContextWithoutData<>(key, type, defaultValue, ctx, clientMetadata, providerMetadata);
}

/**
* Returns a builder for our default HookContext object.
*/
static <T> HookContextWithoutDataBuilder<T> builder() {
return HookContextWithoutData.<T>builder();
}

String getFlagKey();

FlagValueType getType();

T getDefaultValue();

EvaluationContext getCtx();

ClientMetadata getClientMetadata();

Metadata getProviderMetadata();

default HookData getHookData() {
return null;
}
}
50 changes: 50 additions & 0 deletions src/main/java/dev/openfeature/sdk/HookContextWithData.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package dev.openfeature.sdk;

class HookContextWithData<T> implements HookContext<T> {
private final HookContext<T> context;
private final HookData data;

private HookContextWithData(HookContext<T> context, HookData data) {
this.context = context;
this.data = data;
}

public static <T> HookContextWithData<T> of(HookContext<T> context, HookData data) {
return new HookContextWithData<>(context, data);
}

@Override
public String getFlagKey() {
return context.getFlagKey();
}

@Override
public FlagValueType getType() {
return context.getType();
}

@Override
public T getDefaultValue() {
return context.getDefaultValue();
}

@Override
public EvaluationContext getCtx() {
return context.getCtx();
}

@Override
public ClientMetadata getClientMetadata() {
return context.getClientMetadata();
}

@Override
public Metadata getProviderMetadata() {
return context.getProviderMetadata();
}

@Override
public HookData getHookData() {
return data;
}
}
48 changes: 48 additions & 0 deletions src/main/java/dev/openfeature/sdk/HookContextWithoutData.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package dev.openfeature.sdk;

import lombok.AccessLevel;
import lombok.Builder;
import lombok.Data;
import lombok.NonNull;
import lombok.Setter;
import lombok.With;

/**
* A data class to hold immutable context that {@link Hook} instances use.
*
* @param <T> the type for the flag being evaluated
*/
@Data
@Builder
@With
@Setter(AccessLevel.PRIVATE)
class HookContextWithoutData<T> implements HookContext<T> {
@NonNull String flagKey;

@NonNull FlagValueType type;

@NonNull T defaultValue;

@Setter(AccessLevel.PACKAGE)
@NonNull EvaluationContext ctx;

ClientMetadata clientMetadata;
Metadata providerMetadata;

/**
* Builds a {@link HookContextWithoutData} instances from request data.
*
* @param key feature flag key
* @param type flag value type
* @param clientMetadata info on which client is calling
* @param providerMetadata info on the provider
* @param defaultValue Fallback value
* @param <T> type that the flag is evaluating against
* @return resulting context for hook
*/
static <T> HookContextWithoutData<T> from(
String key, FlagValueType type, ClientMetadata clientMetadata, Metadata providerMetadata, T defaultValue) {
return new HookContextWithoutData<>(
key, type, defaultValue, ImmutableContext.EMPTY, clientMetadata, providerMetadata);
}
}
81 changes: 81 additions & 0 deletions src/main/java/dev/openfeature/sdk/HookData.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package dev.openfeature.sdk;

import java.util.HashMap;
import java.util.Map;

/**
* Hook data provides a way for hooks to maintain state across their execution stages.
* Each hook instance gets its own isolated data store that persists only for the duration
* of a single flag evaluation.
*/
public interface HookData {

/**
* Sets a value for the given key.
*
* @param key the key to store the value under
* @param value the value to store
*/
void set(String key, Object value);

/**
* Gets the value for the given key.
*
* @param key the key to retrieve the value for
* @return the value, or null if not found
*/
Object get(String key);

/**
* Gets the value for the given key, cast to the specified type.
*
* @param <T> the type to cast to
* @param key the key to retrieve the value for
* @param type the class to cast to
* @return the value cast to the specified type, or null if not found
* @throws ClassCastException if the value cannot be cast to the specified type
*/
<T> T get(String key, Class<T> type);

/**
* Default implementation uses a HashMap.
*/
static HookData create() {
return new DefaultHookData();
}

/**
* Default implementation of HookData.
*/
class DefaultHookData implements HookData {
private Map<String, Object> data;

@Override
public void set(String key, Object value) {
if (data == null) {
data = new HashMap<>();
}
data.put(key, value);
}

@Override
public Object get(String key) {
if (data == null) {
return null;
}
return data.get(key);
}

@Override
public <T> T get(String key, Class<T> type) {
Object value = get(key);
if (value == null) {
return null;
}
if (!type.isInstance(value)) {
throw new ClassCastException("Value for key '" + key + "' is not of type " + type.getName());
}
return type.cast(value);
}
}
}
Loading
Loading