Skip to content

Commit d07e4e4

Browse files
committed
feat: add flagd provider implementation
1 parent ad1940d commit d07e4e4

File tree

12 files changed

+1041
-243
lines changed

12 files changed

+1041
-243
lines changed

Diff for: checkstyle-suppressions.xml

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0"?>
2+
3+
<!DOCTYPE suppressions PUBLIC "-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN" "https://checkstyle.org/dtds/suppressions_1_2.dtd">
4+
5+
<suppressions>
6+
<!-- don't check style of generated sources (GRPC bindings) -->
7+
<suppress files="[\\/]generated-sources[\\/]" checks="[a-zA-Z0-9]*" />
8+
</suppressions>

Diff for: checkstyle.xml

+208-191
Large diffs are not rendered by default.

Diff for: eclipse-java-google-style.xml.bak

+337
Large diffs are not rendered by default.

Diff for: hooks/open-telemetry/src/main/java/dev/openfeature/contrib/hooks/otel/OpenTelemetryHook.java

+8-7
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,21 @@
44
import dev.openfeature.javasdk.NoOpProvider;
55
import dev.openfeature.javasdk.OpenFeatureAPI;
66

7-
/**
7+
/**
88
* A placeholder.
99
*/
1010
public class OpenTelemetryHook {
11-
12-
/**
11+
12+
/**
1313
* Create a new OpenTelemetryHook instance.
1414
*/
15-
public OpenTelemetryHook() {
15+
private OpenTelemetryHook() {
1616
}
1717

18-
/**
19-
* A test method...
20-
* @return {boolean}
18+
/**
19+
* A test.
20+
*
21+
* @return boolean
2122
*/
2223
public static boolean test() {
2324
OpenFeatureAPI.getInstance().setProvider(new NoOpProvider());

Diff for: pom.xml

+20-7
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@
3333
<version>0.0.3</version>
3434
</dependency>
3535

36+
<!-- provided -->
37+
<dependency>
38+
<groupId>org.projectlombok</groupId>
39+
<artifactId>lombok</artifactId>
40+
<version>1.18.24</version>
41+
<scope>provided</scope>
42+
</dependency>
43+
3644
<!-- test -->
3745
<dependency>
3846
<groupId>org.mockito</groupId>
@@ -121,13 +129,13 @@
121129
<dependency>
122130
<groupId>com.puppycrawl.tools</groupId>
123131
<artifactId>checkstyle</artifactId>
124-
<version>8.31</version>
132+
<version>10.3.2</version>
125133
</dependency>
126134
</dependencies>
127135
<executions>
128136
<execution>
129137
<id>validate</id>
130-
<phase>validate</phase>
138+
<phase>verify</phase>
131139
<goals>
132140
<goal>check</goal>
133141
</goals>
@@ -138,7 +146,9 @@
138146
<plugin>
139147
<groupId>org.apache.maven.plugins</groupId>
140148
<artifactId>maven-pmd-plugin</artifactId>
141-
<version>3.13.0</version>
149+
<configuration>
150+
<excludeRoots>${basedir}/target/generated-sources/</excludeRoots>
151+
</configuration>
142152
<executions>
143153
<execution>
144154
<id>run-pmd</id>
@@ -223,21 +233,24 @@
223233
<version>3.4.0</version>
224234
<configuration>
225235
<failOnWarnings>true</failOnWarnings>
236+
<excludePackageNames>dev.openfeature.flagd.grpc</excludePackageNames>
226237
</configuration>
227238
<executions>
228239
<execution>
229-
<id>vet-javadoc</id>
230-
<phase>validate</phase>
240+
<id>attach-javadocs</id>
241+
<phase>compile</phase>
231242
<goals>
232243
<goal>jar</goal>
233244
</goals>
234245
</execution>
235246
<execution>
236-
<id>attach-javadocs</id>
247+
<id>vet-javadoc</id>
248+
<phase>verify</phase>
237249
<goals>
238250
<goal>jar</goal>
239251
</goals>
240252
</execution>
253+
241254
</executions>
242255
</plugin>
243256
<!-- end source & javadoc -->
@@ -250,7 +263,7 @@
250263
<executions>
251264
<execution>
252265
<id>sign-artifacts</id>
253-
<phase>verify</phase>
266+
<phase>install</phase>
254267
<goals>
255268
<goal>sign</goal>
256269
</goals>

Diff for: providers/flagd/pom.xml

+55
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,61 @@
3131

3232
<dependencies>
3333
<!-- we inherent dev.openfeature.javasdk and the test dependencies from the parent pom -->
34+
35+
<dependency>
36+
<groupId>io.grpc</groupId>
37+
<artifactId>grpc-netty-shaded</artifactId>
38+
<version>1.48.1</version>
39+
<scope>runtime</scope>
40+
</dependency>
41+
<dependency>
42+
<groupId>io.grpc</groupId>
43+
<artifactId>grpc-protobuf</artifactId>
44+
<version>1.48.1</version>
45+
</dependency>
46+
<dependency>
47+
<groupId>io.grpc</groupId>
48+
<artifactId>grpc-stub</artifactId>
49+
<version>1.48.1</version>
50+
</dependency>
51+
<dependency>
52+
<!-- necessary for Java 9+ -->
53+
<groupId>org.apache.tomcat</groupId>
54+
<artifactId>annotations-api</artifactId>
55+
<version>6.0.53</version>
56+
<scope>provided</scope>
57+
</dependency>
3458
</dependencies>
3559

60+
<build>
61+
<extensions>
62+
<extension>
63+
<groupId>kr.motd.maven</groupId>
64+
<artifactId>os-maven-plugin</artifactId>
65+
<version>1.6.2</version>
66+
</extension>
67+
</extensions>
68+
69+
<plugins>
70+
<plugin>
71+
<groupId>org.xolstice.maven.plugins</groupId>
72+
<artifactId>protobuf-maven-plugin</artifactId>
73+
<version>0.6.1</version>
74+
<configuration>
75+
<protocArtifact>com.google.protobuf:protoc:3.21.1:exe:${os.detected.classifier}</protocArtifact>
76+
<pluginId>grpc-java</pluginId>
77+
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.48.1:exe:${os.detected.classifier}</pluginArtifact>
78+
</configuration>
79+
<executions>
80+
<execution>
81+
<goals>
82+
<goal>compile</goal>
83+
<goal>compile-custom</goal>
84+
</goals>
85+
</execution>
86+
</executions>
87+
</plugin>
88+
</plugins>
89+
</build>
90+
3691
</project>
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,185 @@
11
package dev.openfeature.contrib.providers.flagd;
22

3-
import dev.openfeature.javasdk.Client;
4-
import dev.openfeature.javasdk.NoOpProvider;
5-
import dev.openfeature.javasdk.OpenFeatureAPI;
3+
import java.util.HashMap;
4+
import java.util.Map;
65

7-
/**
8-
* A placeholder.
6+
import org.apache.commons.lang3.EnumUtils;
7+
import org.apache.commons.lang3.NotImplementedException;
8+
9+
import com.google.protobuf.Struct;
10+
import com.google.protobuf.Value;
11+
12+
import dev.openfeature.javasdk.EvaluationContext;
13+
import dev.openfeature.javasdk.FeatureProvider;
14+
import dev.openfeature.javasdk.FlagEvaluationOptions;
15+
import dev.openfeature.javasdk.Metadata;
16+
import dev.openfeature.javasdk.ProviderEvaluation;
17+
import dev.openfeature.javasdk.Reason;
18+
import io.grpc.ManagedChannelBuilder;
19+
import lombok.extern.slf4j.Slf4j;
20+
import dev.openfeature.flagd.grpc.Schema.ResolveBooleanRequest;
21+
import dev.openfeature.flagd.grpc.Schema.ResolveBooleanResponse;
22+
import dev.openfeature.flagd.grpc.Schema.ResolveFloatRequest;
23+
import dev.openfeature.flagd.grpc.Schema.ResolveFloatResponse;
24+
import dev.openfeature.flagd.grpc.Schema.ResolveIntRequest;
25+
import dev.openfeature.flagd.grpc.Schema.ResolveIntResponse;
26+
import dev.openfeature.flagd.grpc.Schema.ResolveStringRequest;
27+
import dev.openfeature.flagd.grpc.Schema.ResolveStringResponse;
28+
import dev.openfeature.flagd.grpc.ServiceGrpc;
29+
import dev.openfeature.flagd.grpc.ServiceGrpc.ServiceBlockingStub;
30+
31+
/**
32+
* OpenFeature provider for flagd.
933
*/
10-
public class FlagdProvider {
34+
@Slf4j
35+
public class FlagdProvider implements FeatureProvider {
36+
37+
private ServiceBlockingStub serviceStub;
38+
static final String PROVIDER_NAME = "flagD Provider";
1139

12-
/**
40+
/**
41+
* Create a new FlagdProvider instance.
42+
*
43+
* @param protocol transport protocol, "http" or "https"
44+
* @param host flagd host, defaults to localhost
45+
* @param port flagd port, defaults to 8013
46+
*/
47+
public FlagdProvider(String protocol, String host, int port) {
48+
49+
this("https".equalsIgnoreCase(protocol)
50+
? ServiceGrpc.newBlockingStub(ManagedChannelBuilder.forAddress(host, port)
51+
.useTransportSecurity()
52+
.build()) :
53+
ServiceGrpc.newBlockingStub(ManagedChannelBuilder.forAddress(host, port)
54+
.usePlaintext()
55+
.build()));
56+
}
57+
58+
/**
1359
* Create a new FlagdProvider instance.
1460
*/
1561
public FlagdProvider() {
62+
this("http", "localhost", 8013);
1663
}
1764

18-
/**
19-
* A test method...
20-
*
21-
* @return {boolean}
65+
/**
66+
* Create a new FlagdProvider instance.
67+
*
68+
* @param serviceStub service stub instance to use
2269
*/
23-
public static boolean test() {
24-
OpenFeatureAPI.getInstance().setProvider(new NoOpProvider());
25-
Client client = OpenFeatureAPI.getInstance().getClient();
26-
return client.getBooleanValue("test", true);
70+
public FlagdProvider(ServiceBlockingStub serviceStub) {
71+
this.serviceStub = serviceStub;
72+
}
73+
74+
@Override
75+
public Metadata getMetadata() {
76+
return new Metadata() {
77+
@Override
78+
public String getName() {
79+
return PROVIDER_NAME;
80+
}
81+
};
82+
}
83+
84+
@Override
85+
public ProviderEvaluation<Boolean> getBooleanEvaluation(String key, Boolean defaultValue,
86+
EvaluationContext ctx, FlagEvaluationOptions options) {
87+
88+
ResolveBooleanRequest request = ResolveBooleanRequest.newBuilder()
89+
.setFlagKey(key)
90+
.setContext(this.convertContext(ctx))
91+
.build();
92+
ResolveBooleanResponse r = this.serviceStub.resolveBoolean(request);
93+
return ProviderEvaluation.<Boolean>builder()
94+
.value(r.getValue())
95+
.variant(r.getVariant())
96+
.reason(this.mapReason(r.getReason()))
97+
.build();
98+
}
99+
100+
@Override
101+
public ProviderEvaluation<String> getStringEvaluation(String key, String defaultValue,
102+
EvaluationContext ctx, FlagEvaluationOptions options) {
103+
ResolveStringRequest request = ResolveStringRequest.newBuilder()
104+
.setFlagKey(key)
105+
.setContext(this.convertContext(ctx)).build();
106+
ResolveStringResponse r = this.serviceStub.resolveString(request);
107+
return ProviderEvaluation.<String>builder().value(r.getValue())
108+
.variant(r.getVariant())
109+
.reason(this.mapReason(r.getReason()))
110+
.build();
111+
}
112+
113+
@Override
114+
public ProviderEvaluation<Double> getDoubleEvaluation(String key, Double defaultValue,
115+
EvaluationContext ctx, FlagEvaluationOptions options) {
116+
ResolveFloatRequest request = ResolveFloatRequest.newBuilder()
117+
.setFlagKey(key)
118+
.setContext(this.convertContext(ctx))
119+
.build();
120+
ResolveFloatResponse r = this.serviceStub.resolveFloat(request);
121+
return ProviderEvaluation.<Double>builder()
122+
.value(r.getValue())
123+
.variant(r.getVariant())
124+
.reason(this.mapReason(r.getReason()))
125+
.build();
126+
}
127+
128+
@Override
129+
public ProviderEvaluation<Integer> getIntegerEvaluation(String key, Integer defaultValue,
130+
EvaluationContext ctx, FlagEvaluationOptions options) {
131+
ResolveIntRequest request = ResolveIntRequest.newBuilder()
132+
.setFlagKey(key)
133+
.setContext(this.convertContext(ctx))
134+
.build();
135+
ResolveIntResponse r = this.serviceStub.resolveInt(request);
136+
return ProviderEvaluation.<Integer>builder()
137+
.value((int) r.getValue())
138+
.variant(r.getVariant())
139+
.reason(this.mapReason(r.getReason()))
140+
.build();
141+
}
142+
143+
@Override
144+
public <T> ProviderEvaluation<T> getObjectEvaluation(String key, T defaultValue,
145+
EvaluationContext ctx, FlagEvaluationOptions options) {
146+
throw new NotImplementedException();
147+
}
148+
149+
// Map FlagD reasons to Java SDK reasons.
150+
private Reason mapReason(String flagDreason) {
151+
if (!EnumUtils.isValidEnum(Reason.class, flagDreason)) {
152+
// until we have "STATIC" in the spec and SDK, we map STATIC to DEFAULT
153+
if ("STATIC".equals(flagDreason)) {
154+
return Reason.DEFAULT;
155+
} else {
156+
return Reason.UNKNOWN;
157+
}
158+
} else {
159+
return Reason.valueOf(flagDreason);
160+
}
161+
}
162+
163+
private Struct convertContext(EvaluationContext ctx) {
164+
// TODO: structure attributes?
165+
Map<String, Boolean> booleanAttributes = ctx.getBooleanAttributes();
166+
Map<String, String> stringAttributes = ctx.getStringAttributes();
167+
Map<String, Integer> intAttributes = ctx.getIntegerAttributes();
168+
Map<String, Value> values = new HashMap<>();
169+
booleanAttributes.keySet().stream().forEach((String key) -> values.put(key, Value
170+
.newBuilder()
171+
.setBoolValue(booleanAttributes.get(key))
172+
.build()));
173+
stringAttributes.keySet().stream().forEach((String key) -> values.put(key,
174+
Value.newBuilder().setStringValue(stringAttributes
175+
.get(key))
176+
.build()));
177+
intAttributes.keySet().stream().forEach((String key) -> values.put(key, Value
178+
.newBuilder().setNumberValue(intAttributes.get(key))
179+
.build()));
180+
return Struct.newBuilder()
181+
.putAllFields(values)
182+
.build();
27183
}
28184

29185
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package dev.openfeature.contrib.providers.flagd;
2+
3+
/**
4+
* The commication style to yus.
5+
*/
6+
enum Service {
7+
HTTP,
8+
GRPC
9+
}

0 commit comments

Comments
 (0)