Skip to content

Commit 37c41e5

Browse files
committed
cont.
Signed-off-by: liran2000 <[email protected]>
1 parent 579ec4e commit 37c41e5

File tree

7 files changed

+202
-94
lines changed

7 files changed

+202
-94
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@
279279
* **main:** release dev.openfeature.contrib.providers.go-feature-flag 0.4.3 ([#1246](https://github.com/open-feature/java-sdk-contrib/issues/1246)) ([b3bef6a](https://github.com/open-feature/java-sdk-contrib/commit/b3bef6a912080d79733ebb76b3acd4b08c132045))
280280
* **main:** release dev.openfeature.contrib.providers.jsonlogic 1.1.0 ([#616](https://github.com/open-feature/java-sdk-contrib/issues/616)) ([67e8572](https://github.com/open-feature/java-sdk-contrib/commit/67e85726c114c4c14f17a1fb4fe53808b820bed1))
281281
* **main:** release dev.openfeature.contrib.providers.jsonlogic 1.1.1 ([#838](https://github.com/open-feature/java-sdk-contrib/issues/838)) ([143ecb1](https://github.com/open-feature/java-sdk-contrib/commit/143ecb1183c1430b017537a317f8b606d6c9e124))
282-
* **main:** release dev.openfeature.contrib.providers.statsig 0.1.0 ([#700](https://github.com/open-feature/java-sdk-contrib/issues/700)) ([ac5851e](https://github.com/open-feature/java-sdk-contrib/commit/ac5851e2f0c7257418811626d7cd0ed32857c083))
282+
* **main:** release dev.openfeature.contrib.providers.optimizely 0.1.0 ([#700](https://github.com/open-feature/java-sdk-contrib/issues/700)) ([ac5851e](https://github.com/open-feature/java-sdk-contrib/commit/ac5851e2f0c7257418811626d7cd0ed32857c083))
283283
* **main:** release dev.openfeature.contrib.providers.unleash 0.1.0-alpha ([#620](https://github.com/open-feature/java-sdk-contrib/issues/620)) ([12d06d2](https://github.com/open-feature/java-sdk-contrib/commit/12d06d2c2b5ac0d82433752c8995b7b7d1b3462a))
284284
* **main:** release dev.openfeature.contrib.tools.junitopenfeature 0.0.2 ([#892](https://github.com/open-feature/java-sdk-contrib/issues/892)) ([0efebc7](https://github.com/open-feature/java-sdk-contrib/commit/0efebc7630657a3f398caadaf652e79b525e5ef9))
285285
* **main:** release dev.openfeature.contrib.tools.junitopenfeature 0.0.3 ([#896](https://github.com/open-feature/java-sdk-contrib/issues/896)) ([1c23b15](https://github.com/open-feature/java-sdk-contrib/commit/1c23b156d09011216cb57450c3ce23a309d5e657))

providers/optimizely/README.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Unofficial Optimizely OpenFeature Provider for Java
2+
3+
[optimizely](https://www.optimizely.com/optimization-glossary/feature-flags/) OpenFeature Provider can provide usage for optimizely via OpenFeature Java SDK.
4+
5+
## Installation
6+
7+
<!-- x-release-please-start-version -->
8+
9+
```xml
10+
11+
<dependency>
12+
<groupId>dev.openfeature.contrib.providers</groupId>
13+
<artifactId>optimizely</artifactId>
14+
<version>0.0.1</version>
15+
</dependency>
16+
```
17+
18+
<!-- x-release-please-end-version -->
19+
20+
## Concepts
21+
22+
* Boolean evaluation gets feature [enabled](https://docs.developers.optimizely.com/feature-experimentation/docs/create-feature-flags) value.
23+
* Object evaluation gets a structure representing the evaluated variant variables.
24+
* String/Integer/Double evaluations evaluation are not directly supported by Optimizely provider, use getObjectEvaluation instead.
25+
26+
## Usage
27+
Optimizely OpenFeature Provider is based on [Optimizely Java SDK documentation](https://docs.developers.optimizely.com/feature-experimentation/docs/java-sdk).
28+
29+
### Usage Example
30+
31+
```java
32+
OptimizelyProviderConfig config = OptimizelyProviderConfig.builder()
33+
.eventProcessor(mock(EventProcessor.class))
34+
.datafile(dataFileContent)
35+
.build();
36+
37+
provider = new OptimizelyProvider(config);
38+
provider.initialize(new MutableContext("test-targeting-key"));
39+
40+
ProviderEvaluation<Boolean> evaluation = provider.getBooleanEvaluation("string-feature", false, ctx);
41+
System.out.println("Feature enabled: " + evaluation.getValue());
42+
43+
ProviderEvaluation<Value> result = provider.getObjectEvaluation("string-feature", new Value(), ctx);
44+
System.out.println("Feature variable: " + result.getValue().asStructure().getValue("string_variable_1").asString());
45+
```
46+
47+
See [OptimizelyProviderTest](./src/test/java/dev/openfeature/contrib/providers/optimizely/OptimizelyProviderTest.java)
48+
for more information.
49+
50+
## Notes
51+
Some Optimizely custom operations are supported from the optimizely client via:
52+
53+
```java
54+
provider.getOptimizely()...
55+
```
56+
57+
## Optimizely Provider Tests Strategies
58+
59+
Unit test based on optimizely [Local Data File](https://docs.developers.optimizely.com/feature-experimentation/docs/initialize-sdk-java).
60+
See [OptimizelyProviderTest](./src/test/java/dev/openfeature/contrib/providers/optimizely/OptimizelyProviderTest.java)
61+
for more information.

providers/optimizely/src/main/java/dev/openfeature/contrib/providers/optimizely/OptimizelyProviderConfig.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,16 @@
11
package dev.openfeature.contrib.providers.optimizely;
22

3-
import com.optimizely.ab.bucketing.Bucketer;
4-
import com.optimizely.ab.bucketing.DecisionService;
53
import com.optimizely.ab.bucketing.UserProfileService;
64
import com.optimizely.ab.config.ProjectConfig;
75
import com.optimizely.ab.config.ProjectConfigManager;
86
import com.optimizely.ab.error.ErrorHandler;
97
import com.optimizely.ab.event.EventHandler;
108
import com.optimizely.ab.event.EventProcessor;
11-
import com.optimizely.ab.notification.NotificationCenter;
129
import com.optimizely.ab.odp.ODPManager;
13-
import com.optimizely.ab.optimizelyconfig.OptimizelyConfigManager;
1410
import com.optimizely.ab.optimizelydecision.OptimizelyDecideOption;
11+
import java.util.List;
1512
import lombok.Builder;
1613
import lombok.Getter;
17-
import java.util.List;
1814

1915
/** Configuration for initializing statsig provider. */
2016
@Getter

providers/optimizely/src/test/java/dev/openfeature/contrib/providers/optimizely/OptimizelyProviderTest.java

Lines changed: 32 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,41 @@
33
import static org.assertj.core.api.Assertions.assertThat;
44
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
55
import static org.junit.jupiter.api.Assertions.assertEquals;
6-
import static org.junit.jupiter.api.Assertions.assertFalse;
76
import static org.junit.jupiter.api.Assertions.assertNotNull;
8-
import static org.mockito.ArgumentMatchers.any;
9-
import static org.mockito.ArgumentMatchers.anyString;
7+
import static org.junit.jupiter.api.Assertions.assertTrue;
108
import static org.mockito.Mockito.mock;
11-
import static org.mockito.Mockito.when;
129

13-
import com.optimizely.ab.Optimizely;
14-
import com.optimizely.ab.OptimizelyUserContext;
15-
import com.optimizely.ab.bucketing.UserProfileService;
1610
import com.optimizely.ab.config.ProjectConfigManager;
17-
import com.optimizely.ab.error.ErrorHandler;
1811
import com.optimizely.ab.event.EventProcessor;
19-
import com.optimizely.ab.odp.ODPManager;
20-
import com.optimizely.ab.optimizelydecision.OptimizelyDecision;
21-
import com.optimizely.ab.optimizelyjson.OptimizelyJSON;
2212
import dev.openfeature.sdk.EvaluationContext;
2313
import dev.openfeature.sdk.MutableContext;
2414
import dev.openfeature.sdk.ProviderEvaluation;
2515
import dev.openfeature.sdk.Value;
26-
import java.util.List;
27-
import java.util.Map;
16+
import java.io.File;
2817
import lombok.SneakyThrows;
18+
import org.junit.jupiter.api.BeforeAll;
2919
import org.junit.jupiter.api.Test;
3020

3121
public class OptimizelyProviderTest {
3222

23+
private static OptimizelyProvider provider;
24+
25+
@SneakyThrows
26+
@BeforeAll
27+
static void setUp() {
28+
File dataFile = new File(
29+
OptimizelyProviderTest.class.getClassLoader().getResource("data.json").getFile());
30+
String dataFileContent = new String(java.nio.file.Files.readAllBytes(dataFile.toPath()));
31+
32+
OptimizelyProviderConfig config = OptimizelyProviderConfig.builder()
33+
.eventProcessor(mock(EventProcessor.class))
34+
.datafile(dataFileContent)
35+
.build();
36+
37+
provider = new OptimizelyProvider(config);
38+
provider.initialize(new MutableContext("test-targeting-key"));
39+
}
40+
3341
@Test
3442
public void test_constructor_initializes_provider_with_valid_config() {
3543
OptimizelyProviderConfig config = OptimizelyProviderConfig.builder()
@@ -38,10 +46,10 @@ public void test_constructor_initializes_provider_with_valid_config() {
3846
.datafile("test-datafile")
3947
.build();
4048

41-
OptimizelyProvider provider = new OptimizelyProvider(config);
49+
OptimizelyProvider localProvider = new OptimizelyProvider(config);
4250

43-
assertThat(provider).isNotNull();
44-
assertEquals("Optimizely", provider.getMetadata().getName());
51+
assertThat(localProvider).isNotNull();
52+
assertEquals("Optimizely", localProvider.getMetadata().getName());
4553
}
4654

4755
@Test
@@ -52,92 +60,30 @@ public void test_initialize_handles_null_configuration_parameters() {
5260
.datafile(null)
5361
.build();
5462

55-
OptimizelyProvider provider = new OptimizelyProvider(config);
63+
OptimizelyProvider localProvider = new OptimizelyProvider(config);
5664
EvaluationContext evaluationContext = mock(EvaluationContext.class);
5765

5866
assertDoesNotThrow(() -> {
59-
provider.initialize(evaluationContext);
67+
localProvider.initialize(evaluationContext);
6068
});
6169
}
6270

63-
@Test
64-
public void test_initialize_builds_optimizely_and_context_transformer() throws Exception {
65-
OptimizelyProviderConfig config = mock(OptimizelyProviderConfig.class);
66-
when(config.getProjectConfigManager()).thenReturn(mock(ProjectConfigManager.class));
67-
OptimizelyProvider provider = new OptimizelyProvider(config);
68-
provider.initialize(mock(EvaluationContext.class));
69-
}
70-
71-
@SneakyThrows
72-
@Test
73-
public void test_get_string_evaluation_returns_correct_value() {
74-
OptimizelyProviderConfig config = mock(OptimizelyProviderConfig.class);
75-
ContextTransformer contextTransformer = mock(ContextTransformer.class);
76-
OptimizelyUserContext userContext = mock(OptimizelyUserContext.class);
77-
OptimizelyDecision decision = mock(OptimizelyDecision.class);
78-
79-
when(contextTransformer.transform(any(EvaluationContext.class))).thenReturn(userContext);
80-
when(userContext.decide(anyString())).thenReturn(decision);
81-
when(decision.getVariationKey()).thenReturn("variationKey");
82-
when(decision.getEnabled()).thenReturn(true);
83-
84-
OptimizelyProvider provider = new OptimizelyProvider(config);
85-
provider.initialize(new MutableContext());
86-
87-
EvaluationContext ctx = new MutableContext("targetingKey");
88-
ProviderEvaluation<String> result = provider.getStringEvaluation("featureKey", "defaultValue", ctx);
89-
90-
assertEquals("variationKey", result.getValue());
91-
92-
when(decision.getEnabled()).thenReturn(false);
93-
result = provider.getStringEvaluation("featureKey", "defaultValue", ctx);
94-
95-
assertEquals("defaultValue", result.getValue());
96-
}
97-
9871
@SneakyThrows
9972
@Test
10073
public void test_get_object_evaluation_returns_transformed_variables() {
101-
OptimizelyProviderConfig config = mock(OptimizelyProviderConfig.class);
102-
ContextTransformer contextTransformer = mock(ContextTransformer.class);
103-
OptimizelyUserContext userContext = mock(OptimizelyUserContext.class);
104-
OptimizelyDecision decision = mock(OptimizelyDecision.class);
105-
OptimizelyJSON optimizelyJSON = mock(OptimizelyJSON.class);
106-
107-
when(contextTransformer.transform(any(EvaluationContext.class))).thenReturn(userContext);
108-
when(userContext.decide(anyString())).thenReturn(decision);
109-
when(decision.getEnabled()).thenReturn(true);
110-
when(decision.getVariables()).thenReturn(optimizelyJSON);
111-
when(optimizelyJSON.toMap()).thenReturn(Map.of("key", "value"));
112-
113-
OptimizelyProvider provider = new OptimizelyProvider(config);
114-
provider.initialize(mock(EvaluationContext.class));
115-
11674
EvaluationContext ctx = new MutableContext("targetingKey");
117-
ProviderEvaluation<Value> result = provider.getObjectEvaluation("featureKey", new Value(), ctx);
75+
ProviderEvaluation<Value> result = provider.getObjectEvaluation("string-feature", new Value(), ctx);
11876

11977
assertNotNull(result.getValue());
120-
assertNotNull(result.getValue().asStructure().getValue("variables"));
78+
assertEquals("string_feature_variation", result.getVariant());
79+
assertEquals("str1", result.getValue().asStructure().getValue("string_variable_1").asString());
12180
}
12281

12382
@Test
12483
public void test_get_boolean_evaluation_handles_null_variation_key() {
125-
OptimizelyProviderConfig config = mock(OptimizelyProviderConfig.class);
126-
ContextTransformer contextTransformerMock = mock(ContextTransformer.class);
127-
OptimizelyUserContext userContextMock = mock(OptimizelyUserContext.class);
128-
OptimizelyDecision decisionMock = mock(OptimizelyDecision.class);
129-
130-
when(contextTransformerMock.transform(any())).thenReturn(userContextMock);
131-
when(userContextMock.decide(anyString())).thenReturn(decisionMock);
132-
when(decisionMock.getVariationKey()).thenReturn(null);
133-
when(decisionMock.getReasons()).thenReturn(List.of("reason1", "reason2"));
134-
when(decisionMock.getEnabled()).thenReturn(false);
135-
136-
OptimizelyProvider provider = new OptimizelyProvider(config);
137-
138-
ProviderEvaluation<Boolean> evaluation = provider.getBooleanEvaluation("key", false, mock(EvaluationContext.class));
84+
EvaluationContext ctx = new MutableContext("targetingKey");
85+
ProviderEvaluation<Boolean> evaluation = provider.getBooleanEvaluation("string-feature", false, ctx);
13986

140-
assertFalse(evaluation.getValue());
141-
assertEquals("reason1, reason2", evaluation.getReason());
87+
assertTrue(evaluation.getValue());
14288
}
14389
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
{
2+
"version": "4",
3+
"rollouts": [
4+
{
5+
"experiments": [
6+
{
7+
"status": "Running",
8+
"key": "boolean-feature-rollout",
9+
"layerId": "boolean_feature_layer",
10+
"trafficAllocation": [
11+
{
12+
"entityId": "boolean_feature_variation",
13+
"endOfRange": 10000
14+
}
15+
],
16+
"audienceIds": [],
17+
"variations": [
18+
{
19+
"variables": [],
20+
"id": "boolean_feature_variation",
21+
"key": "boolean_feature_variation",
22+
"featureEnabled": true
23+
}
24+
],
25+
"forcedVariations": {},
26+
"id": "boolean_feature_rollout_experiment"
27+
}
28+
],
29+
"id": "boolean_feature_rollout"
30+
},
31+
{
32+
"experiments": [
33+
{
34+
"status": "Running",
35+
"key": "string-feature-rollout",
36+
"layerId": "string_feature_layer",
37+
"trafficAllocation": [
38+
{
39+
"entityId": "string_feature_variation",
40+
"endOfRange": 10000
41+
}
42+
],
43+
"audienceIds": [],
44+
"variations": [
45+
{
46+
"variables": [
47+
{
48+
"id": "string_variable_1",
49+
"value": "str1"
50+
}
51+
],
52+
"id": "string_feature_variation",
53+
"key": "string_feature_variation",
54+
"featureEnabled": true
55+
}
56+
],
57+
"forcedVariations": {},
58+
"id": "string_feature_rollout_experiment"
59+
}
60+
],
61+
"id": "string_feature_rollout"
62+
}
63+
],
64+
"typedAudiences": [],
65+
"anonymizeIP": false,
66+
"projectId": "12345678901",
67+
"variables": [
68+
{
69+
"defaultValue": "str1",
70+
"type": "string",
71+
"id": "string_variable_1",
72+
"key": "string_variable_1"
73+
}
74+
],
75+
"featureFlags": [
76+
{
77+
"experimentIds": [],
78+
"rolloutId": "boolean_feature_rollout",
79+
"variables": [],
80+
"id": "boolean_feature_flag",
81+
"key": "boolean-feature"
82+
},
83+
{
84+
"experimentIds": [],
85+
"rolloutId": "string_feature_rollout",
86+
"variables": [
87+
{
88+
"defaultValue": "str1",
89+
"type": "string",
90+
"id": "string_variable_1",
91+
"key": "string_variable_1"
92+
}
93+
],
94+
"id": "string_feature_flag",
95+
"key": "string-feature"
96+
}
97+
],
98+
"experiments": [],
99+
"audiences": [],
100+
"groups": [],
101+
"attributes": [],
102+
"accountId": "12345678901",
103+
"events": [],
104+
"revision": "1"
105+
}

providers/optimizely/src/test/resources/log4j2-test.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@
1010
<AppenderRef ref="consoleLogger"/>
1111
</Root>
1212
</Loggers>
13-
</Configuration>
13+
</Configuration>

providers/statsig/src/main/java/dev/openfeature/contrib/providers/statsig/ContextTransformer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package dev.openfeature.contrib.providers.statsig;
1+
package dev.openfeature.contrib.providers.optimizely;
22

33
import com.statsig.sdk.StatsigUser;
44
import dev.openfeature.sdk.EvaluationContext;

0 commit comments

Comments
 (0)