From d5cc07ab92fe6504483b576f61a9e67f338425ad Mon Sep 17 00:00:00 2001 From: Roger Floriano <31597636+petruki@users.noreply.github.com> Date: Sat, 30 Nov 2024 15:34:12 -0800 Subject: [PATCH 1/2] Fixes native artifacts, improved Executor API specification (#334) * Fixes native artifacts, improved Executor API specification --- README.md | 6 +- .../switcherapi/client/SwitcherContext.java | 2 +- .../client/SwitcherContextBase.java | 18 +- .../switcherapi/client/SwitcherExecutor.java | 122 +++----------- .../client/SwitcherExecutorImpl.java | 65 +++++++ .../client/model/SwitcherRequest.java | 13 +- .../remote/dto/CriteriaInputRequest.java | 25 +++ .../client/remote/dto/CriteriaRequest.java | 17 +- .../service/local/SwitcherLocalService.java | 4 +- .../service/remote/SwitcherRemoteService.java | 10 +- .../client/test/SwitcherBypass.java | 65 +++++++ .../client/test/SwitcherTestExtension.java | 9 +- .../switcher-client/reflect-config.json | 6 +- .../switcher-client/resource-config.json | 0 .../client/SwitcherBypassTest.java | 33 ++-- .../client/remote/ClientRemoteTest.java | 7 +- .../nativeimage/NativeReflectConfigTest.java | 59 +++++++ .../nativeimage/NativeResourceConfigTest.java | 159 ++++++++++++++++++ 18 files changed, 453 insertions(+), 167 deletions(-) create mode 100644 src/main/java/com/github/switcherapi/client/SwitcherExecutorImpl.java create mode 100644 src/main/java/com/github/switcherapi/client/remote/dto/CriteriaInputRequest.java create mode 100644 src/main/java/com/github/switcherapi/client/test/SwitcherBypass.java rename src/main/resources/META-INF/native-image/{com.github.switcherapi.client => com.github.switcherapi}/switcher-client/reflect-config.json (95%) rename src/main/resources/META-INF/native-image/{com.github.switcherapi.client => com.github.switcherapi}/switcher-client/resource-config.json (100%) create mode 100644 src/test/java/metainf/nativeimage/NativeReflectConfigTest.java create mode 100644 src/test/java/metainf/nativeimage/NativeResourceConfigTest.java diff --git a/README.md b/README.md index a2e6d62..db47d13 100644 --- a/README.md +++ b/README.md @@ -285,10 +285,10 @@ Write automated tests using this built-in test annotation to guide your test sce ```java Switcher switcher = MyAppFeatures.getSwitcher(FEATURE01); -SwitcherExecutor.assume(FEATURE01, false); +SwitcherBypass.assume(FEATURE01, false); switcher.isItOn(); // 'false' -SwitcherExecutor.forget(FEATURE01); +SwitcherBypass.forget(FEATURE01); switcher.isItOn(); // Now, it's going to return the result retrieved from the API or the Snapshot file ``` @@ -297,7 +297,7 @@ For more complex scenarios where you need to test features based on specific inp ```java Switcher switcher = MyAppFeatures.getSwitcher(FEATURE01).checkValue("My value").build(); -SwitcherExecutor.assume(FEATURE01, true).when(StrategyValidator.VALUE, "My value"); +SwitcherBypass.assume(FEATURE01, true).when(StrategyValidator.VALUE, "My value"); switcher.isItOn(); // 'true' ``` diff --git a/src/main/java/com/github/switcherapi/client/SwitcherContext.java b/src/main/java/com/github/switcherapi/client/SwitcherContext.java index 36502d2..09c0f62 100644 --- a/src/main/java/com/github/switcherapi/client/SwitcherContext.java +++ b/src/main/java/com/github/switcherapi/client/SwitcherContext.java @@ -92,7 +92,7 @@ public static void checkSwitchers() { * @return snapshot version */ public static long getSnapshotVersion() { - return SwitcherContextBase.instance.getSnapshotVersion(); + return SwitcherContextBase.switcherExecutor.getSnapshotVersion(); } /** diff --git a/src/main/java/com/github/switcherapi/client/SwitcherContextBase.java b/src/main/java/com/github/switcherapi/client/SwitcherContextBase.java index 34adfcc..267d559 100644 --- a/src/main/java/com/github/switcherapi/client/SwitcherContextBase.java +++ b/src/main/java/com/github/switcherapi/client/SwitcherContextBase.java @@ -86,7 +86,7 @@ public abstract class SwitcherContextBase extends SwitcherConfig { protected static SwitcherProperties switcherProperties; protected static Set switcherKeys; protected static Map switchers; - protected static SwitcherExecutor instance; + protected static SwitcherExecutor switcherExecutor; private static ScheduledExecutorService scheduledExecutorService; private static ExecutorService watcherExecutorService; private static SnapshotWatcher watcherSnapshot; @@ -180,7 +180,7 @@ public static void loadProperties(String contextFilename) { public static void initializeClient() { validateContext(); registerSwitcherKeys(); - instance = buildInstance(); + switcherExecutor = buildInstance(); loadSwitchers(); scheduleSnapshotAutoUpdate(contextStr(ContextKey.SNAPSHOT_AUTO_UPDATE_INTERVAL)); @@ -258,7 +258,7 @@ private static void loadSwitchers() { switchers.clear(); for (String key : switcherKeys) { - switchers.put(key, new SwitcherRequest(key, instance)); + switchers.put(key, new SwitcherRequest(key, switcherExecutor, switcherProperties)); } } @@ -280,7 +280,7 @@ public static ScheduledFuture scheduleSnapshotAutoUpdate(String intervalValue final Runnable runnableSnapshotValidate = () -> { try { if (validateSnapshot()) { - callbackFinal.onSnapshotUpdate(instance.getSnapshotVersion()); + callbackFinal.onSnapshotUpdate(switcherExecutor.getSnapshotVersion()); } } catch (Exception e) { logger.error(e.getMessage(), e); @@ -386,11 +386,11 @@ public static SwitcherRequest getSwitcher(String key) { * @return true if snapshot was updated */ public static boolean validateSnapshot() { - if (contextBol(ContextKey.SNAPSHOT_SKIP_VALIDATION) || instance.checkSnapshotVersion()) { + if (contextBol(ContextKey.SNAPSHOT_SKIP_VALIDATION) || switcherExecutor.checkSnapshotVersion()) { return false; } - instance.updateSnapshot(); + switcherExecutor.updateSnapshot(); return true; } @@ -416,12 +416,12 @@ public static void watchSnapshot() { * @throws SwitcherException if using remote service */ public static void watchSnapshot(SnapshotEventHandler handler) { - if (!(instance instanceof SwitcherLocalService)) { + if (!(switcherExecutor instanceof SwitcherLocalService)) { throw new SwitcherException("Cannot watch snapshot when using remote", new UnsupportedOperationException()); } if (Objects.isNull(watcherSnapshot)) { - watcherSnapshot = new SnapshotWatcher((SwitcherLocalService) instance, handler, + watcherSnapshot = new SnapshotWatcher((SwitcherLocalService) switcherExecutor, handler, contextStr(ContextKey.SNAPSHOT_LOCATION)); } @@ -448,7 +448,7 @@ public static void stopWatchingSnapshot() { * @throws SwitchersValidationException when one or more Switcher Key is not found */ public static void checkSwitchers() { - instance.checkSwitchers(switcherKeys); + switcherExecutor.checkSwitchers(switcherKeys); } /** diff --git a/src/main/java/com/github/switcherapi/client/SwitcherExecutor.java b/src/main/java/com/github/switcherapi/client/SwitcherExecutor.java index 7ff6884..ed93ac8 100644 --- a/src/main/java/com/github/switcherapi/client/SwitcherExecutor.java +++ b/src/main/java/com/github/switcherapi/client/SwitcherExecutor.java @@ -1,157 +1,73 @@ package com.github.switcherapi.client; -import com.github.switcherapi.client.exception.SwitcherRemoteException; -import com.github.switcherapi.client.exception.SwitcherSnapshotWriteException; -import com.github.switcherapi.client.model.ContextKey; import com.github.switcherapi.client.model.SwitcherRequest; -import com.github.switcherapi.client.model.criteria.Domain; -import com.github.switcherapi.client.model.criteria.Snapshot; import com.github.switcherapi.client.model.SwitcherResult; -import com.github.switcherapi.client.service.remote.ClientRemote; -import com.github.switcherapi.client.utils.SnapshotLoader; -import com.github.switcherapi.client.utils.SwitcherUtils; -import com.google.gson.Gson; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import com.github.switcherapi.client.model.criteria.Domain; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; import java.util.Set; /** - * The Executor provides an API to handle Remote and Local functionalities that + * An Executor provides the API to handle Remote and Local operations that * should be available for both Services implementations. * * @author Roger Floriano (petruki) * @since 2019-12-24 */ -public abstract class SwitcherExecutor { - - private static final Logger logger = LoggerFactory.getLogger(SwitcherExecutor.class); - - private static final Map bypass = new HashMap<>(); - - protected final SwitcherProperties switcherProperties; - - protected Domain domain; +public interface SwitcherExecutor { - protected SwitcherExecutor(final SwitcherProperties switcherProperties) { - this.switcherProperties = switcherProperties; - } - /** * Execute criteria based on the Switcher configuration * * @param switcher to be evaluated * @return Criteria response containing the evaluation details */ - public abstract SwitcherResult executeCriteria(final SwitcherRequest switcher); + SwitcherResult executeCriteria(final SwitcherRequest switcher); /** * Check the snapshot versions against the Remote configuration. * * @return True if snapshot is up-to-date */ - public abstract boolean checkSnapshotVersion(); + boolean checkSnapshotVersion(); /** * Retrieve updated snapshot from the remote API */ - public abstract void updateSnapshot(); + void updateSnapshot(); /** * Check set of Switchers if they are properly configured. * * @param switchers To be validated */ - public abstract void checkSwitchers(final Set switchers); + void checkSwitchers(final Set switchers); /** * Retrieve local snapshot version * * @return snapshot version */ - public abstract long getSnapshotVersion(); - - protected boolean checkSnapshotVersion(ClientRemote clientRemote, final Domain domain) { - final String environment = switcherProperties.getValue(ContextKey.ENVIRONMENT); - SwitcherUtils.debug(logger, "verifying snapshot version - environment: {}", environment); - - return clientRemote.checkSnapshotVersion(domain.getVersion()); - } - - protected Domain initializeSnapshotFromAPI(ClientRemote clientRemote) - throws SwitcherRemoteException, SwitcherSnapshotWriteException { - final String environment = switcherProperties.getValue(ContextKey.ENVIRONMENT); - SwitcherUtils.debug(logger, "initializing snapshot from API - environment: {}", environment); - - final Snapshot snapshot = clientRemote.resolveSnapshot(); - final String snapshotLocation = switcherProperties.getValue(ContextKey.SNAPSHOT_LOCATION); + long getSnapshotVersion(); - if (Objects.nonNull(snapshotLocation)) { - SnapshotLoader.saveSnapshot(snapshot, snapshotLocation, environment); - } - - return snapshot.getDomain(); - } - /** - * It manipulates the result of a given Switcher key. - * - * @param key name of the key that you want to change the result - * @param expectedResult that will be returned when performing isItOn - * @return SwitcherResult with the manipulated result + * Retrieve the Domain object from the current snapshot + * + * @return Domain object */ - public static SwitcherResult assume(final String key, boolean expectedResult) { - return assume(key, expectedResult, null); - } + Domain getDomain(); /** - * It manipulates the result of a given Switcher key. + * Set the Domain object from the current snapshot * - * @param key name of the key that you want to change the result - * @param metadata additional information about the assumption (JSON) - * @param expectedResult that will be returned when performing isItOn - * @return SwitcherResult with the manipulated result + * @param domain to be set */ - public static SwitcherResult assume(final String key, boolean expectedResult, String metadata) { - SwitcherResult switcherResult = new SwitcherResult(); - switcherResult.setResult(expectedResult); - switcherResult.setReason("Switcher bypassed"); - - if (StringUtils.isNotBlank(metadata)) { - Gson gson = new Gson(); - switcherResult.setMetadata(gson.fromJson(metadata, Object.class)); - } + void setDomain(Domain domain); - bypass.put(key, switcherResult); - return switcherResult; - } - /** - * It will clean up any result manipulation added before by invoking {@link SwitcherExecutor#assume(String, boolean)} - * - * @param key name of the key you want to remove + * Retrieve the Switcher properties configured for the executor + * + * @return SwitcherProperties object */ - public static void forget(final String key) { - bypass.remove(key); - } - - public static Map getBypass() { - return bypass; - } - - public SwitcherProperties getSwitcherProperties() { - return switcherProperties; - } - - public Domain getDomain() { - return domain; - } + SwitcherProperties getSwitcherProperties(); - public void setDomain(Domain domain) { - this.domain = domain; - } } diff --git a/src/main/java/com/github/switcherapi/client/SwitcherExecutorImpl.java b/src/main/java/com/github/switcherapi/client/SwitcherExecutorImpl.java new file mode 100644 index 0000000..abc9f8f --- /dev/null +++ b/src/main/java/com/github/switcherapi/client/SwitcherExecutorImpl.java @@ -0,0 +1,65 @@ +package com.github.switcherapi.client; + +import com.github.switcherapi.client.exception.SwitcherRemoteException; +import com.github.switcherapi.client.exception.SwitcherSnapshotWriteException; +import com.github.switcherapi.client.model.ContextKey; +import com.github.switcherapi.client.model.criteria.Domain; +import com.github.switcherapi.client.model.criteria.Snapshot; +import com.github.switcherapi.client.service.remote.ClientRemote; +import com.github.switcherapi.client.utils.SnapshotLoader; +import com.github.switcherapi.client.utils.SwitcherUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Objects; + +public abstract class SwitcherExecutorImpl implements SwitcherExecutor { + + private static final Logger logger = LoggerFactory.getLogger(SwitcherExecutorImpl.class); + + protected final SwitcherProperties switcherProperties; + + protected Domain domain; + + protected SwitcherExecutorImpl(final SwitcherProperties switcherProperties) { + this.switcherProperties = switcherProperties; + } + + protected boolean checkSnapshotVersion(ClientRemote clientRemote, final Domain domain) { + final String environment = switcherProperties.getValue(ContextKey.ENVIRONMENT); + SwitcherUtils.debug(logger, "verifying snapshot version - environment: {}", environment); + + return clientRemote.checkSnapshotVersion(domain.getVersion()); + } + + protected Domain initializeSnapshotFromAPI(ClientRemote clientRemote) + throws SwitcherRemoteException, SwitcherSnapshotWriteException { + final String environment = switcherProperties.getValue(ContextKey.ENVIRONMENT); + SwitcherUtils.debug(logger, "initializing snapshot from API - environment: {}", environment); + + final Snapshot snapshot = clientRemote.resolveSnapshot(); + final String snapshotLocation = switcherProperties.getValue(ContextKey.SNAPSHOT_LOCATION); + + if (Objects.nonNull(snapshotLocation)) { + SnapshotLoader.saveSnapshot(snapshot, snapshotLocation, environment); + } + + return snapshot.getDomain(); + } + + @Override + public SwitcherProperties getSwitcherProperties() { + return switcherProperties; + } + + @Override + public Domain getDomain() { + return domain; + } + + @Override + public void setDomain(Domain domain) { + this.domain = domain; + } + +} diff --git a/src/main/java/com/github/switcherapi/client/model/SwitcherRequest.java b/src/main/java/com/github/switcherapi/client/model/SwitcherRequest.java index a9f8978..c3bfbea 100644 --- a/src/main/java/com/github/switcherapi/client/model/SwitcherRequest.java +++ b/src/main/java/com/github/switcherapi/client/model/SwitcherRequest.java @@ -2,7 +2,9 @@ import com.github.switcherapi.client.SwitcherContext; import com.github.switcherapi.client.SwitcherExecutor; +import com.github.switcherapi.client.SwitcherProperties; import com.github.switcherapi.client.exception.SwitcherException; +import com.github.switcherapi.client.test.SwitcherBypass; import java.util.*; @@ -37,9 +39,12 @@ public final class SwitcherRequest extends SwitcherBuilder { * * @param switcherKey name of the key created * @param switcherExecutor client context in which the switcher will be executed (local/remote) + * @param switcherProperties properties to be used with executor operations */ - public SwitcherRequest(final String switcherKey, final SwitcherExecutor switcherExecutor) { - super(switcherExecutor.getSwitcherProperties()); + public SwitcherRequest(final String switcherKey, + final SwitcherExecutor switcherExecutor, + final SwitcherProperties switcherProperties) { + super(switcherProperties); this.switcherExecutor = switcherExecutor; this.switcherKey = switcherKey; this.historyExecution = new HashSet<>(); @@ -83,8 +88,8 @@ public boolean isItOn() throws SwitcherException { @Override public SwitcherResult submit() throws SwitcherException { - if (SwitcherExecutor.getBypass().containsKey(switcherKey)) { - return SwitcherExecutor.getBypass().get(switcherKey).buildFromSwitcher(switcherKey, entry); + if (SwitcherBypass.getBypass().containsKey(switcherKey)) { + return SwitcherBypass.getBypass().get(switcherKey).buildFromSwitcher(switcherKey, entry); } if (canUseAsync()) { diff --git a/src/main/java/com/github/switcherapi/client/remote/dto/CriteriaInputRequest.java b/src/main/java/com/github/switcherapi/client/remote/dto/CriteriaInputRequest.java new file mode 100644 index 0000000..f3a4997 --- /dev/null +++ b/src/main/java/com/github/switcherapi/client/remote/dto/CriteriaInputRequest.java @@ -0,0 +1,25 @@ +package com.github.switcherapi.client.remote.dto; + +import com.github.switcherapi.client.model.Entry; + +import java.util.Arrays; + +public class CriteriaInputRequest { + + private final Entry[] entry; + + public CriteriaInputRequest(final Entry[] entry) { + this.entry = entry; + } + + public Entry[] getEntry() { + return this.entry; + } + + @Override + public String toString() { + return "CriteriaInputRequest{" + + "entry=" + Arrays.toString(entry) + + '}'; + } +} diff --git a/src/main/java/com/github/switcherapi/client/remote/dto/CriteriaRequest.java b/src/main/java/com/github/switcherapi/client/remote/dto/CriteriaRequest.java index 8a06239..061197c 100644 --- a/src/main/java/com/github/switcherapi/client/remote/dto/CriteriaRequest.java +++ b/src/main/java/com/github/switcherapi/client/remote/dto/CriteriaRequest.java @@ -43,24 +43,11 @@ public void setBypassMetric(boolean bypassMetric) { * * @return json input request */ - public CriteriaRequest.GsonInputRequest getInputRequest() { - return new CriteriaRequest.GsonInputRequest( + public CriteriaInputRequest getInputRequest() { + return new CriteriaInputRequest( Optional.ofNullable(this.entry) .orElseGet(ArrayList::new) .toArray(new Entry[0])); } - public static class GsonInputRequest { - - private final Entry[] entry; - - public GsonInputRequest(final Entry[] entry) { - this.entry = entry; - } - - public Entry[] getEntry() { - return this.entry; - } - } - } diff --git a/src/main/java/com/github/switcherapi/client/service/local/SwitcherLocalService.java b/src/main/java/com/github/switcherapi/client/service/local/SwitcherLocalService.java index 8a4207a..fd9d22a 100644 --- a/src/main/java/com/github/switcherapi/client/service/local/SwitcherLocalService.java +++ b/src/main/java/com/github/switcherapi/client/service/local/SwitcherLocalService.java @@ -1,6 +1,6 @@ package com.github.switcherapi.client.service.local; -import com.github.switcherapi.client.SwitcherExecutor; +import com.github.switcherapi.client.SwitcherExecutorImpl; import com.github.switcherapi.client.SwitcherProperties; import com.github.switcherapi.client.exception.SwitcherException; import com.github.switcherapi.client.exception.SwitcherKeyNotFoundException; @@ -27,7 +27,7 @@ * @author Roger Floriano (petruki) * @since 2019-12-24 */ -public class SwitcherLocalService extends SwitcherExecutor { +public class SwitcherLocalService extends SwitcherExecutorImpl { private static final Logger logger = LoggerFactory.getLogger(SwitcherLocalService.class); diff --git a/src/main/java/com/github/switcherapi/client/service/remote/SwitcherRemoteService.java b/src/main/java/com/github/switcherapi/client/service/remote/SwitcherRemoteService.java index d3b5c78..d4d63ab 100644 --- a/src/main/java/com/github/switcherapi/client/service/remote/SwitcherRemoteService.java +++ b/src/main/java/com/github/switcherapi/client/service/remote/SwitcherRemoteService.java @@ -1,6 +1,7 @@ package com.github.switcherapi.client.service.remote; import com.github.switcherapi.client.SwitcherExecutor; +import com.github.switcherapi.client.SwitcherExecutorImpl; import com.github.switcherapi.client.exception.SwitcherRemoteException; import com.github.switcherapi.client.exception.SwitchersValidationException; import com.github.switcherapi.client.model.ContextKey; @@ -16,13 +17,14 @@ import org.slf4j.LoggerFactory; import java.util.Arrays; +import java.util.Objects; import java.util.Set; /** * @author Roger Floriano (petruki) * @since 2019-12-24 */ -public class SwitcherRemoteService extends SwitcherExecutor { +public class SwitcherRemoteService extends SwitcherExecutorImpl { private static final Logger logger = LoggerFactory.getLogger(SwitcherRemoteService.class); @@ -30,7 +32,7 @@ public class SwitcherRemoteService extends SwitcherExecutor { private final ClientRemote clientRemote; - public SwitcherRemoteService(ClientRemote clientRemote, SwitcherExecutor switcherExecutor) { + public SwitcherRemoteService(final ClientRemote clientRemote, final SwitcherExecutor switcherExecutor) { super(switcherExecutor.getSwitcherProperties()); this.clientRemote = clientRemote; this.switcherLocal = switcherExecutor; @@ -50,7 +52,7 @@ public SwitcherResult executeCriteria(final SwitcherRequest switcher) { return tryExecuteLocalCriteria(switcher, e); } } - + private SwitcherResult tryExecuteLocalCriteria(final SwitcherRequest switcher, final SwitcherRemoteException e) { if (StringUtils.isNotBlank(switcherProperties.getValue(ContextKey.SILENT_MODE))) { @@ -73,7 +75,7 @@ private SwitcherResult tryExecuteLocalCriteria(final SwitcherRequest switcher, @Override public boolean checkSnapshotVersion() { if (StringUtils.isNotBlank(switcherProperties.getValue(ContextKey.SNAPSHOT_LOCATION)) - && this.switcherLocal.getDomain() != null) { + && Objects.nonNull(this.switcherLocal.getDomain())) { return super.checkSnapshotVersion(this.clientRemote, this.switcherLocal.getDomain()); } diff --git a/src/main/java/com/github/switcherapi/client/test/SwitcherBypass.java b/src/main/java/com/github/switcherapi/client/test/SwitcherBypass.java new file mode 100644 index 0000000..1278ae6 --- /dev/null +++ b/src/main/java/com/github/switcherapi/client/test/SwitcherBypass.java @@ -0,0 +1,65 @@ +package com.github.switcherapi.client.test; + +import com.github.switcherapi.client.model.SwitcherResult; +import com.google.gson.Gson; +import org.apache.commons.lang3.StringUtils; + +import java.util.HashMap; +import java.util.Map; + +/** + * SwitcherBypass is a helper class to manipulate the result of a given Switcher key.
+ * It can be used to simulate a result for a specific key, useful for testing purposes. + */ +public class SwitcherBypass { + + private static final Map bypass = new HashMap<>(); + + private SwitcherBypass() {} + + /** + * It manipulates the result of a given Switcher key. + * + * @param key name of the key that you want to change the result + * @param expectedResult that will be returned when performing isItOn + * @return SwitcherResult with the manipulated result + */ + public static SwitcherResult assume(final String key, boolean expectedResult) { + return assume(key, expectedResult, null); + } + + /** + * It manipulates the result of a given Switcher key. + * + * @param key name of the key that you want to change the result + * @param metadata additional information about the assumption (JSON) + * @param expectedResult that will be returned when performing isItOn + * @return SwitcherResult with the manipulated result + */ + public static SwitcherResult assume(final String key, boolean expectedResult, String metadata) { + SwitcherResult switcherResult = new SwitcherResult(); + switcherResult.setResult(expectedResult); + switcherResult.setReason("Switcher bypassed"); + + if (StringUtils.isNotBlank(metadata)) { + switcherResult.setMetadata(new Gson().fromJson(metadata, Object.class)); + } + + bypass.put(key, switcherResult); + return switcherResult; + } + + /** + * It will clean up any result manipulation added before by invoking {@link SwitcherBypass#assume(String, boolean)} + * + * @param key name of the key you want to remove + */ + public static void forget(final String key) { + bypass.remove(key); + } + + public static Map getBypass() { + return bypass; + } + +} diff --git a/src/main/java/com/github/switcherapi/client/test/SwitcherTestExtension.java b/src/main/java/com/github/switcherapi/client/test/SwitcherTestExtension.java index 0abbdfa..4a69721 100644 --- a/src/main/java/com/github/switcherapi/client/test/SwitcherTestExtension.java +++ b/src/main/java/com/github/switcherapi/client/test/SwitcherTestExtension.java @@ -1,6 +1,5 @@ package com.github.switcherapi.client.test; -import com.github.switcherapi.client.SwitcherExecutor; import com.github.switcherapi.client.model.SwitcherResult; import org.apache.commons.lang3.ArrayUtils; import org.junit.jupiter.api.extension.*; @@ -65,11 +64,11 @@ public void afterTestExecution(ExtensionContext context) { if (ArrayUtils.isNotEmpty(keys)) { for (String keyStored : keys) { - SwitcherExecutor.forget(keyStored); + SwitcherBypass.forget(keyStored); } } else { String switcherKey = store.remove(STORE_KEY, String.class); - SwitcherExecutor.forget(switcherKey); + SwitcherBypass.forget(switcherKey); } } @@ -79,7 +78,7 @@ private void mockMultipleSwitchers(ExtensionContext context, SwitcherTest switch .toArray(String[]::new); for (SwitcherTestValue value : switcherTest.switchers()) { - SwitcherResult switcherResult = SwitcherExecutor.assume(value.key(), inverted != value.result(), value.metadata()); + SwitcherResult switcherResult = SwitcherBypass.assume(value.key(), inverted != value.result(), value.metadata()); applySwitcherTestWhen(switcherResult, value.when()); } @@ -87,7 +86,7 @@ private void mockMultipleSwitchers(ExtensionContext context, SwitcherTest switch } private void mockSingleSwitcher(ExtensionContext context, SwitcherTest switcherTest, boolean inverted) { - SwitcherResult switcherResult = SwitcherExecutor.assume(switcherTest.key(), inverted != switcherTest.result(), switcherTest.metadata()); + SwitcherResult switcherResult = SwitcherBypass.assume(switcherTest.key(), inverted != switcherTest.result(), switcherTest.metadata()); applySwitcherTestWhen(switcherResult, switcherTest.when()); getStore(context).put(STORE_KEY, switcherTest.key()); } diff --git a/src/main/resources/META-INF/native-image/com.github.switcherapi.client/switcher-client/reflect-config.json b/src/main/resources/META-INF/native-image/com.github.switcherapi/switcher-client/reflect-config.json similarity index 95% rename from src/main/resources/META-INF/native-image/com.github.switcherapi.client/switcher-client/reflect-config.json rename to src/main/resources/META-INF/native-image/com.github.switcherapi/switcher-client/reflect-config.json index b9bd1dc..04a7809 100644 --- a/src/main/resources/META-INF/native-image/com.github.switcherapi.client/switcher-client/reflect-config.json +++ b/src/main/resources/META-INF/native-image/com.github.switcherapi/switcher-client/reflect-config.json @@ -2,7 +2,7 @@ { "name": "com.github.switcherapi.client.model.Entry", "condition": { - "typeReachable": "com.github.switcherapi.client.remote.dto.CriteriaRequest.GsonInputRequest" + "typeReachable": "com.github.switcherapi.client.remote.dto.CriteriaInputRequest" }, "allDeclaredFields": true, "methods": [ @@ -13,7 +13,7 @@ ] }, { - "name": "com.github.switcherapi.client.remote.dto.CriteriaRequest.GsonInputRequest", + "name": "com.github.switcherapi.client.remote.dto.CriteriaInputRequest", "condition": { "typeReachable": "com.github.switcherapi.client.model.Switcher" }, @@ -39,7 +39,7 @@ ] }, { - "name": "com.github.switcherapi.client.model.criteria.Criteria", + "name": "com.github.switcherapi.client.model.criteria.Data", "condition": { "typeReachable": "com.github.switcherapi.client.model.criteria.Snapshot" }, diff --git a/src/main/resources/META-INF/native-image/com.github.switcherapi.client/switcher-client/resource-config.json b/src/main/resources/META-INF/native-image/com.github.switcherapi/switcher-client/resource-config.json similarity index 100% rename from src/main/resources/META-INF/native-image/com.github.switcherapi.client/switcher-client/resource-config.json rename to src/main/resources/META-INF/native-image/com.github.switcherapi/switcher-client/resource-config.json diff --git a/src/test/java/com/github/switcherapi/client/SwitcherBypassTest.java b/src/test/java/com/github/switcherapi/client/SwitcherBypassTest.java index d23677b..76fc57a 100644 --- a/src/test/java/com/github/switcherapi/client/SwitcherBypassTest.java +++ b/src/test/java/com/github/switcherapi/client/SwitcherBypassTest.java @@ -1,9 +1,10 @@ package com.github.switcherapi.client; import com.github.switcherapi.client.model.StrategyValidator; -import com.github.switcherapi.client.model.SwitcherRequest; import com.github.switcherapi.client.model.Switcher; +import com.github.switcherapi.client.model.SwitcherRequest; import com.github.switcherapi.client.model.SwitcherResult; +import com.github.switcherapi.client.test.SwitcherBypass; import com.github.switcherapi.client.test.SwitcherTest; import com.github.switcherapi.client.test.SwitcherTestValue; import com.github.switcherapi.client.test.SwitcherTestWhen; @@ -34,7 +35,7 @@ static void setupContext() { @AfterAll static void resetMock() { - SwitcherExecutor.getBypass().clear(); + SwitcherBypass.getBypass().clear(); } @Test @@ -46,8 +47,8 @@ void shouldReturnFalse_afterAssumingItsFalse() { //test SwitcherRequest switcher = getSwitcher(USECASE11); assertTrue(switcher.isItOn()); - - SwitcherExecutor.assume(USECASE11, false); + + SwitcherBypass.assume(USECASE11, false); assertFalse(switcher.isItOn()); } @@ -59,8 +60,8 @@ void shouldReturnTrue_afterAssumingItsTrue() { SwitcherRequest switcher = getSwitcher(USECASE111); assertFalse(switcher.isItOn()); - - SwitcherExecutor.assume(USECASE111, true); + + SwitcherBypass.assume(USECASE111, true); assertTrue(switcher.isItOn()); } @@ -73,11 +74,11 @@ void shouldReturnTrue_afterForgettingItWasFalse() { //test SwitcherRequest switcher = getSwitcher(USECASE11); assertTrue(switcher.isItOn()); - - SwitcherExecutor.assume(USECASE11, false); + + SwitcherBypass.assume(USECASE11, false); assertFalse(switcher.isItOn()); - - SwitcherExecutor.forget(USECASE11); + + SwitcherBypass.forget(USECASE11); assertTrue(switcher.isItOn()); } @@ -90,11 +91,11 @@ void shouldReturnFalse_afterAssumingItsTrue() { //test SwitcherRequest switcher = getSwitcher(USECASE111); assertFalse(switcher.isItOn()); - - SwitcherExecutor.assume(USECASE111, true); + + SwitcherBypass.assume(USECASE111, true); assertTrue(switcher.isItOn()); - - SwitcherExecutor.forget(USECASE111); + + SwitcherBypass.forget(USECASE111); assertFalse(switcher.isItOn()); } @@ -252,7 +253,7 @@ void shouldReturnTrue_afterAssumingItsTrueWhenValueMatches() { //test Switcher switcher = getSwitcher(USECASE41).checkValue("Value1").build(); - SwitcherExecutor.assume(USECASE41, true) + SwitcherBypass.assume(USECASE41, true) .when(StrategyValidator.VALUE, "Value1"); assertTrue(switcher.isItOn()); @@ -267,7 +268,7 @@ void shouldReturnFalse_afterAssumingItsTrueWhenValueNotMatches() { //test Switcher switcher = getSwitcher(USECASE41).checkValue("Value2").build(); - SwitcherExecutor.assume(USECASE41, true) + SwitcherBypass.assume(USECASE41, true) .when(StrategyValidator.VALUE, "Value1"); assertFalse(switcher.isItOn()); diff --git a/src/test/java/com/github/switcherapi/client/remote/ClientRemoteTest.java b/src/test/java/com/github/switcherapi/client/remote/ClientRemoteTest.java index fdbed95..539717a 100644 --- a/src/test/java/com/github/switcherapi/client/remote/ClientRemoteTest.java +++ b/src/test/java/com/github/switcherapi/client/remote/ClientRemoteTest.java @@ -70,10 +70,13 @@ void shouldExecuteCriteria() { givenResponse(generateMockAuth(100)); givenResponse(generateCriteriaResponse("true", false)); + SwitcherProperties switcherProperties = Switchers.getSwitcherProperties(); SwitcherValidator validatorService = new ValidatorService(); ClientLocal clientLocal = new ClientLocalService(validatorService); - SwitcherRequest switcher = new SwitcherRequest("KEY", new SwitcherRemoteService(clientRemote, - new SwitcherLocalService(clientRemote, clientLocal, Switchers.getSwitcherProperties()))); + SwitcherRequest switcher = new SwitcherRequest( + "KEY", + new SwitcherRemoteService(clientRemote, new SwitcherLocalService(clientRemote, clientLocal, switcherProperties)), + switcherProperties); //test SwitcherResult actual = Mapper.mapFrom(clientRemote.executeCriteria(Mapper.mapFrom(switcher))); diff --git a/src/test/java/metainf/nativeimage/NativeReflectConfigTest.java b/src/test/java/metainf/nativeimage/NativeReflectConfigTest.java new file mode 100644 index 0000000..fbd86e8 --- /dev/null +++ b/src/test/java/metainf/nativeimage/NativeReflectConfigTest.java @@ -0,0 +1,59 @@ +package metainf.nativeimage; + +import com.google.gson.Gson; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Objects; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class NativeReflectConfigTest { + + private static final Path reflectPath = Path.of("src/main/resources/META-INF/native-image/com.github.switcherapi/switcher-client/reflect-config.json"); + + private static String reflectContent; + + @BeforeAll + static void readReflectConfig() throws Exception { + reflectContent = new String(Files.readAllBytes(reflectPath)); + } + + @Test + void shouldReadReflectConfig() { + assertTrue(reflectContent.contains("com.github.switcherapi.client")); + } + + @Test + void shouldParseReflectConfig() { + ReflectJson[] reflectJson = new Gson().fromJson(reflectContent, ReflectJson[].class); + assertTrue(reflectJson.length > 0); + } + + @Test + void shouldApplyReflection() { + ReflectJson[] reflectJson = new Gson().fromJson(reflectContent, ReflectJson[].class); + + for (ReflectJson json : reflectJson) { + assertDoesNotThrow(() -> Class.forName(json.name), + String.format("Class %s not reachable", json.name)); + + if (Objects.nonNull(json.condition)) { + assertDoesNotThrow(() -> Class.forName(json.condition.typeReachable), + String.format("Class %s not reachable", json.condition.typeReachable)); + } + } + } + + static class ReflectJson { + String name; + ReflectCondition condition; + } + + static class ReflectCondition { + String typeReachable; + } +} diff --git a/src/test/java/metainf/nativeimage/NativeResourceConfigTest.java b/src/test/java/metainf/nativeimage/NativeResourceConfigTest.java new file mode 100644 index 0000000..1553f2d --- /dev/null +++ b/src/test/java/metainf/nativeimage/NativeResourceConfigTest.java @@ -0,0 +1,159 @@ +package metainf.nativeimage; + +import com.google.gson.Gson; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; + +import static org.junit.jupiter.api.Assertions.*; + +class NativeResourceConfigTest { + + private static final Path resourcePath = Path.of("src/main/resources/META-INF/native-image/com.github.switcherapi/switcher-client/resource-config.json"); + + private static String resourceContent; + + @BeforeAll + static void readReflectConfig() throws Exception { + resourceContent = new String(Files.readAllBytes(resourcePath)); + } + + @Test + void shouldReadResourceConfig() { + assertTrue(resourceContent.contains("resources")); + } + + @Test + void shouldParseResourceConfig() { + ResourceJson resourceJson = new Gson().fromJson(resourceContent, ResourceJson.class); + assertTrue(resourceJson.resources.includes.length > 0); + } + + @Test + void shouldApplyReflectionFromCondition() { + ResourceJson resourceJson = new Gson().fromJson(resourceContent, ResourceJson.class); + + for (Include include : resourceJson.resources.includes) { + assertDoesNotThrow(() -> Class.forName(include.condition.typeReachable), + String.format("Class %s not reachable", include.condition.typeReachable)); + } + } + + @Test + void shouldContainPropertiesConfig() { + ResourceJson resourceJson = new Gson().fromJson(resourceContent, ResourceJson.class); + + boolean hasProperties = Arrays.stream(resourceJson.resources.includes) + .anyMatch(include -> include.pattern.contains("properties")); + + assertTrue(hasProperties, "Properties not found in resource config"); + } + + @Test + void shouldContainsSnapshotJsonConfig() { + ResourceJson resourceJson = new Gson().fromJson(resourceContent, ResourceJson.class); + + boolean hasSnapshotJson = Arrays.stream(resourceJson.resources.includes) + .anyMatch(include -> include.pattern.contains("snapshots/")); + + assertTrue(hasSnapshotJson, "snapshots/ structure not found in resource config"); + } + + @Test + void shouldBeValidPropertiesName() { + ResourceJson resourceJson = new Gson().fromJson(resourceContent, ResourceJson.class); + + for (Include include : resourceJson.resources.includes) { + if (include.pattern.contains("properties")) { + assertPattern(include, new String[] { + "switcherapi.properties", + "switcherapidev.properties", + "switcherapi-dev.properties" + }, true); + } + } + } + + @Test + void shouldNotBeValidPropertiesName() { + ResourceJson resourceJson = new Gson().fromJson(resourceContent, ResourceJson.class); + + for (Include include : resourceJson.resources.includes) { + if (include.pattern.contains("properties")) { + assertPattern(include, new String[] { + "switcher.properties", + "switcher-dev.properties" + }, false); + } + } + } + + @Test + void shouldBeValidSnapshotJsonPathName() { + ResourceJson resourceJson = new Gson().fromJson(resourceContent, ResourceJson.class); + + for (Include include : resourceJson.resources.includes) { + if (include.pattern.contains("snapshots/")) { + assertPattern(include, new String[] { + "snapshots/default.json", + "snapshots/production.json", + "snapshots/env-dev.json" + }, true); + } + } + } + + @Test + void shouldNotBeValidSnapshotJsonPathName() { + ResourceJson resourceJson = new Gson().fromJson(resourceContent, ResourceJson.class); + + for (Include include : resourceJson.resources.includes) { + if (include.pattern.contains("snapshots/")) { + assertPattern(include, new String[] { + "default.json", + "snapshots/default.yaml", + "snapshot/default.json" + }, false); + } + } + } + + private void assertPattern(Include include, String[] input, boolean expected) { + String pattern = include.pattern; + + boolean isValid = false; + for (String validProperty : input) { + if (validProperty.matches(pattern)) { + isValid = true; + break; + } + } + + if (expected) { + assertTrue(isValid, String.format("Pattern %s is not valid for properties", pattern)); + } else { + assertFalse(isValid, String.format("Pattern %s is valid for properties", pattern)); + } + } + + static class ResourceJson { + Resources resources; + } + + static class Resources { + Include[] includes; + } + + static class Include { + String pattern; + Condition condition; + } + + static class Condition { + String typeReachable; + } + +} From 04da65346b67be2468e496cf17e8b4ddf21a682c Mon Sep 17 00:00:00 2001 From: petruki <31597636+petruki@users.noreply.github.com> Date: Sun, 1 Dec 2024 17:22:35 -0800 Subject: [PATCH 2/2] Fixes tests --- src/test/java/metainf/nativeimage/NativeReflectConfigTest.java | 3 ++- .../java/metainf/nativeimage/NativeResourceConfigTest.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/test/java/metainf/nativeimage/NativeReflectConfigTest.java b/src/test/java/metainf/nativeimage/NativeReflectConfigTest.java index fbd86e8..00cb6f1 100644 --- a/src/test/java/metainf/nativeimage/NativeReflectConfigTest.java +++ b/src/test/java/metainf/nativeimage/NativeReflectConfigTest.java @@ -6,6 +6,7 @@ import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Objects; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; @@ -13,7 +14,7 @@ class NativeReflectConfigTest { - private static final Path reflectPath = Path.of("src/main/resources/META-INF/native-image/com.github.switcherapi/switcher-client/reflect-config.json"); + private static final Path reflectPath = Paths.get("src/main/resources/META-INF/native-image/com.github.switcherapi/switcher-client/reflect-config.json"); private static String reflectContent; diff --git a/src/test/java/metainf/nativeimage/NativeResourceConfigTest.java b/src/test/java/metainf/nativeimage/NativeResourceConfigTest.java index 1553f2d..25bc00b 100644 --- a/src/test/java/metainf/nativeimage/NativeResourceConfigTest.java +++ b/src/test/java/metainf/nativeimage/NativeResourceConfigTest.java @@ -6,13 +6,14 @@ import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Arrays; import static org.junit.jupiter.api.Assertions.*; class NativeResourceConfigTest { - private static final Path resourcePath = Path.of("src/main/resources/META-INF/native-image/com.github.switcherapi/switcher-client/resource-config.json"); + private static final Path resourcePath = Paths.get("src/main/resources/META-INF/native-image/com.github.switcherapi/switcher-client/resource-config.json"); private static String resourceContent;