Skip to content

Commit e43f7c8

Browse files
committed
chore: javadoc and tests for api, context
Signed-off-by: Todd Baert <[email protected]>
1 parent 08567f4 commit e43f7c8

File tree

3 files changed

+87
-35
lines changed

3 files changed

+87
-35
lines changed

src/main/java/dev/openfeature/sdk/OpenFeatureAPI.java

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,30 +48,62 @@ public static OpenFeatureAPI getInstance() {
4848
return SingletonHolder.INSTANCE;
4949
}
5050

51+
/**
52+
* Get metadata about the default provider.
53+
*
54+
* @return the provider metadata
55+
*/
5156
public Metadata getProviderMetadata() {
5257
return getProvider().getMetadata();
5358
}
5459

60+
/**
61+
* Get metadata about a registered provider using the client name.
62+
* An unbound or empty client name will return metadata from the default provider.
63+
*
64+
* @param clientName an identifier which logically binds clients with providers
65+
* @return the provider metadata
66+
*/
5567
public Metadata getProviderMetadata(String clientName) {
5668
return getProvider(clientName).getMetadata();
5769
}
5870

5971
/**
60-
* {@inheritDoc}
72+
* A factory function for creating new, OpenFeature clients.
73+
* Clients can contain their own state (e.g. logger, hook, context).
74+
* Multiple clients can be used to segment feature flag configuration.
75+
* All un-named or unbound clients use the default provider.
76+
*
77+
* @return a new client instance
6178
*/
6279
public Client getClient() {
6380
return getClient(null, null);
6481
}
6582

6683
/**
67-
* {@inheritDoc}
84+
* A factory function for creating new domainless OpenFeature clients.
85+
* Clients can contain their own state (e.g. logger, hook, context).
86+
* Multiple clients can be used to segment feature flag configuration.
87+
* If there is already a provider bound to this domain, this provider will be used.
88+
* Otherwise, the default provider is used until a provider is assigned to that domain.
89+
*
90+
* @param name an identifier which logically binds clients with providers
91+
* @return a new client instance
6892
*/
6993
public Client getClient(String name) {
7094
return getClient(name, null);
7195
}
7296

