Skip to content

Commit 4126b51

Browse files
authored
chore: javadoc and tests for api, context (#942)
* chore: javadoc and tests for api, context Signed-off-by: Todd Baert <[email protected]> * fixup: lint Signed-off-by: Todd Baert <[email protected]> --------- Signed-off-by: Todd Baert <[email protected]>
1 parent a0b1d25 commit 4126b51

File tree

3 files changed

+87
-35
lines changed

3 files changed

+87
-35
lines changed

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

+49-8
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 domain an identifier which logically binds clients with providers
65+
* @return the provider metadata
66+
*/
5567
public Metadata getProviderMetadata(String domain) {
5668
return getProvider(domain).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 domain an identifier which logically binds clients with providers
91+
* @return a new client instance
6892
*/
6993
public Client getClient(String domain) {
7094
return getClient(domain, 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 domain 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 domain, String version) {
77109
return new OpenFeatureClient(this,
@@ -80,7 +112,10 @@ public Client getClient(String domain, 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 domain) {
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

-10
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

+38-17
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

@@ -307,11 +309,30 @@ public void initialize(EvaluationContext evaluationContext) throws Exception {
307309
assertNotNull(result.getFlagMetadata());
308310
}
309311

310-
@Specification(number="3.2.1.1", text="The API, Client and invocation MUST have a method for supplying evaluation context.")
311312
@Specification(number="3.2.2.1", text="The API MUST have a method for setting the global evaluation context.")
313+
@Test void api_context() {
314+
String contextKey = "some-key";
315+
String contextValue = "some-value";
316+
DoSomethingProvider provider = spy( new DoSomethingProvider());
317+
FeatureProviderTestUtils.setFeatureProvider(provider);
318+
319+
Map<String, Value> attributes = new HashMap<>();
320+
attributes.put(contextKey, new Value(contextValue));
321+
EvaluationContext apiCtx = new ImmutableContext(attributes);
322+
323+
// set the global context
324+
api.setEvaluationContext(apiCtx);
325+
Client client = api.getClient();
326+
client.getBooleanValue("any-flag", false);
327+
328+
// assert that the value from the global context was passed to the provider
329+
verify(provider).getBooleanEvaluation(any(), any(), argThat((arg) -> arg.getValue(contextKey).asString().equals(contextValue)));
330+
}
331+
332+
@Specification(number="3.2.1.1", text="The API, Client and invocation MUST have a method for supplying evaluation context.")
312333
@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.")
313334
@Test void multi_layer_context_merges_correctly() {
314-
DoSomethingProvider provider = new DoSomethingProvider();
335+
DoSomethingProvider provider = spy(new DoSomethingProvider());
315336
FeatureProviderTestUtils.setFeatureProvider(provider);
316337
TransactionContextPropagator transactionContextPropagator = new ThreadLocalTransactionContextPropagator();
317338
api.setTransactionContextPropagator(transactionContextPropagator);
@@ -356,21 +377,21 @@ public void initialize(EvaluationContext evaluationContext) throws Exception {
356377
invocationAttributes.put("invocation", new Value("4"));
357378
EvaluationContext invocationCtx = new ImmutableContext(invocationAttributes);
358379

359-
// dosomethingprovider inverts this value.
360-
assertTrue(c.getBooleanValue("key", false, invocationCtx));
361-
362-
EvaluationContext merged = provider.getMergedContext();
363-
assertEquals("1", merged.getValue("api").asString());
364-
assertEquals("2", merged.getValue("transaction").asString());
365-
assertEquals("3", merged.getValue("client").asString());
366-
assertEquals("4", merged.getValue("invocation").asString());
367-
assertEquals("2", merged.getValue("common1").asString(), "transaction merge is incorrect");
368-
assertEquals("3", merged.getValue("common2").asString(), "api client merge is incorrect");
369-
assertEquals("4", merged.getValue("common3").asString(), "invocation merge is incorrect");
370-
assertEquals("3", merged.getValue("common4").asString(), "api client merge is incorrect");
371-
assertEquals("4", merged.getValue("common5").asString(), "invocation merge is incorrect");
372-
assertEquals("4", merged.getValue("common6").asString(), "invocation merge is incorrect");
373-
380+
c.getBooleanValue("key", false, invocationCtx);
381+
382+
// assert the connect overrides
383+
verify(provider).getBooleanEvaluation(any(), any(), argThat((arg) -> {
384+
return arg.getValue("api").asString().equals("1") &&
385+
arg.getValue("transaction").asString().equals("2") &&
386+
arg.getValue("client").asString().equals("3") &&
387+
arg.getValue("invocation").asString().equals("4") &&
388+
arg.getValue("common1").asString().equals("2") &&
389+
arg.getValue("common2").asString().equals("3") &&
390+
arg.getValue("common3").asString().equals("4") &&
391+
arg.getValue("common4").asString().equals("3") &&
392+
arg.getValue("common5").asString().equals("4") &&
393+
arg.getValue("common6").asString().equals("4");
394+
}));
374395
}
375396

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

0 commit comments

Comments
 (0)