Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: flagd in-process provider #412

Merged
merged 32 commits into from
Sep 7, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
6b398d8
refactor packaging and initializing helpers
Kavindu-Dodan Aug 30, 2023
43b8d49
simple flag parsing
Kavindu-Dodan Aug 31, 2023
b834106
minimal end to end solution
Kavindu-Dodan Aug 31, 2023
b7917de
first evaluation state
Kavindu-Dodan Aug 31, 2023
c8a3960
grpc handling and json logic
Kavindu-Dodan Sep 1, 2023
37b3df7
resolving logic cleanup
Kavindu-Dodan Sep 1, 2023
860c6ca
refactor storage layer
Kavindu-Dodan Sep 1, 2023
fc5f344
connector refactored
Kavindu-Dodan Sep 1, 2023
22246fe
doc comments and organize
Kavindu-Dodan Sep 4, 2023
e46c62c
event handling wiring
Kavindu-Dodan Sep 4, 2023
bd53b6b
resolving tests
Kavindu-Dodan Sep 4, 2023
2bcde9b
lint fixes
Kavindu-Dodan Sep 4, 2023
bda533f
review changes and fix lint
Kavindu-Dodan Sep 5, 2023
009ae86
schema validation, tests and lint
Kavindu-Dodan Sep 5, 2023
aa61605
spotbug fixes
Kavindu-Dodan Sep 5, 2023
a895402
fix submodule status
Kavindu-Dodan Sep 5, 2023
2b88c08
disable e2e testst till framework is ready
Kavindu-Dodan Sep 5, 2023
d7e0c54
fix exclusions
Kavindu-Dodan Sep 5, 2023
d3888ad
Update providers/flagd/src/main/java/dev/openfeature/contrib/provider…
Kavindu-Dodan Sep 6, 2023
da9e252
fix test harness and review changes
Kavindu-Dodan Sep 6, 2023
d74b3f9
fix object resolving
Kavindu-Dodan Sep 6, 2023
cc8bdc6
testing storage connector contract
Kavindu-Dodan Sep 6, 2023
2e4677c
tests for grpc connector
Kavindu-Dodan Sep 6, 2023
81fb334
documentation
Kavindu-Dodan Sep 6, 2023
96240a7
reset retry
Kavindu-Dodan Sep 6, 2023
63d9d0e
Update providers/flagd/src/test/java/dev/openfeature/contrib/provider…
Kavindu-Dodan Sep 7, 2023
e90a954
Update providers/flagd/src/test/java/dev/openfeature/contrib/provider…
Kavindu-Dodan Sep 7, 2023
a0915b0
Update providers/flagd/src/test/java/dev/openfeature/contrib/provider…
Kavindu-Dodan Sep 7, 2023
7ce88e6
Update providers/flagd/README.md
Kavindu-Dodan Sep 7, 2023
a24dfab
Update providers/flagd/src/test/java/dev/openfeature/contrib/provider…
Kavindu-Dodan Sep 7, 2023
c8ba6ee
add tests for int and double
Kavindu-Dodan Sep 7, 2023
729c1e6
make wait configurable
Kavindu-Dodan Sep 7, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
testing storage connector contract
Signed-off-by: Kavindu Dodanduwa <[email protected]>
  • Loading branch information
Kavindu-Dodan committed Sep 7, 2023
commit cc8bdc642f73748f5c0f6285329f157845fe6430
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public void init() throws Exception {
case STALE:
// todo set stale state
default:
log.log(Level.INFO, String.format("Storage emitted unknown status: %s", storageState));
log.log(Level.INFO, String.format("Storage emitted unhandled status: %s", storageState));
}
}
} catch (InterruptedException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import dev.openfeature.contrib.providers.flagd.resolver.process.storage.connector.StreamPayload;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import lombok.extern.java.Log;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
Expand Down Expand Up @@ -119,6 +120,8 @@ private void streamerListener(final Connector connector) throws InterruptedExcep
log.log(Level.INFO, String.format("Payload with unknown type: %s", take.getType()));
}
}

log.log(Level.INFO, "Shutting down store stream listener");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package dev.openfeature.contrib.providers.flagd.resolver.process;

import dev.openfeature.contrib.providers.flagd.resolver.process.model.FlagParser;

import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;

