diff --git a/.github/workflows/master-2.yml b/.github/workflows/master-2.yml index b94ab7f..1f64f7f 100644 --- a/.github/workflows/master-2.yml +++ b/.github/workflows/master-2.yml @@ -9,7 +9,7 @@ on: jobs: build-scan: name: SonarCloud Scan - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 @@ -35,7 +35,7 @@ jobs: fail-fast: false matrix: java: ['11', '17', '21'] - os: [ubuntu-latest, windows-latest] + os: [ubuntu-22.04, windows-latest] runs-on: ${{ matrix.os }} steps: diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index c8000c7..1e282a1 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -9,7 +9,7 @@ on: jobs: build-scan: name: SonarCloud Scan - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 @@ -35,7 +35,7 @@ jobs: fail-fast: false matrix: java: ['8', '11', '17', '21'] - os: [ubuntu-latest, windows-latest] + os: [ubuntu-22.04, windows-latest] runs-on: ${{ matrix.os }} steps: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0ee87e7..f0dc27d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,7 +11,7 @@ jobs: fail-fast: false matrix: java: ['11', '17', '21'] - os: [ubuntu-latest, windows-latest] + os: [ubuntu-22.04, windows-latest] runs-on: ${{ matrix.os }} steps: @@ -39,7 +39,7 @@ jobs: publish: name: Publish Release needs: [build-test] - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml index db54d6e..2f67530 100644 --- a/.github/workflows/sonar.yml +++ b/.github/workflows/sonar.yml @@ -11,7 +11,7 @@ on: jobs: sonar-analysis: name: SonarCloud Analysis for PR - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Get PR details diff --git a/.github/workflows/staging.yml b/.github/workflows/staging.yml index 9e8bccf..a48d1fd 100644 --- a/.github/workflows/staging.yml +++ b/.github/workflows/staging.yml @@ -9,7 +9,7 @@ on: required: true default: '17' os: - description: 'Operating System (ubuntu-20.04, ubuntu-latest, windows-latest)' + description: 'Operating System (ubuntu-22.04, ubuntu-latest, windows-latest)' required: true default: 'ubuntu-latest' diff --git a/pom.xml b/pom.xml index 1bbb0aa..a87ab88 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ com.switcherapi switcher-client jar - 2.4.0 + 2.4.1-SNAPSHOT Switcher Client Switcher Client SDK for working with Switcher API diff --git a/src/main/java/com/switcherapi/client/model/Entry.java b/src/main/java/com/switcherapi/client/model/Entry.java index 56d8e29..ba3f38a 100644 --- a/src/main/java/com/switcherapi/client/model/Entry.java +++ b/src/main/java/com/switcherapi/client/model/Entry.java @@ -11,27 +11,29 @@ public class Entry { private final String strategy; private final String input; - - private Entry(final String strategy, final String input) { + + public Entry(String strategy, String input) { this.strategy = strategy; this.input = input; } - - private Entry(final StrategyValidator strategy, final String input) { + + public Entry(StrategyValidator strategy, String input) { this(strategy.toString(), input); } /** + * Creates a new Entry with the given strategy and input. + * * @param strategy Validator used to evaluate the Switcher * @param input follow the required format documented into each strategy type * @return new Entry * @see StrategyValidator */ - public static Entry build(final StrategyValidator strategy, final String input) { + public static Entry of(StrategyValidator strategy, String input) { return new Entry(strategy, input); } - - public static Entry build(final String strategy, final String input) { + + public static Entry of(String strategy, String input) { return new Entry(strategy, input); } diff --git a/src/main/java/com/switcherapi/client/model/SwitcherBuilder.java b/src/main/java/com/switcherapi/client/model/SwitcherBuilder.java index 7dd36ba..a7d393c 100644 --- a/src/main/java/com/switcherapi/client/model/SwitcherBuilder.java +++ b/src/main/java/com/switcherapi/client/model/SwitcherBuilder.java @@ -95,7 +95,7 @@ public SwitcherBuilder restrictRelay(boolean restrictRelay) { */ public SwitcherBuilder check(StrategyValidator strategy, String input) { if (StringUtils.isNotBlank(input)) { - entry.add(Entry.build(strategy, input)); + entry.add(Entry.of(strategy, input)); } return this; diff --git a/src/main/resources/META-INF/native-image/com.switcherapi/switcher-client/reflect-config.json b/src/main/resources/META-INF/native-image/com.switcherapi/switcher-client/reflect-config.json index 700b356..a66c838 100644 --- a/src/main/resources/META-INF/native-image/com.switcherapi/switcher-client/reflect-config.json +++ b/src/main/resources/META-INF/native-image/com.switcherapi/switcher-client/reflect-config.json @@ -8,7 +8,10 @@ "methods": [ { "name": "", - "parameterTypes": [] + "parameterTypes": [ + "java.lang.String", + "java.lang.String" + ] } ] }, @@ -21,7 +24,9 @@ "methods": [ { "name": "", - "parameterTypes": [] + "parameterTypes": [ + "com.switcherapi.client.model.Entry[]" + ] } ] }, @@ -34,7 +39,14 @@ "methods": [ { "name": "", - "parameterTypes": [] + "parameterTypes": [ + "java.lang.String", + "java.lang.String", + "boolean", + "com.switcherapi.client.model.criteria.StrategyConfig[]", + "java.lang.String[]", + "com.switcherapi.client.model.criteria.Relay" + ] } ] }, @@ -73,7 +85,12 @@ "methods": [ { "name": "", - "parameterTypes": [] + "parameterTypes": [ + "java.lang.String", + "java.lang.String", + "boolean", + "com.switcherapi.client.model.criteria.Config[]" + ] } ] }, @@ -99,7 +116,29 @@ "methods": [ { "name": "", - "parameterTypes": [] + "parameterTypes": [ + "java.lang.String", + "java.lang.String", + "java.lang.String", + "boolean", + "java.lang.String[]" + ] + } + ] + }, + { + "name": "com.switcherapi.client.model.criteria.Relay", + "condition": { + "typeReachable": "com.switcherapi.client.model.criteria.Snapshot" + }, + "allDeclaredFields": true, + "methods": [ + { + "name": "", + "parameterTypes": [ + "java.lang.String", + "boolean" + ] } ] }, diff --git a/src/test/java/com/switcherapi/client/SwitcherLocal1Test.java b/src/test/java/com/switcherapi/client/SwitcherLocal1Test.java index a9b5d8e..9c5f184 100644 --- a/src/test/java/com/switcherapi/client/SwitcherLocal1Test.java +++ b/src/test/java/com/switcherapi/client/SwitcherLocal1Test.java @@ -115,7 +115,7 @@ static Stream dateTestArguments() { @MethodSource("dateTestArguments") void localShouldTest_dateValidation(String useCaseKey, String input, boolean expected) { SwitcherRequest switcher = Switchers.getSwitcher(useCaseKey); - Entry entry = Entry.build(StrategyValidator.DATE, input); + Entry entry = Entry.of(StrategyValidator.DATE, input); assertEquals(expected, switcher.prepareEntry(entry).isItOn()); } @@ -130,7 +130,7 @@ void localShouldTestChained_dateValidation(String useCaseKey, String input, bool @Test void localShouldReturnFalse_dateValidationWrongFormat() { SwitcherRequest switcher = Switchers.getSwitcher(Switchers.USECASE33); - Entry input = Entry.build(StrategyValidator.DATE, "2019/121/13"); + Entry input = Entry.of(StrategyValidator.DATE, "2019/121/13"); switcher.prepareEntry(input); assertThrows(SwitcherInvalidTimeFormat.class, switcher::isItOn); @@ -157,7 +157,7 @@ static Stream valueTestArguments() { @MethodSource("valueTestArguments") void localShouldTest_valueValidation(String useCaseKey, String input, boolean expected) { SwitcherRequest switcher = Switchers.getSwitcher(useCaseKey); - Entry entry = Entry.build(StrategyValidator.VALUE, input); + Entry entry = Entry.of(StrategyValidator.VALUE, input); switcher.prepareEntry(entry); assertEquals(expected, switcher.isItOn()); @@ -198,7 +198,7 @@ static Stream numericTestArguments() { @MethodSource("numericTestArguments") void localShouldTest_numericValidation(String useCaseKey, String input, boolean expected) { SwitcherRequest switcher = Switchers.getSwitcher(useCaseKey); - Entry entry = Entry.build(StrategyValidator.NUMERIC, input); + Entry entry = Entry.of(StrategyValidator.NUMERIC, input); switcher.prepareEntry(entry); assertEquals(expected, switcher.isItOn()); @@ -215,7 +215,7 @@ void localShouldTestChained_numericValidation(String useCaseKey, String input, b @Test void localShouldReturnException_invalidNumericInput() { SwitcherRequest switcher = Switchers.getSwitcher(Switchers.USECASE81); - Entry input = Entry.build(StrategyValidator.NUMERIC, "INVALID_NUMBER"); + Entry input = Entry.of(StrategyValidator.NUMERIC, "INVALID_NUMBER"); switcher.prepareEntry(input); assertThrows(SwitcherInvalidNumericFormat.class, switcher::isItOn); @@ -239,7 +239,7 @@ static Stream timeTestArguments() { @MethodSource("timeTestArguments") void localShouldTest_timeValidation(String useCaseKey, String input, boolean expected) { SwitcherRequest switcher = Switchers.getSwitcher(useCaseKey); - Entry entry = Entry.build(StrategyValidator.TIME, input); + Entry entry = Entry.of(StrategyValidator.TIME, input); switcher.prepareEntry(entry); assertEquals(expected, switcher.isItOn()); @@ -255,7 +255,7 @@ void localShouldTestChained_timeValidation(String useCaseKey, String input, bool @Test void localShouldReturnFalse_timeValidationWrongFormat() { SwitcherRequest switcher = Switchers.getSwitcher(Switchers.USECASE53); - Entry input = Entry.build(StrategyValidator.TIME, "2019-12-10"); + Entry input = Entry.of(StrategyValidator.TIME, "2019-12-10"); switcher.prepareEntry(input); assertThrows(SwitcherInvalidTimeFormat.class, switcher::isItOn); @@ -279,7 +279,7 @@ static Stream networkTestArguments() { @MethodSource("networkTestArguments") void localShouldTest_networkValidation(String useCaseKey, String input, boolean expected) { SwitcherRequest switcher = Switchers.getSwitcher(useCaseKey); - Entry entry = Entry.build(StrategyValidator.NETWORK, input); + Entry entry = Entry.of(StrategyValidator.NETWORK, input); switcher.prepareEntry(entry); assertEquals(expected, switcher.isItOn()); @@ -301,7 +301,7 @@ void localShouldReturnFalse_strategyRequiresInput() { @Test void localShouldReturnFalse_invalidStrategyInput() { SwitcherRequest switcher = Switchers.getSwitcher(Switchers.USECASE33); - switcher.prepareEntry(Entry.build(StrategyValidator.INVALID, "Value")); + switcher.prepareEntry(Entry.of(StrategyValidator.INVALID, "Value")); assertFalse(switcher.isItOn()); } @@ -328,7 +328,7 @@ static Stream regexTestArguments() { @MethodSource("regexTestArguments") void localShouldTest_regexValidation(String useCaseKey, String input, boolean expected) { SwitcherRequest switcher = Switchers.getSwitcher(useCaseKey); - Entry entry = Entry.build(StrategyValidator.REGEX, input); + Entry entry = Entry.of(StrategyValidator.REGEX, input); switcher.prepareEntry(entry); assertEquals(expected, switcher.isItOn()); @@ -357,7 +357,7 @@ static Stream payloadTestArguments() { @MethodSource("payloadTestArguments") void localShouldTest_payloadValidation(String useCaseKey, String input, boolean expected) { SwitcherRequest switcher = Switchers.getSwitcher(useCaseKey); - Entry entry = Entry.build(StrategyValidator.PAYLOAD, input); + Entry entry = Entry.of(StrategyValidator.PAYLOAD, input); switcher.prepareEntry(entry); assertEquals(expected, switcher.isItOn()); diff --git a/src/test/java/com/switcherapi/client/SwitcherLocal3Test.java b/src/test/java/com/switcherapi/client/SwitcherLocal3Test.java index a0c2bd5..52a23a1 100644 --- a/src/test/java/com/switcherapi/client/SwitcherLocal3Test.java +++ b/src/test/java/com/switcherapi/client/SwitcherLocal3Test.java @@ -79,7 +79,7 @@ static Stream failTestArguments() { void localShouldReturnError(String useCaseKey, String strategyValidator, String input, Class error) { SwitcherRequest switcher = Switchers.getSwitcher(useCaseKey); - switcher.prepareEntry(Entry.build(strategyValidator, input)); + switcher.prepareEntry(Entry.of(strategyValidator, input)); assertThrows(error, switcher::isItOn); } diff --git a/src/test/java/com/switcherapi/client/model/ModelTest.java b/src/test/java/com/switcherapi/client/model/ModelTest.java index 923a0e3..01ac7de 100644 --- a/src/test/java/com/switcherapi/client/model/ModelTest.java +++ b/src/test/java/com/switcherapi/client/model/ModelTest.java @@ -9,8 +9,8 @@ class ModelTest { @Test void testModelEntry() { - Entry entry1 = Entry.build(StrategyValidator.DATE, "2019-12-10"); - Entry entry2 = Entry.build(StrategyValidator.VALUE, "Value"); + Entry entry1 = Entry.of(StrategyValidator.DATE, "2019-12-10"); + Entry entry2 = Entry.of(StrategyValidator.VALUE, "Value"); assertNotEquals(true, entry1.equals(entry2)); assertNotNull(entry1.toString()); diff --git a/src/test/java/metainf/nativeimage/NativeReflectConfigTest.java b/src/test/java/metainf/nativeimage/NativeReflectConfigTest.java index d1b3e9a..dd7d2cf 100644 --- a/src/test/java/metainf/nativeimage/NativeReflectConfigTest.java +++ b/src/test/java/metainf/nativeimage/NativeReflectConfigTest.java @@ -4,6 +4,7 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import java.lang.reflect.Array; import java.nio.file.Files; import java.nio.file.Path; import java.util.Objects; @@ -38,8 +39,7 @@ 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)); + assertConstructorsReachable(json); if (Objects.nonNull(json.condition)) { assertDoesNotThrow(() -> Class.forName(json.condition.typeReachable), @@ -48,12 +48,65 @@ void shouldApplyReflection() { } } + private void assertConstructorsReachable(ReflectJson json) { + if (Objects.nonNull(json.methods)) { + for (ReflectMethod method : json.methods) { + if ("".equals(method.name)) { + assertDoesNotThrow(() -> Class.forName(json.name).getDeclaredConstructor(getArgClasses(method.parameterTypes)), + String.format("Constructor (%s) not reachable in class %s", + String.join(", ", method.parameterTypes), json.name)); + } + } + } + } + + private Class[] getArgClasses(String[] parameterTypes) throws ClassNotFoundException { + if (parameterTypes == null || parameterTypes.length == 0) { + return new Class[0]; + } + + Class[] classes = new Class[parameterTypes.length]; + for (int i = 0; i < parameterTypes.length; i++) { + String paramType = parameterTypes[i]; + if (paramType.endsWith("[]")) { + String baseType = paramType.substring(0, paramType.length() - 2); + Class baseClass = getPrimitiveOrClass(baseType); + classes[i] = Array.newInstance(baseClass, 0).getClass(); + } else { + classes[i] = getPrimitiveOrClass(paramType); + } + } + + return classes; + } + + private Class getPrimitiveOrClass(String typeName) throws ClassNotFoundException { + switch (typeName) { + case "boolean": return boolean.class; + case "byte": return byte.class; + case "char": return char.class; + case "short": return short.class; + case "int": return int.class; + case "long": return long.class; + case "float": return float.class; + case "double": return double.class; + case "void": return void.class; + default: return Class.forName(typeName); + } + } + static class ReflectJson { String name; ReflectCondition condition; + ReflectMethod[] methods; } static class ReflectCondition { String typeReachable; } + + static class ReflectMethod { + String name; + String[] parameterTypes; + } }