Skip to content

Commit 9e8ba3e

Browse files
authored
Eppo Client with shared UFC tests passing (#23)
* tests passing for rule evaluator, flag evaluator, and eppo value * work in progress * shared UFC tests passing * don't check in test data * changes from self-review of PR * apply spotless linter * working on tests * better test logging * use make test for tests
1 parent 4cb7418 commit 9e8ba3e

File tree

62 files changed

+2741
-1095
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+2741
-1095
lines changed

.github/workflows/lint-test-sdk.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,4 @@ jobs:
3232
gpg-passphrase: ${{ secrets.GPG_PASSPHRASE }}
3333

3434
- name: Run tests
35-
run: ./gradlew check --no-daemon
35+
run: make test

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
src/test/resources/shared
12
.gradle
23
build/
34
!gradle/wrapper/gradle-wrapper.jar

Makefile

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Make settings - @see https://tech.davis-hansson.com/p/make/
2+
SHELL := bash
3+
.ONESHELL:
4+
.SHELLFLAGS := -eu -o pipefail -c
5+
.DELETE_ON_ERROR:
6+
MAKEFLAGS += --warn-undefined-variables
7+
MAKEFLAGS += --no-builtin-rules
8+
9+
# Log levels
10+
DEBUG := $(shell printf "\e[2D\e[35m")
11+
INFO := $(shell printf "\e[2D\e[36m🔵 ")
12+
OK := $(shell printf "\e[2D\e[32m🟢 ")
13+
WARN := $(shell printf "\e[2D\e[33m🟡 ")
14+
ERROR := $(shell printf "\e[2D\e[31m🔴 ")
15+
END := $(shell printf "\e[0m")
16+
17+
18+
.PHONY: default
19+
default: help
20+
21+
## help - Print help message.
22+
.PHONY: help
23+
help: Makefile
24+
@echo "usage: make <target>"
25+
@sed -n 's/^##//p' $<
26+
27+
.PHONY: build
28+
build: test-data
29+
./gradlew assemble
30+
31+
## test-data
32+
testDataDir := src/test/resources/shared
33+
tempDir := ${testDataDir}/temp
34+
gitDataDir := ${tempDir}/sdk-test-data
35+
branchName := main
36+
githubRepoLink := https://github.com/Eppo-exp/sdk-test-data.git
37+
.PHONY: test-data
38+
test-data:
39+
rm -rf $(testDataDir)
40+
mkdir -p ${tempDir}
41+
git clone -b ${branchName} --depth 1 --single-branch ${githubRepoLink} ${gitDataDir}
42+
cp -r ${gitDataDir}/ufc ${testDataDir}
43+
rm ${testDataDir}/ufc/bandit-tests/*.dynamic-typing.json
44+
rm -rf ${tempDir}
45+
46+
.PHONY: test
47+
test: test-data build
48+
./gradlew check --no-daemon

build.gradle

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ ext.isReleaseVersion = !version.endsWith("SNAPSHOT")
1111

1212
dependencies {
1313
implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.2'
14+
implementation 'com.github.zafarkhaja:java-semver:0.10.2'
15+
implementation "com.squareup.okhttp3:okhttp:4.12.0"
16+
1417
// For UFC DTOs
1518
implementation 'commons-codec:commons-codec:1.17.0'
1619
implementation 'org.slf4j:slf4j-api:2.0.13'
@@ -19,10 +22,18 @@ dependencies {
1922
testImplementation 'org.skyscreamer:jsonassert:1.5.3'
2023
testImplementation 'commons-io:commons-io:2.11.0'
2124
testImplementation "com.google.truth:truth:1.4.4"
25+
testImplementation 'org.mockito:mockito-core:4.11.0'
2226
}
2327

2428
test {
2529
useJUnitPlatform()
30+
testLogging {
31+
events "started", "passed", "skipped", "failed"
32+
exceptionFormat "full"
33+
showExceptions true
34+
showCauses true
35+
showStackTraces true
36+
}
2637
}
2738

2839
spotless {
@@ -138,6 +149,9 @@ signing {
138149

139150

140151
javadoc {
152+
failOnError = false
153+
options.addStringOption('Xdoclint:none', '-quiet')
154+
options.addBooleanOption('failOnError', false)
141155
if (JavaVersion.current().isJava9Compatible()) {
142156
options.addBooleanOption('html5', true)
143157
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package cloud.eppo;
2+
3+
import cloud.eppo.ufc.dto.FlagConfig;
4+
import java.io.IOException;
5+
import okhttp3.Response;
6+
import org.slf4j.Logger;
7+
import org.slf4j.LoggerFactory;
8+
9+
// TODO: handle bandit stuff
10+
public class ConfigurationRequestor {
11+
private static final Logger log = LoggerFactory.getLogger(ConfigurationRequestor.class);
12+
13+
private final EppoHttpClient client;
14+
private final ConfigurationStore configurationStore;
15+
16+
public ConfigurationRequestor(ConfigurationStore configurationStore, EppoHttpClient client) {
17+
this.configurationStore = configurationStore;
18+
this.client = client;
19+
}
20+
21+
public void load() {
22+
log.debug("Fetching configuration");
23+
Response response = client.get("/api/flag-config/v1/config");
24+
try {
25+
if (!response.isSuccessful()) {
26+
throw new RuntimeException("Failed to fetch configuration");
27+
}
28+
configurationStore.setFlagsFromJsonString(response.body().string());
29+
} catch (IOException e) {
30+
// TODO: better exception handling?
31+
throw new RuntimeException(e);
32+
}
33+
}
34+
35+
// TODO: async loading for android
36+
37+
public FlagConfig getConfiguration(String flagKey) {
38+
return configurationStore.getFlag(flagKey);
39+
}
40+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package cloud.eppo;
2+
3+
import cloud.eppo.ufc.dto.BanditParameters;
4+
import cloud.eppo.ufc.dto.FlagConfig;
5+
import cloud.eppo.ufc.dto.FlagConfigResponse;
6+
import cloud.eppo.ufc.dto.adapters.EppoModule;
7+
import com.fasterxml.jackson.core.JsonProcessingException;
8+
import com.fasterxml.jackson.databind.ObjectMapper;
9+
import java.util.concurrent.ConcurrentHashMap;
10+
import org.slf4j.Logger;
11+
import org.slf4j.LoggerFactory;
12+
13+
public class ConfigurationStore {
14+
private static final Logger log = LoggerFactory.getLogger(ConfigurationStore.class);
15+
private final ObjectMapper mapper = new ObjectMapper().registerModule(EppoModule.eppoModule());
16+
17+
private ConcurrentHashMap<String, FlagConfig> flags;
18+
private ConcurrentHashMap<String, BanditParameters> banditParameters; // TODO: bandit stuff
19+
// TODO: another map of bandit flag variations to handle no action case
20+
static ConfigurationStore instance = null;
21+
22+
public ConfigurationStore() {
23+
// TODO: handle caching
24+
}
25+
26+
public void setFlagsFromJsonString(String jsonString) throws JsonProcessingException {
27+
FlagConfigResponse config = mapper.readValue(jsonString, FlagConfigResponse.class);
28+
if (config == null || config.getFlags() == null) {
29+
log.warn("Flags missing in configuration response");
30+
flags = new ConcurrentHashMap<>();
31+
} else {
32+
// TODO: atomic flags to prevent clobbering like android does
33+
// Record that flags were set from a response so we don't later clobber them with a
34+
// slow cache read
35+
flags = new ConcurrentHashMap<>(config.getFlags());
36+
log.debug("Loaded " + flags.size() + " flags from configuration response");
37+
}
38+
}
39+
40+
public BanditParameters getBanditParameters(String banditKey) {
41+
return this.banditParameters.get(banditKey);
42+
}
43+
44+
public FlagConfig getFlag(String flagKey) {
45+
if (flags == null) {
46+
log.warn("Request for flag " + flagKey + " before flags have been loaded");
47+
return null;
48+
} else if (flags.isEmpty()) {
49+
log.warn("Request for flag " + flagKey + " with empty flags");
50+
}
51+
return flags.get(flagKey);
52+
}
53+
}

src/main/java/cloud/eppo/rac/Constants.java renamed to src/main/java/cloud/eppo/Constants.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package cloud.eppo.rac;
1+
package cloud.eppo;
22

33
/** Constants Class */
44
public class Constants {

0 commit comments

Comments
 (0)