public class TestUtils {
public static final String VALID_SIMPLE = "flagConfigurations/valid-simple.json";
public static final String VALID_LONG = "flagConfigurations/valid-long.json";
public static final String INVALID_FLAG = "flagConfigurations/invalid-flag.json";
public static final String INVALID_CFG = "flagConfigurations/invalid-configuration.json";


public static String getFlagsFromResource(final String file) throws IOException {
final URL url = FlagParser.class.getClassLoader().getResource(file);
if (url == null) {
throw new IllegalStateException(String.format("Resource %s not found", file));
} else {
return new String(Files.readAllBytes(Paths.get(url.getPath())));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,18 @@
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Map;

import static org.junit.jupiter.api.Assertions.*;
import static dev.openfeature.contrib.providers.flagd.resolver.process.TestUtils.INVALID_CFG;
import static dev.openfeature.contrib.providers.flagd.resolver.process.TestUtils.INVALID_FLAG;
import static dev.openfeature.contrib.providers.flagd.resolver.process.TestUtils.VALID_LONG;
import static dev.openfeature.contrib.providers.flagd.resolver.process.TestUtils.VALID_SIMPLE;
import static dev.openfeature.contrib.providers.flagd.resolver.process.TestUtils.getFlagsFromResource;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;

class FlagParserTest {

private static final String VALID_SIMPLE = "flagConfigurations/valid-simple.json";
private static final String VALID_LONG = "flagConfigurations/valid-long.json";
private static final String INVALID_FLAG = "flagConfigurations/invalid-flag.json";
private static final String INVALID_CFG = "flagConfigurations/invalid-configuration.json";

@Test
public void validJsonConfigurationParsing() throws IOException {
Map<String, FeatureFlag> flagMap = FlagParser.parseString(getFlagsFromResource(VALID_SIMPLE));
Expand Down Expand Up @@ -65,13 +63,4 @@ public void invalidConfigurationsThrowsError() {
FlagParser.parseString(getFlagsFromResource(INVALID_CFG));
});
}

private static String getFlagsFromResource(final String file) throws IOException {
final URL url = FlagParser.class.getClassLoader().getResource(file);
if (url == null) {
throw new IllegalStateException(String.format("Resource %s not found", file));
} else {
return new String(Files.readAllBytes(Paths.get(url.getPath())));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package dev.openfeature.contrib.providers.flagd.resolver.process.storage;

import dev.openfeature.contrib.providers.flagd.resolver.process.storage.connector.StreamPayload;
import dev.openfeature.contrib.providers.flagd.resolver.process.storage.connector.StreamPayloadType;
import org.junit.jupiter.api.Test;

import java.time.Duration;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import static dev.openfeature.contrib.providers.flagd.resolver.process.TestUtils.INVALID_FLAG;
import static dev.openfeature.contrib.providers.flagd.resolver.process.TestUtils.VALID_LONG;
import static dev.openfeature.contrib.providers.flagd.resolver.process.TestUtils.VALID_SIMPLE;
import static dev.openfeature.contrib.providers.flagd.resolver.process.TestUtils.getFlagsFromResource;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTimeout;

class FlagStoreTest {

@Test
public void connectorHandling() {
final BlockingQueue<StreamPayload> payload = new LinkedBlockingQueue<>();
FlagStore store = new FlagStore(new MockConnector(payload));

store.init();
final BlockingQueue<StorageState> states = store.getStateQueue();

// OK for simple flag
assertTimeout(Duration.ofMillis(200), ()-> {
payload.offer(new StreamPayload(StreamPayloadType.DATA, getFlagsFromResource(VALID_SIMPLE)));
});

assertTimeout(Duration.ofMillis(200), ()-> {
assertEquals(StorageState.OK, states.take());
});

// STALE for invalid flag
assertTimeout(Duration.ofMillis(200), ()-> {
payload.offer(new StreamPayload(StreamPayloadType.DATA, getFlagsFromResource(INVALID_FLAG)));
});

assertTimeout(Duration.ofMillis(200), ()-> {
assertEquals(StorageState.STALE, states.take());
});

// OK again for next payload
assertTimeout(Duration.ofMillis(200), ()-> {
payload.offer(new StreamPayload(StreamPayloadType.DATA, getFlagsFromResource(VALID_LONG)));
});

assertTimeout(Duration.ofMillis(200), ()-> {
assertEquals(StorageState.OK, states.take());
});

// ERROR is propagated correctly
assertTimeout(Duration.ofMillis(200), ()-> {
payload.offer(new StreamPayload(StreamPayloadType.ERROR, null));
});

assertTimeout(Duration.ofMillis(200), ()-> {
assertEquals(StorageState.ERROR, states.take());
});

// Shutdown handling
store.shutdown();

assertTimeout(Duration.ofMillis(200), ()-> {
assertEquals(StorageState.ERROR, states.take());
});
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package dev.openfeature.contrib.providers.flagd.resolver.process.storage;

import dev.openfeature.contrib.providers.flagd.resolver.process.storage.connector.Connector;
import dev.openfeature.contrib.providers.flagd.resolver.process.storage.connector.StreamPayload;
import dev.openfeature.contrib.providers.flagd.resolver.process.storage.connector.StreamPayloadType;
import lombok.extern.java.Log;

import java.util.concurrent.BlockingQueue;
import java.util.logging.Level;

@Log
public class MockConnector implements Connector {

private BlockingQueue<StreamPayload> mockQueue;

MockConnector(final BlockingQueue<StreamPayload> mockQueue){
this.mockQueue = mockQueue;
}

public void init() {
// no-op
}

public BlockingQueue<StreamPayload> getStream() {
return mockQueue;
}

public void shutdown() {
// Emit error mocking closed connection scenario
if(!mockQueue.offer(new StreamPayload(StreamPayloadType.ERROR, "shutdown invoked"))){
log.log(Level.WARNING, "Failed to offer shutdown status");
}
}
}