7397
/**
74-
* {@inheritDoc}
98+
* A factory function for creating new domainless OpenFeature clients.
99+
* Clients can contain their own state (e.g. logger, hook, context).
100+
* Multiple clients can be used to segment feature flag configuration.
101+
* If there is already a provider bound to this domain, this provider will be used.
102+
* Otherwise, the default provider is used until a provider is assigned to that domain.
103+
*
104+
* @param name a identifier which logically binds clients with providers
105+
* @param version a version identifier
106+
* @return a new client instance
75107
*/
76108
public Client getClient(String name, String version) {
77109
return new OpenFeatureClient(this,
@@ -80,7 +112,10 @@ public Client getClient(String name, String version) {
80112
}
81113

82114
/**
83-
* {@inheritDoc}
115+
* Sets the global evaluation context, which will be used for all evaluations.
116+
*
117+
* @param evaluationContext the context
118+
* @return api instance
84119
*/
85120
public OpenFeatureAPI setEvaluationContext(EvaluationContext evaluationContext) {
86121
try (AutoCloseableLock __ = lock.writeLockAutoCloseable()) {
@@ -90,7 +125,9 @@ public OpenFeatureAPI setEvaluationContext(EvaluationContext evaluationContext)
90125
}
91126

92127
/**
93-
* {@inheritDoc}
128+
* Gets the global evaluation context, which will be used for all evaluations.
129+
*
130+
* @return evaluation context
94131
*/
95132
public EvaluationContext getEvaluationContext() {
96133
try (AutoCloseableLock __ = lock.readLockAutoCloseable()) {
@@ -250,7 +287,10 @@ public FeatureProvider getProvider(String name) {
250287
}
251288

252289
/**
253-
* {@inheritDoc}
290+
* Adds hooks for globally, used for all evaluations.
291+
* Hooks are run in the order they're added in the before stage. They are run in reverse order for all other stages.
292+
*
293+
* @param hooks The hook to add.
254294
*/
255295
public void addHooks(Hook... hooks) {
256296
try (AutoCloseableLock __ = lock.writeLockAutoCloseable()) {
@@ -259,7 +299,8 @@ public void addHooks(Hook... hooks) {
259299
}
260300

261301
/**
262-
* {@inheritDoc}
302+
* Fetch the hooks associated to this client.
303+
* @return A list of {@link Hook}s.
263304
*/
264305
public List<Hook> getHooks() {
265306
try (AutoCloseableLock __ = lock.readLockAutoCloseable()) {
@@ -268,7 +309,7 @@ public List<Hook> getHooks() {
268309
}
269310

270311
/**
271-
* {@inheritDoc}
312+
* Removes all hooks.
272313
*/
273314
public void clearHooks() {
274315
try (AutoCloseableLock __ = lock.writeLockAutoCloseable()) {

src/test/java/dev/openfeature/sdk/DoSomethingProvider.java

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ class DoSomethingProvider implements FeatureProvider {
77
static final ImmutableMetadata DEFAULT_METADATA = ImmutableMetadata.builder().build();
88
private ImmutableMetadata flagMetadata;
99

10-
private EvaluationContext savedContext;
11-
1210
public DoSomethingProvider() {
1311
this.flagMetadata = DEFAULT_METADATA;
1412
}
@@ -17,18 +15,13 @@ public DoSomethingProvider(ImmutableMetadata flagMetadata) {
1715
this.flagMetadata = flagMetadata;
1816
}
1917

20-
EvaluationContext getMergedContext() {
21-
return savedContext;
22-
}
23-
2418
@Override
2519
public Metadata getMetadata() {
2620
return () -> name;
2721
}
2822

2923
@Override
3024
public ProviderEvaluation<Boolean> getBooleanEvaluation(String key, Boolean defaultValue, EvaluationContext ctx) {
31-
savedContext = ctx;
3225
return ProviderEvaluation.<Boolean>builder()
3326
.value(!defaultValue)
3427
.flagMetadata(flagMetadata)
@@ -45,7 +38,6 @@ public ProviderEvaluation<String> getStringEvaluation(String key, String default
4538

4639
@Override
4740
public ProviderEvaluation<Integer> getIntegerEvaluation(String key, Integer defaultValue, EvaluationContext ctx) {
48-
savedContext = ctx;
4941
return ProviderEvaluation.<Integer>builder()
5042
.value(defaultValue * 100)
5143
.flagMetadata(flagMetadata)
@@ -54,7 +46,6 @@ public ProviderEvaluation<Integer> getIntegerEvaluation(String key, Integer defa
5446

5547
@Override
5648
public ProviderEvaluation<Double> getDoubleEvaluation(String key, Double defaultValue, EvaluationContext ctx) {
57-
savedContext = ctx;
5849
return ProviderEvaluation.<Double>builder()
5950
.value(defaultValue * 100)
6051
.flagMetadata(flagMetadata)
@@ -63,7 +54,6 @@ public ProviderEvaluation<Double> getDoubleEvaluation(String key, Double default
6354

6455
@Override
6556
public ProviderEvaluation<Value> getObjectEvaluation(String key, Value defaultValue, EvaluationContext invocationContext) {
66-
savedContext = invocationContext;
6757
return ProviderEvaluation.<Value>builder()
6858
.value(null)
6959
.flagMetadata(flagMetadata)

src/test/java/dev/openfeature/sdk/FlagEvaluationSpecTest.java

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
import static org.junit.jupiter.api.Assertions.assertThrows;
1212
import static org.junit.jupiter.api.Assertions.assertTrue;
1313
import static org.mockito.ArgumentMatchers.any;
14+
import static org.mockito.ArgumentMatchers.argThat;
1415
import static org.mockito.Mockito.mock;
16+
import static org.mockito.Mockito.spy;
1517
import static org.mockito.Mockito.times;
1618
import static org.mockito.Mockito.verify;
1719

@@ -300,11 +302,30 @@ public void initialize(EvaluationContext evaluationContext) throws Exception {
300302
assertNotNull(result.getFlagMetadata());
301303
}
302304

303-
@Specification(number="3.2.1.1", text="The API, Client and invocation MUST have a method for supplying evaluation context.")
304305
@Specification(number="3.2.2.1", text="The API MUST have a method for setting the global evaluation context.")
306+
@Test void api_context() {
307+
String contextKey = "some-key";
308+
String contextValue = "some-value";
309+
DoSomethingProvider provider = spy( new DoSomethingProvider());
310+
FeatureProviderTestUtils.setFeatureProvider(provider);
311+
312+
Map<String, Value> attributes = new HashMap<>();
313+
attributes.put(contextKey, new Value(contextValue));
314+
EvaluationContext apiCtx = new ImmutableContext(attributes);
315+
316+
// set the global context
317+
api.setEvaluationContext(apiCtx);
318+
Client client = api.getClient();
319+
client.getBooleanValue("any-flag", false);
320+
321+
// assert that the value from the global context was passed to the provider
322+
verify(provider).getBooleanEvaluation(any(), any(), argThat((arg) -> arg.getValue(contextKey).asString().equals(contextValue)));
323+
}
324+
325+
@Specification(number="3.2.1.1", text="The API, Client and invocation MUST have a method for supplying evaluation context.")
305326
@Specification(number="3.2.3", text="Evaluation context MUST be merged in the order: API (global; lowest precedence) -> transaction -> client -> invocation -> before hooks (highest precedence), with duplicate values being overwritten.")
306327
@Test void multi_layer_context_merges_correctly() {
307-
DoSomethingProvider provider = new DoSomethingProvider();
328+
DoSomethingProvider provider = spy(new DoSomethingProvider());
308329
FeatureProviderTestUtils.setFeatureProvider(provider);
309330
TransactionContextPropagator transactionContextPropagator = new ThreadLocalTransactionContextPropagator();
310331
api.setTransactionContextPropagator(transactionContextPropagator);
@@ -349,21 +370,21 @@ public void initialize(EvaluationContext evaluationContext) throws Exception {
349370
invocationAttributes.put("invocation", new Value("4"));
350371
EvaluationContext invocationCtx = new ImmutableContext(invocationAttributes);
351372

352-
// dosomethingprovider inverts this value.
353-
assertTrue(c.getBooleanValue("key", false, invocationCtx));
354-
355-
EvaluationContext merged = provider.getMergedContext();
356-
assertEquals("1", merged.getValue("api").asString());
357-
assertEquals("2", merged.getValue("transaction").asString());
358-
assertEquals("3", merged.getValue("client").asString());
359-
assertEquals("4", merged.getValue("invocation").asString());
360-
assertEquals("2", merged.getValue("common1").asString(), "transaction merge is incorrect");
361-
assertEquals("3", merged.getValue("common2").asString(), "api client merge is incorrect");
362-
assertEquals("4", merged.getValue("common3").asString(), "invocation merge is incorrect");
363-
assertEquals("3", merged.getValue("common4").asString(), "api client merge is incorrect");
364-
assertEquals("4", merged.getValue("common5").asString(), "invocation merge is incorrect");
365-
assertEquals("4", merged.getValue("common6").asString(), "invocation merge is incorrect");
366-
373+
c.getBooleanValue("key", false, invocationCtx);
374+
375+
// assert the connect overrides
376+
verify(provider).getBooleanEvaluation(any(), any(), argThat((arg) -> {
377+
return arg.getValue("api").asString().equals("1") &&
378+
arg.getValue("transaction").asString().equals("2") &&
379+
arg.getValue("client").asString().equals("3") &&
380+
arg.getValue("invocation").asString().equals("4") &&
381+
arg.getValue("common1").asString().equals("2") &&
382+
arg.getValue("common2").asString().equals("3") &&
383+
arg.getValue("common3").asString().equals("4") &&
384+
arg.getValue("common4").asString().equals("3") &&
385+
arg.getValue("common5").asString().equals("4") &&
386+
arg.getValue("common6").asString().equals("4");
387+
}));
367388
}
368389

369390
@Specification(number="3.3.1.1", text="The API SHOULD have a method for setting a transaction context propagator.")

0 commit comments

Comments
 (0)