Skip to content

Commit b55a52e

Browse files
feat(go-feature-flag): Support Exporter Metadata (#1167)
Signed-off-by: Thomas Poignant <[email protected]>
1 parent 969448a commit b55a52e

File tree

4 files changed

+78
-8
lines changed

4 files changed

+78
-8
lines changed

providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/GoFeatureFlagProviderOptions.java

+9
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.github.benmanes.caffeine.cache.Caffeine;
44
import dev.openfeature.sdk.ProviderEvaluation;
5+
import java.util.Map;
56
import lombok.Builder;
67
import lombok.Getter;
78

@@ -90,4 +91,12 @@ public class GoFeatureFlagProviderOptions {
9091
* retrieved in the cache. default: false
9192
*/
9293
private boolean disableDataCollection;
94+
95+
/**
96+
* (optional) exporterMetadata is the metadata we send to the GO Feature Flag relay proxy when we report the
97+
* evaluation data usage.
98+
* ‼️Important: If you are using a GO Feature Flag relay proxy before version v1.41.0, the information of this
99+
* field will not be added to your feature events.
100+
*/
101+
private Map<String, Object> exporterMetadata;
93102
}

providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/controller/GoFeatureFlagController.java

+7-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@
3131
import dev.openfeature.sdk.exceptions.TypeMismatchError;
3232
import java.io.IOException;
3333
import java.net.HttpURLConnection;
34+
import java.util.HashMap;
3435
import java.util.List;
36+
import java.util.Map;
3537
import java.util.concurrent.TimeUnit;
3638
import lombok.Builder;
3739
import lombok.extern.slf4j.Slf4j;
@@ -69,6 +71,9 @@ public class GoFeatureFlagController {
6971

7072
private final HttpUrl parsedEndpoint;
7173

74+
/** exporterMetadata contains the metadata to send to the collector API. */
75+
private Map<String, Object> exporterMetadata = new HashMap<>();
76+
7277
/**
7378
* etag contains the etag of the configuration, if null, it means that the configuration has never
7479
* been retrieved.
@@ -85,6 +90,7 @@ public class GoFeatureFlagController {
8590
@Builder
8691
private GoFeatureFlagController(final GoFeatureFlagProviderOptions options) throws InvalidOptions {
8792
this.apiKey = options.getApiKey();
93+
this.exporterMetadata = options.getExporterMetadata();
8894

8995
this.parsedEndpoint = HttpUrl.parse(options.getEndpoint());
9096
if (this.parsedEndpoint == null) {
@@ -218,7 +224,7 @@ public <T> EvaluationResponse<T> evaluateFlag(
218224
*/
219225
public void sendEventToDataCollector(List<Event> eventsList) {
220226
try {
221-
Events events = new Events(eventsList);
227+
Events events = new Events(eventsList, this.exporterMetadata);
222228
HttpUrl url = this.parsedEndpoint
223229
.newBuilder()
224230
.addEncodedPathSegment("v1")

providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/hook/events/Events.java

+19-7
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,28 @@
99
/** Events data. */
1010
@Getter
1111
public class Events {
12-
private static final Map<String, String> meta = new HashMap<>();
13-
14-
static {
15-
meta.put("provider", "java");
16-
meta.put("openfeature", "true");
17-
}
12+
/**
13+
* meta contains the metadata of the events to be sent along the events.
14+
*/
15+
private final Map<String, Object> meta = new HashMap<>();
1816

17+
/**
18+
* list of events to be sent to the data collector to collect the evaluation data.
19+
*/
1920
private final List<Event> events;
2021

21-
public Events(List<Event> events) {
22+
/**
23+
* Constructor.
24+
*
25+
* @param events - list of events to be sent to the data collector to collect the evaluation data.
26+
* @param exporterMetadata - metadata of the events to be sent along the events.
27+
*/
28+
public Events(List<Event> events, Map<String, Object> exporterMetadata) {
2229
this.events = new ArrayList<>(events);
30+
this.meta.put("provider", "java");
31+
this.meta.put("openfeature", true);
32+
if (exporterMetadata != null) {
33+
this.meta.putAll(exporterMetadata);
34+
}
2335
}
2436
}

providers/go-feature-flag/src/test/java/dev/openfeature/contrib/providers/gofeatureflag/GoFeatureFlagProviderTest.java

+43
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.nio.file.Paths;
2727
import java.util.ArrayList;
2828
import java.util.Arrays;
29+
import java.util.HashMap;
2930
import java.util.List;
3031
import java.util.Map;
3132
import lombok.SneakyThrows;
@@ -44,6 +45,7 @@
4445
@Slf4j
4546
class GoFeatureFlagProviderTest {
4647
private int publishEventsRequestsReceived = 0;
48+
private Map exporterMetadata;
4749
private int flagChangeCallCounter = 0;
4850
private boolean flagChanged404 = false;
4951

@@ -67,6 +69,7 @@ public MockResponse dispatch(RecordedRequest request) {
6769
String requestBody = request.getBody().readString(StandardCharsets.UTF_8);
6870
Map<String, Object> map = requestMapper.readValue(requestBody, Map.class);
6971
publishEventsRequestsReceived = ((List) map.get("events")).size();
72+
exporterMetadata = ((Map) map.get("meta"));
7073
if (requestBody.contains("fail_500") && publishEventsRequestsReceived == 1) {
7174
return new MockResponse().setResponseCode(502);
7275
}
@@ -944,6 +947,46 @@ void should_stop_calling_flag_change_if_receive_404() {
944947
assertEquals(1, this.flagChangeCallCounter);
945948
}
946949

950+
@SneakyThrows
951+
@Test
952+
void should_send_exporter_metadata() {
953+
Map<String, Object> customExporterMetadata = new HashMap<>();
954+
customExporterMetadata.put("version", "1.0.0");
955+
customExporterMetadata.put("intTest", 1234567890);
956+
customExporterMetadata.put("doubleTest", 12345.67890);
957+
GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder()
958+
.endpoint(this.baseUrl.toString())
959+
.timeout(1000)
960+
.enableCache(true)
961+
.flushIntervalMs(150L)
962+
.exporterMetadata(customExporterMetadata)
963+
.build());
964+
String providerName = this.testName;
965+
OpenFeatureAPI.getInstance().setProviderAndWait(providerName, g);
966+
Client client = OpenFeatureAPI.getInstance().getClient(providerName);
967+
client.getBooleanDetails("bool_targeting_match", false, this.evaluationContext);
968+
client.getBooleanDetails("bool_targeting_match", false, this.evaluationContext);
969+
client.getBooleanDetails("bool_targeting_match", false, this.evaluationContext);
970+
client.getBooleanDetails("bool_targeting_match", false, this.evaluationContext);
971+
client.getBooleanDetails("bool_targeting_match", false, this.evaluationContext);
972+
client.getBooleanDetails("bool_targeting_match", false, this.evaluationContext);
973+
client.getBooleanDetails("bool_targeting_match", false, this.evaluationContext);
974+
client.getBooleanDetails("bool_targeting_match", false, this.evaluationContext);
975+
client.getBooleanDetails("bool_targeting_match", false, this.evaluationContext);
976+
Thread.sleep(150);
977+
978+
Map<String, Object> want = new HashMap<>();
979+
want.put("version", "1.0.0");
980+
want.put("intTest", 1234567890);
981+
want.put("doubleTest", 12345.6789);
982+
want.put("openfeature", true);
983+
want.put("provider", "java");
984+
assertEquals(
985+
want,
986+
this.exporterMetadata,
987+
"we should have the exporter metadata in the last event sent to the data collector");
988+
}
989+
947990
private String readMockResponse(String filename) throws Exception {
948991
URL url = getClass().getClassLoader().getResource("mock_responses/" + filename);
949992
assert url != null;

0 commit comments

Comments
 (0)