Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
56 changes: 32 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ switcher.isItOn();
```

3. **Strategy validation - Fluent style**
Create chained calls using 'getSwitcher' then 'prepareEntry' then 'isItOn' functions.
Create chained calls to validate the switcher with a more readable and maintainable code.

```java
import static **.MyAppFeatures.*;
Expand All @@ -165,32 +165,22 @@ getSwitcher(FEATURE01)
.isItOn();
```

4. **Strategy validation - all-in-one execution**
All-in-one method is fast and include everything you need to execute a complex call to the API. Stack inputs changing the last parameter to *true* in case you need to add more values to the strategy validator.

```java
switcher.isItOn(FEATURE01, Entry.build(StrategyValidator.NETWORK, "10.0.0.3"), false);
//or simply
switcher.checkNetwork("10.0.0.3").isItOn();
```

5. **Accessing the response history**
Switchers when created store the last execution result from a given switcher key. This can be useful for troubleshooting or internal logging.
4. **Accessing the response history**
Switchers stores the last execution result from a given switcher key/entry.

```java
switcher.getHistoryExecution();
```

6. **Throttling**
Improve the overall performance by using throttle feature to skip API calls in a short time. This feature is ideal for critical or repetitive code executions that requires high performance.
5. **Throttling**
Run Switchers asynchronously when using throttling. It will return the last known value until the throttle time is over.

```java
switcher.throttle(1000).isItOn();
```

## Local settings
You can also set the Switcher library to work locally. It will use a local snapshot file to retrieve the switchers configuration.<br>
This feature is useful for testing purposes or when you need to run your application without internet access.
You can also set the Switcher library to work locally. It will use a local snapshot file to retrieve the switchers configuration.

```java
MyAppFeatures.configure(ContextBuilder.builder()
Expand All @@ -207,21 +197,39 @@ switcher.isItOn();
Forcing Switchers to resolve remotely can help you define exclusive features that cannot be resolved locally.<br>
This feature is ideal if you want to run the SDK in local mode but still want to resolve a specific switcher remotely.

```java
switcher.forceRemote().isItOn();
```

Another option is to use in-memory loaded snapshots to resolve the switchers.<br>
Switcher SDK will schedule a background task to update snapshot in-memory a new version is available.

```java
MyAppFeatures.configure(ContextBuilder.builder()
.url("https://switcher-api.com")
.apiKey("API_KEY")
.url("https://api.switcherapi.com")
.apiKey("[API-KEY]")
.domain("Playground")
.component("switcher-playground")
.local(true)
.snapshotLocation("/src/resources"));
.local(true)
.snapshotAutoLoad(true)
.snapshotAutoUpdateInterval("5s") // You can choose to configure here or using `scheduleSnapshotAutoUpdate`
.component("switcher-playground"));

MyAppFeatures.initializeClient();

Switcher switcher = MyAppFeatures.getSwitcher(FEATURE01);
switcher.forceRemote().isItOn();
MyAppFeatures.scheduleSnapshotAutoUpdate("5s", new SnapshotCallback() {
@Override
public void onSnapshotUpdate(long version) {
logger.info("Snapshot updated: {}", version);
}

@Override
public void onSnapshotUpdateError(Exception e) {
logger.error("Failed to update snapshot: {}", e.getMessage());
}
});
```



## Real-time snapshot updater
Let the Switcher Client manage your application local snapshot.<br>
These features allow you to configure the SDK to automatically update the snapshot in the background.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,10 +170,11 @@ private static void loadSwitchers() {
*
* @param intervalValue to be used for the update (e.g. 5s, 1m, 1h, 1d)
* @param callback to be invoked when the snapshot is updated or when an error occurs
* @return true if the task was scheduled successfully
*/
public static void scheduleSnapshotAutoUpdate(String intervalValue, SnapshotCallback callback) {
if (StringUtils.isBlank(intervalValue)) {
return;
public static boolean scheduleSnapshotAutoUpdate(String intervalValue, SnapshotCallback callback) {
if (StringUtils.isBlank(intervalValue) || scheduledExecutorService != null) {
return false;
}

final long interval = SwitcherUtils.getMillis(intervalValue);
Expand All @@ -191,16 +192,18 @@ public static void scheduleSnapshotAutoUpdate(String intervalValue, SnapshotCall

initExecutorService();
scheduledExecutorService.scheduleAtFixedRate(runnableSnapshotValidate, 0, interval, TimeUnit.MILLISECONDS);
return true;
}

/**
* Schedule a task to update the snapshot automatically.<br>
* The task will be executed in a single thread executor service.
*
* @param intervalValue to be used for the update (e.g. 5s, 1m, 1h, 1d)
* @return true if the task was scheduled successfully
*/
public static void scheduleSnapshotAutoUpdate(String intervalValue) {
scheduleSnapshotAutoUpdate(intervalValue, null);
public static boolean scheduleSnapshotAutoUpdate(String intervalValue) {
return scheduleSnapshotAutoUpdate(intervalValue, null);
}

/**
Expand Down Expand Up @@ -335,6 +338,7 @@ public static void configure(ContextBuilder builder) {
public static void terminateSnapshotAutoUpdateWorker() {
if (scheduledExecutorService != null) {
scheduledExecutorService.shutdownNow();
scheduledExecutorService = null;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

public enum WorkerName {

REGEX_VALIDATOR_WORKER("switcherapi-regex-validator"),
SNAPSHOT_WATCH_WORKER("switcherapi-snapshot-watcher"),
SNAPSHOT_UPDATE_WORKER("switcherapi-snapshot-update");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,8 @@ public CriteriaResponse executeCriteria(final Switcher switcher, final Domain do
*/
private CriteriaResponse processOperation(final Strategy[] configStrategies, final List<Entry> input,
final Switcher switcher) {
SwitcherUtils.debug(logger, "configStrategies: {}", () -> Arrays.toString(configStrategies));
SwitcherUtils.debug(logger, "input: {}", () -> Arrays.toString(input != null ? input.toArray() : ArrayUtils.EMPTY_STRING_ARRAY));
SwitcherUtils.debugSupplier(logger, "configStrategies: {}", Arrays.toString(configStrategies));
SwitcherUtils.debugSupplier(logger, "input: {}", Arrays.toString(input != null ? input.toArray() : ArrayUtils.EMPTY_STRING_ARRAY));

boolean result;
for (final Strategy strategy : configStrategies) {
Expand Down Expand Up @@ -153,8 +153,9 @@ private CriteriaResponse strategyFailed(Switcher switcher, Strategy strategy, St
}

private Entry tryGetSwitcherInput(final List<Entry> input, Strategy strategy) {
if (input == null)
if (input == null) {
return null;
}

return input.stream()
.filter(i -> i.getStrategy().equals(strategy.getStrategy()))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,14 @@ public void run() {
WatchEvent<Path> ev = (WatchEvent<Path>) event;
Path filename = ev.context();

if (executorInstance != null)
executorInstance.notifyChange(filename.toString(), handler);
if (executorInstance != null) {
executorInstance.notifyChange(filename.toString(), handler);
}
}

boolean valid = key.reset();

if (!valid)
break;

if (!key.reset()) {
break;
}
}
} catch (IOException | InterruptedException | ClosedWatchServiceException e) {
Thread.currentThread().interrupt();
Expand All @@ -74,8 +74,9 @@ public void run() {

public void terminate() {
try {
if (watcher != null)
if (watcher != null) {
watcher.close();
}
} catch (IOException e) {
logger.error(e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

Expand Down Expand Up @@ -143,8 +142,9 @@ public static Set<String> payloadReader(String jsonStr, String prevKey) {
* @param handler to notify snapshot change events
*/
public static void watchSnapshot(final SwitcherExecutor executorInstance, SnapshotEventHandler handler) {
if (watcher == null)
if (watcher == null) {
watcher = new SnapshotWatcher(executorInstance, handler);
}

initExecutorService();
executorService.submit(watcher);
Expand Down Expand Up @@ -211,14 +211,14 @@ public static void debug(Logger logger, String message, Object... args) {
* @param message to be logged
* @param paramSuppliers parameters to be replaced in the message
*/
public static void debug(Logger logger, String message, Supplier<?> paramSuppliers) {
public static void debugSupplier(Logger logger, String message, Object paramSuppliers) {
if (logger.isDebugEnabled()) {
logger.debug(message, paramSuppliers);
logger.debug(message, () -> paramSuppliers);
}
}

/**
* Resolve environment variable 'value'and extract its value from either
* Resolve environment variable 'value' and extract its value from either
* System environment or default argument.
*
* @param value assigned from the properties file
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.junit.jupiter.params.provider.MethodSource;

import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Stream;

import static org.junit.jupiter.api.Assertions.*;
Expand Down Expand Up @@ -75,6 +76,16 @@ void localShouldNotReturn_keyNotFound() {
Switcher switcher = Switchers.getSwitcher(Switchers.NOT_FOUND_KEY);
assertThrows(SwitcherKeyNotFoundException.class, switcher::isItOn);
}

@Test
void localShouldReturnFalse_nullEntryForStrategy() {
Switcher switcher = Switchers.getSwitcher(Switchers.USECASE31);

List<Entry> entry = null;
assertFalse(switcher
.prepareEntry(entry)
.isItOn());
}

static Stream<Arguments> dateTestArguments() {
return Stream.of(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@
import java.nio.file.Paths;
import java.util.Date;

import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.*;

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class SwitcherSnapshotAutoUpdateTest {
Expand Down Expand Up @@ -261,4 +260,25 @@ void shouldNotKillThread_whenAPI_wentLocal() {
assertEquals(2, Switchers.getSnapshotVersion());
}

@Test
@Order(6)
void shouldPreventSnapshotAutoUpdateToStart_whenAlreadySetup() {
//given
givenResponse(generateMockAuth()); //auth
givenResponse(generateSnapshotResponse("default.json")); //graphql

//that
Switchers.configure(ContextBuilder.builder()
.url(String.format("http://localhost:%s", mockBackEnd.getPort()))
.snapshotLocation(null)
.environment("generated_mock_default_6")
.local(true)
.snapshotAutoLoad(true)
.snapshotAutoUpdateInterval("1s"));

Switchers.initializeClient();
boolean started = Switchers.scheduleSnapshotAutoUpdate("1m");
assertFalse(started);
}

}