Provider hooks

This commit is contained in:
Justin Abrahms 2022-08-05 14:19:18 -07:00
parent f37bae77d7
commit def7f57007
No known key found for this signature in database
GPG Key ID: 599E2E12011DC474
4 changed files with 49 additions and 13 deletions

View File

@ -1,10 +1,19 @@
package dev.openfeature.javasdk;
import com.google.common.collect.Lists;
import java.util.List;
/**
* The interface implemented by upstream flag providers to resolve flags for their service.
*/
public interface FeatureProvider {
Metadata getMetadata();
default List<Hook> getProviderHooks() {
return Lists.newArrayList();
}
ProviderEvaluation<Boolean> getBooleanEvaluation(String key, Boolean defaultValue, EvaluationContext ctx, FlagEvaluationOptions options);
ProviderEvaluation<String> getStringEvaluation(String key, String defaultValue, EvaluationContext ctx, FlagEvaluationOptions options);
ProviderEvaluation<Integer> getIntegerEvaluation(String key, Integer defaultValue, EvaluationContext ctx, FlagEvaluationOptions options);

View File

@ -43,7 +43,7 @@ public class OpenFeatureClient implements Client {
// TODO: Context transformation?
HookContext<T> hookCtx = HookContext.from(key, type, this.getMetadata(), openfeatureApi.getProvider().getMetadata(), ctx, defaultValue);
List<Hook> mergedHooks = ObjectUtils.merge(flagOptions.getHooks(), clientHooks, openfeatureApi.getApiHooks());
List<Hook> mergedHooks = ObjectUtils.merge(provider.getProviderHooks(), flagOptions.getHooks(), clientHooks, openfeatureApi.getApiHooks());
FlagEvaluationDetails<T> details = null;
try {

View File

@ -3,6 +3,7 @@ package dev.openfeature.javasdk;
import java.util.*;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import dev.openfeature.javasdk.fixtures.HookFixtures;
import lombok.SneakyThrows;
import org.junit.jupiter.api.*;
@ -166,15 +167,41 @@ public class HookSpecTest implements HookFixtures {
}
@Specification(number="4.4.1", text="The API, Client and invocation MUST have a method for registering hooks which accepts flag evaluation options")
@Specification(number="4.4.1", text="The API, Client, Provider, and invocation MUST have a method for registering hooks.")
@Specification(number="4.3.5", text="The after stage MUST run after flag resolution occurs. It accepts a hook context (required), flag evaluation details (required) and hook hints (optional). It has no return value.")
@Specification(number="4.4.2", text="Hooks MUST be evaluated in the following order: - before: API, Client, Invocation - after: Invocation, Client, API - error (if applicable): Invocation, Client, API - finally: Invocation, Client, API")
@Specification(number="4.4.2", text="Hooks MUST be evaluated in the following order: - before: API, Client, Invocation, Provider - after: Provider, Invocation, Client, API - error (if applicable): Provider, Invocation, Client, API - finally: Provider, Invocation, Client, API")
@Specification(number="4.3.6", text="The error hook MUST run when errors are encountered in the before stage, the after stage or during flag resolution. It accepts hook context (required), exception representing what went wrong (required), and hook hints (optional). It has no return value.")
@Specification(number="4.3.7", text="The finally hook MUST run after the before, after, and error stages. It accepts a hook context (required) and hook hints (optional). There is no return value.")
@Test void hook_eval_order() {
List<String> evalOrder = new ArrayList<>();
OpenFeatureAPI api = OpenFeatureAPI.getInstance();
api.setProvider(new NoOpProvider());
api.setProvider(new NoOpProvider() {
public List<Hook> getProviderHooks() {
return Lists.newArrayList(new BooleanHook() {
@Override
public Optional<EvaluationContext> before(HookContext<Boolean> ctx, Map<String, Object> hints) {
evalOrder.add("provider before");
return null;
}
@Override
public void after(HookContext<Boolean> ctx, FlagEvaluationDetails<Boolean> details, Map<String, Object> hints) {
evalOrder.add("provider after");
}
@Override
public void error(HookContext<Boolean> ctx, Exception error, Map<String, Object> hints) {
evalOrder.add("provider error");
}
@Override
public void finallyAfter(HookContext<Boolean> ctx, Map<String, Object> hints) {
evalOrder.add("provider finally");
}
});
}
});
api.addHooks(new BooleanHook() {
@Override
public Optional<EvaluationContext> before(HookContext<Boolean> ctx, Map<String, Object> hints) {
@ -250,10 +277,10 @@ public class HookSpecTest implements HookFixtures {
.build());
List<String> expectedOrder = Arrays.asList(
"api before", "client before", "invocation before",
"invocation after", "client after", "api after",
"invocation error", "client error", "api error",
"invocation finally", "client finally", "api finally");
"api before", "client before", "invocation before", "provider before",
"provider after", "invocation after", "client after", "api after",
"provider error", "invocation error", "client error", "api error",
"provider finally", "invocation finally", "client finally", "api finally");
assertEquals(expectedOrder, evalOrder);
}

View File

@ -80,9 +80,9 @@ public class ProviderSpecTest {
assertNotNull(boolean_result.getReason());
}
@Specification(number="2.11.1", text="If the implementation includes a context transformer, the provider SHOULD accept a generic argument (or use an equivalent language feature) indicating the type of the transformed context. If such type information is supplied, more accurate type information can be supplied in the flag resolution methods.")
@Specification(number="2.10", text="The provider interface MAY define a context transformer method " +
"or function, which can be optionally implemented in order to transform the evaluation context prior to " +
"flag value resolution.")
@Test void not_doing() {}
@Specification(number="2.10", text="The provider interface MUST define a provider hook mechanism which can be optionally implemented in order to add hook instances to the evaluation life-cycle.")
@Specification(number="4.4.1", text="The API, Client, Provider, and invocation MUST have a method for registering hooks.")
@Test void provider_hooks() {
assertEquals(0, p.getProviderHooks().size());
}
}