Skip to content

Commit 063d66b

Browse files
committed
feat: new provider
Signed-off-by: Simon Schrottner <[email protected]>
1 parent a14931f commit 063d66b

File tree

7 files changed

+96
-29
lines changed

7 files changed

+96
-29
lines changed

providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/Config.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public final class Config {
4040

4141
static final String RESOLVER_RPC = "rpc";
4242
static final String RESOLVER_IN_PROCESS = "in-process";
43+
static final String RESOLVER_FILE = "file";
4344

4445
public static final String STATIC_REASON = "STATIC";
4546
public static final String CACHED_REASON = "CACHED";
@@ -87,6 +88,8 @@ static Resolver fromValueProvider(Function<String, String> provider) {
8788
return Resolver.IN_PROCESS;
8889
case "rpc":
8990
return Resolver.RPC;
91+
case "file":
92+
return Resolver.FILE;
9093
default:
9194
log.warn("Unsupported resolver variable: {}", resolverVar);
9295
return DEFAULT_RESOLVER_TYPE;
@@ -143,6 +146,11 @@ public String asString() {
143146
public String asString() {
144147
return RESOLVER_IN_PROCESS;
145148
}
149+
},
150+
FILE {
151+
public String asString() {
152+
return RESOLVER_FILE;
153+
}
146154
}
147155
}
148156
}

providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdOptions.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,7 @@ public class FlagdOptions {
119119
* File source of flags to be used by offline mode.
120120
* Setting this enables the offline mode of the in-process provider.
121121
*/
122-
@Builder.Default
123-
private String offlineFlagSourcePath = fallBackToEnvOrDefault(Config.OFFLINE_SOURCE_PATH, null);
122+
private String offlineFlagSourcePath;
124123

125124
/**
126125
* gRPC custom target string.
@@ -193,7 +192,19 @@ void prebuild() {
193192
resolverType = fromValueProvider(System::getenv);
194193
}
195194

196-
if (port == 0) {
195+
if (offlineFlagSourcePath == null
196+
|| offlineFlagSourcePath.isEmpty()) {
197+
offlineFlagSourcePath = fallBackToEnvOrDefault(Config.OFFLINE_SOURCE_PATH, null);
198+
}
199+
200+
if (offlineFlagSourcePath != null
201+
&& !offlineFlagSourcePath.isEmpty()) {
202+
resolverType = Config.Resolver.FILE;
203+
} else if (resolverType == Config.Resolver.FILE) {
204+
throw new IllegalArgumentException("Resolver Type 'FILE' requires a offlineFlagSourcePath");
205+
}
206+
207+
if (port == 0 && resolverType != Config.Resolver.FILE) {
197208
port = Integer.parseInt(
198209
fallBackToEnvOrDefault(Config.PORT_ENV_VAR_NAME, determineDefaultPortForResolver()));
199210
}

providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdProvider.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ public FlagdProvider() {
8282
*/
8383
public FlagdProvider(final FlagdOptions options) {
8484
switch (options.getResolverType().asString()) {
85+
case Config.RESOLVER_FILE:
8586
case Config.RESOLVER_IN_PROCESS:
8687
this.flagResolver = new InProcessResolver(options, this::onProviderEvent);
8788
break;

providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/FlagdOptionsTest.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@ void TestBuilderOptions() {
7676
assertEquals("lru", flagdOptions.getCacheType());
7777
assertEquals(100, flagdOptions.getMaxCacheSize());
7878
assertEquals("app=weatherApp", flagdOptions.getSelector());
79-
assertEquals("some-path", flagdOptions.getOfflineFlagSourcePath());
8079
assertEquals(openTelemetry, flagdOptions.getOpenTelemetry());
8180
assertEquals(connector, flagdOptions.getCustomConnector());
8281
assertEquals(Resolver.IN_PROCESS, flagdOptions.getResolverType());
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package dev.openfeature.contrib.providers.flagd.e2e;
2+
3+
import dev.openfeature.contrib.providers.flagd.Config;
4+
import org.apache.logging.log4j.core.config.Order;
5+
import org.junit.platform.suite.api.BeforeSuite;
6+
import org.junit.platform.suite.api.ConfigurationParameter;
7+
import org.junit.platform.suite.api.ExcludeTags;
8+
import org.junit.platform.suite.api.IncludeEngines;
9+
import org.junit.platform.suite.api.IncludeTags;
10+
import org.junit.platform.suite.api.SelectDirectories;
11+
import org.junit.platform.suite.api.Suite;
12+
import org.testcontainers.junit.jupiter.Testcontainers;
13+
14+
import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME;
15+
import static io.cucumber.junit.platform.engine.Constants.OBJECT_FACTORY_PROPERTY_NAME;
16+
import static io.cucumber.junit.platform.engine.Constants.PLUGIN_PROPERTY_NAME;
17+
18+
/**
19+
* Class for running the reconnection tests for the RPC provider
20+
*/
21+
@Order(value = Integer.MAX_VALUE)
22+
@Suite
23+
@IncludeEngines("cucumber")
24+
@SelectDirectories("test-harness/gherkin")
25+
// if you want to run just one feature file, use the following line instead of @SelectDirectories
26+
// @SelectFile("test-harness/gherkin/connection.feature")
27+
@ConfigurationParameter(key = PLUGIN_PROPERTY_NAME, value = "pretty")
28+
@ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "dev.openfeature.contrib.providers.flagd.e2e.steps")
29+
@ConfigurationParameter(key = OBJECT_FACTORY_PROPERTY_NAME, value = "io.cucumber.picocontainer.PicoFactory")
30+
@IncludeTags("in-process")
31+
@ExcludeTags({"unixsocket", "targetURI", "reconnect", "customCert", "events"})
32+
@Testcontainers
33+
public class RunFileTest {
34+
35+
@BeforeSuite
36+
public static void before() {
37+
State.resolverType = Config.Resolver.FILE;
38+
}
39+
}

providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/ConfigSteps.java

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -87,15 +87,16 @@ public void the_option_of_type_should_have_the_value(String option, String type,
8787
}
8888

8989
option = mapOptionNames(option);
90-
91-
assertThat(state.options).hasFieldOrPropertyWithValue(option, convert);
92-
93-
// Resetting env vars
94-
for (Map.Entry<String, String> envVar : envVarsSet.entrySet()) {
95-
if (envVar.getValue() == null) {
96-
EnvironmentVariableUtils.clear(envVar.getKey());
97-
} else {
98-
EnvironmentVariableUtils.set(envVar.getKey(), envVar.getValue());
90+
try {
91+
assertThat(state.options).hasFieldOrPropertyWithValue(option, convert);
92+
} finally {
93+
// Resetting env vars
94+
for (Map.Entry<String, String> envVar : envVarsSet.entrySet()) {
95+
if (envVar.getValue() == null) {
96+
EnvironmentVariableUtils.clear(envVar.getKey());
97+
} else {
98+
EnvironmentVariableUtils.set(envVar.getKey(), envVar.getValue());
99+
}
99100
}
100101
}
101102
}

providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/steps/ProviderSteps.java

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,8 @@ public int getPort(Config.Resolver resolver, ProviderType providerType) {
134134
case SSL:
135135
return toxiproxy.getMappedPort(8669);
136136
}
137+
case FILE:
138+
return 0;
137139
default:
138140
throw new IllegalArgumentException("Unsupported resolver: " + resolver);
139141
}
@@ -143,10 +145,24 @@ public int getPort(Config.Resolver resolver, ProviderType providerType) {
143145
public void setupProvider(String providerType) throws IOException {
144146
state.builder.deadline(500).keepAlive(0).retryGracePeriod(3);
145147
boolean wait = true;
148+
File flags = new File("test-harness/flags");
149+
ObjectMapper objectMapper = new ObjectMapper();
150+
Object merged = new Object();
151+
for (File listFile : Objects.requireNonNull(flags.listFiles())) {
152+
ObjectReader updater = objectMapper.readerForUpdating(merged);
153+
merged = updater.readValue(listFile, Object.class);
154+
}
155+
Path offlinePath = Files.createTempFile("flags", ".json");
156+
objectMapper.writeValue(offlinePath.toFile(), merged);
146157
switch (providerType) {
147158
case "unavailable":
148159
this.state.providerType = ProviderType.SOCKET;
149160
state.builder.port(UNAVAILABLE_PORT);
161+
if (State.resolverType == Config.Resolver.FILE) {
162+
163+
state.builder
164+
.offlineFlagSourcePath("not-existing");
165+
}
150166
wait = false;
151167
break;
152168
case "socket":
@@ -167,25 +183,17 @@ public void setupProvider(String providerType) throws IOException {
167183
.tls(true)
168184
.certPath(absolutePath);
169185
break;
170-
case "offline":
171-
File flags = new File("test-harness/flags");
172-
ObjectMapper objectMapper = new ObjectMapper();
173-
Object merged = new Object();
174-
for (File listFile : Objects.requireNonNull(flags.listFiles())) {
175-
ObjectReader updater = objectMapper.readerForUpdating(merged);
176-
merged = updater.readValue(listFile, Object.class);
177-
}
178-
Path offlinePath = Files.createTempFile("flags", ".json");
179-
objectMapper.writeValue(offlinePath.toFile(), merged);
180-
181-
state.builder
182-
.port(UNAVAILABLE_PORT)
183-
.offlineFlagSourcePath(offlinePath.toAbsolutePath().toString());
184-
break;
185186

186187
default:
187188
this.state.providerType = ProviderType.DEFAULT;
188-
state.builder.port(getPort(State.resolverType, state.providerType));
189+
if (State.resolverType == Config.Resolver.FILE) {
190+
191+
state.builder
192+
.port(UNAVAILABLE_PORT)
193+
.offlineFlagSourcePath(offlinePath.toAbsolutePath().toString());
194+
} else {
195+
state.builder.port(getPort(State.resolverType, state.providerType));
196+
}
189197
break;
190198
}
191199
FeatureProvider provider =

0 commit comments

Comments
 (0)