diff --git a/src/main/java/io/github/artemget/entrys/file/EVal.java b/src/main/java/io/github/artemget/entrys/file/EVal.java index 4214455..878ac1a 100644 --- a/src/main/java/io/github/artemget/entrys/file/EVal.java +++ b/src/main/java/io/github/artemget/entrys/file/EVal.java @@ -24,21 +24,9 @@ package io.github.artemget.entrys.file; -import com.amihaiemil.eoyaml.Node; -import com.amihaiemil.eoyaml.Yaml; -import com.amihaiemil.eoyaml.YamlMapping; -import com.amihaiemil.eoyaml.YamlNode; import io.github.artemget.entrys.ESafe; import io.github.artemget.entrys.Entry; -import io.github.artemget.entrys.EntryException; -import io.github.artemget.entrys.operation.EContains; -import io.github.artemget.entrys.operation.EFork; -import io.github.artemget.entrys.operation.ESplit; -import io.github.artemget.entrys.operation.EUnwrap; -import io.github.artemget.entrys.system.EEnv; -import java.io.IOException; -import java.util.List; -import java.util.stream.Collectors; +import io.github.artemget.entrys.yaml.EYaml; /** * Configuration properties entry. @@ -77,72 +65,8 @@ public EVal(final String key, final String path) { @SuppressWarnings("PMD.ConstructorOnlyInitializesOrCallOtherConstructors") public EVal(final String key, final Entry content) { super( - () -> { - YamlMapping root; - try { - root = Yaml.createYamlInput(content.value()) - .readYamlMapping(); - } catch (final IOException | EntryException exception) { - throw new EntryException( - String.format("Failed to read yaml mapping for key: '%s'", key), - exception - ); - } - String res = null; - final List elements = new ESplit(() -> key, ".").value(); - for (int element = 0; element < elements.size(); ++element) { - if (element == elements.size() - 1) { - final YamlNode value = root.value(elements.get(element)); - if (Node.SCALAR == value.type()) { - res = EVal.ejected(value); - } else if (Node.SEQUENCE == value.type()) { - res = value.asSequence() - .children().stream() - .map(node -> node.asScalar().value()) - .collect(Collectors.joining(";")); - } - } - root = root.yamlMapping(elements.get(element)); - } - return res; - }, + () -> new EYaml(key, content).value(), () -> String.format("Attribute for key '%s' is null", key) ); } - - private static String ejected(final YamlNode node) throws EntryException { - final String scalar = node.asScalar().value(); - return new EFork<>( - () -> scalar.startsWith("${") && scalar.endsWith("}"), - new EFork<>( - () -> new ESplit(() -> scalar, ":").value().size() >= 2, - new EFork<>( - new EContains(new EEnv(() -> EVal.selectedEnv(scalar).trim())), - new EEnv(() -> EVal.selectedEnv(scalar).trim()), - () -> EVal.joined(scalar) - ), - new EEnv(new EUnwrap(scalar, "${", "}")) - ), - () -> scalar - ).value(); - } - - private static String selectedEnv(final String scalar) throws EntryException { - return new ESplit(new EUnwrap(scalar, "${", "}"), ":") - .value().get(0); - } - - private static String joined(final String scalar) throws EntryException { - final List split = new ESplit( - new EUnwrap(scalar, "${", "}"), ":" - ).value(); - final StringBuilder value = new StringBuilder(); - for (int index = 1; index < split.size(); ++index) { - value.append(split.get(index)); - if (index < split.size() - 1) { - value.append(':'); - } - } - return value.toString(); - } } diff --git a/src/main/java/io/github/artemget/entrys/profile/EValProf.java b/src/main/java/io/github/artemget/entrys/profile/EValProf.java new file mode 100644 index 0000000..d0b1cb7 --- /dev/null +++ b/src/main/java/io/github/artemget/entrys/profile/EValProf.java @@ -0,0 +1,87 @@ +/* + * MIT License + * + * Copyright (c) 2024-2025. Artem Getmanskii + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package io.github.artemget.entrys.profile; + +import io.github.artemget.entrys.ESafe; +import io.github.artemget.entrys.Entry; +import io.github.artemget.entrys.file.EFile; +import io.github.artemget.entrys.operation.EContains; +import io.github.artemget.entrys.yaml.EYaml; + +/** + * Configuration properties entry. + * By default gets entries from "src/main/resources/application.yaml" + * Supports all yaml types, default values and envs passed via ${ENV}. + * + * @since 0.4.0 + */ +public final class EValProf extends ESafe { + + /** + * From yaml file at default dir. + * + * @param key Of Entry + */ + public EValProf(final String key) { + this(key, "src/main/resources/application.yaml"); + } + + /** + * From yaml file. + * + * @param key Of Entry + * @param path To Yaml File + */ + public EValProf(final String key, final String path) { + this(key, new EFile(path), path); + } + + /** + * Main ctor. + * + * @param key Of Entry + * @param content Yaml Content + * @param path String Path + */ + public EValProf(final String key, final Entry content, final String path) { + super( + () -> { + final String result; + if (new EContains(new EYaml("entrys.profile", path)).value()) { + final String profile = new EYaml("entrys.profile", path).value(); + result = new EYaml(key, parsePath(path, profile)).value(); + } else { + result = new EYaml(key, content).value(); + } + return result; + }, + () -> String.format("Attribute for key '%s' is null", key) + ); + } + + private static String parsePath(final String path, final String profile) { + return path.replace("application", String.format("application-%s", profile)); + } +} diff --git a/src/main/java/io/github/artemget/entrys/profile/package-info.java b/src/main/java/io/github/artemget/entrys/profile/package-info.java new file mode 100644 index 0000000..e9b9346 --- /dev/null +++ b/src/main/java/io/github/artemget/entrys/profile/package-info.java @@ -0,0 +1,28 @@ +/* + * MIT License + * + * Copyright (c) 2024-2025. Artem Getmanskii + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/** + * Profile operation directory. + */ +package io.github.artemget.entrys.profile; diff --git a/src/main/java/io/github/artemget/entrys/yaml/EYaml.java b/src/main/java/io/github/artemget/entrys/yaml/EYaml.java new file mode 100644 index 0000000..c3af5f8 --- /dev/null +++ b/src/main/java/io/github/artemget/entrys/yaml/EYaml.java @@ -0,0 +1,160 @@ +/* + * MIT License + * + * Copyright (c) 2024-2025. Artem Getmanskii + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package io.github.artemget.entrys.yaml; + +import com.amihaiemil.eoyaml.Node; +import com.amihaiemil.eoyaml.Yaml; +import com.amihaiemil.eoyaml.YamlMapping; +import com.amihaiemil.eoyaml.YamlNode; +import io.github.artemget.entrys.ESafe; +import io.github.artemget.entrys.Entry; +import io.github.artemget.entrys.EntryException; +import io.github.artemget.entrys.EntryExceptionUnchecked; +import io.github.artemget.entrys.file.EFile; +import io.github.artemget.entrys.operation.EContains; +import io.github.artemget.entrys.operation.EFork; +import io.github.artemget.entrys.operation.ESplit; +import io.github.artemget.entrys.operation.EUnwrap; +import io.github.artemget.entrys.system.EEnv; +import java.io.IOException; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Configuration properties entry. + * By default gets entries from "src/main/resources/application.yaml" + * Supports all yaml types, default values and envs passed via ${ENV}. + * + * @since 0.4.0 + */ +public class EYaml extends ESafe { + + /** + * From yaml file at default dir. + * + * @param key Of Entry + */ + public EYaml(final String key) { + this(key, "src/main/resources/application.yaml"); + } + + /** + * From yaml file. + * + * @param key Of Entry + * @param path To Yaml File + */ + public EYaml(final String key, final String path) { + this(key, new EFile(path)); + } + + /** + * Main ctor. + * + * @param key Of Entry + * @param content Yaml Content + */ + public EYaml(final String key, final Entry content) { + super( + () -> { + YamlMapping root = fileToYaml(content, key); + String res = null; + final List elements = new ESplit(() -> key, ".").value(); + for (int element = 0; element < elements.size(); ++element) { + if (element == elements.size() - 1) { + final YamlNode value = root.value(elements.get(element)); + if (value.isEmpty()) { + throw new EntryExceptionUnchecked( + String.format("Empty value for key: %s", key) + ); + } + if (Node.SCALAR == value.type()) { + res = EYaml.ejected(value); + } else if (Node.SEQUENCE == value.type()) { + res = value.asSequence() + .children().stream() + .map(node -> node.asScalar().value()) + .collect(Collectors.joining(";")); + } + } + root = root.yamlMapping(elements.get(element)); + } + return res; + }, + () -> String.format("Attribute for key '%s' is null", key) + ); + } + + private static YamlMapping fileToYaml(final Entry content, final String key) + throws EntryException { + final YamlMapping root; + try { + root = Yaml.createYamlInput(content.value()) + .readYamlMapping(); + } catch (final IOException | EntryException exception) { + throw new EntryException( + String.format("Failed to read yaml mapping for key: '%s'", key), + exception + ); + } + return root; + } + + private static String ejected(final YamlNode node) throws EntryException { + final String scalar = node.asScalar().value(); + return new EFork<>( + () -> scalar.startsWith("${") && scalar.endsWith("}"), + new EFork<>( + () -> new ESplit(() -> scalar, ":").value().size() >= 2, + new EFork<>( + new EContains(new EEnv(() -> EYaml.selectedEnv(scalar).trim())), + new EEnv(() -> EYaml.selectedEnv(scalar).trim()), + () -> EYaml.joined(scalar) + ), + new EEnv(new EUnwrap(scalar, "${", "}")) + ), + () -> scalar + ).value(); + } + + private static String selectedEnv(final String scalar) throws EntryException { + return new ESplit(new EUnwrap(scalar, "${", "}"), ":") + .value().get(0); + } + + private static String joined(final String scalar) throws EntryException { + final List split = new ESplit( + new EUnwrap(scalar, "${", "}"), ":" + ).value(); + final StringBuilder value = new StringBuilder(); + for (int index = 1; index < split.size(); ++index) { + value.append(split.get(index)); + if (index < split.size() - 1) { + value.append(':'); + } + } + return value.toString(); + } +} diff --git a/src/main/java/io/github/artemget/entrys/yaml/package-info.java b/src/main/java/io/github/artemget/entrys/yaml/package-info.java new file mode 100644 index 0000000..ea1266f --- /dev/null +++ b/src/main/java/io/github/artemget/entrys/yaml/package-info.java @@ -0,0 +1,28 @@ +/* + * MIT License + * + * Copyright (c) 2024-2025. Artem Getmanskii + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/** + * Yaml operation directory. + */ +package io.github.artemget.entrys.yaml; diff --git a/src/test/java/io/github/artemget/entrys/file/EValTest.java b/src/test/java/io/github/artemget/entrys/file/EValTest.java index 57f6836..7ead671 100644 --- a/src/test/java/io/github/artemget/entrys/file/EValTest.java +++ b/src/test/java/io/github/artemget/entrys/file/EValTest.java @@ -25,6 +25,7 @@ package io.github.artemget.entrys.file; import io.github.artemget.entrys.EntryException; +import io.github.artemget.entrys.EntryExceptionUnchecked; import io.github.artemget.entrys.fake.EFake; import io.github.artemget.entrys.fake.EFakeErr; import org.junit.jupiter.api.Assertions; @@ -33,6 +34,7 @@ /** * Test cases for {@link io.github.artemget.entrys.json.EJsonArr}. + * * @since 0.4.0 */ @SuppressWarnings({"PMD.AvoidDuplicateLiterals", "PMD.TooManyMethods"}) @@ -100,7 +102,7 @@ void parsesBoolean() throws EntryException { @Test void throwsAtNullValue() { Assertions.assertThrows( - EntryException.class, + EntryExceptionUnchecked.class, () -> new EVal("age", new EFake<>("age: null")).value(), "Didnt throw at null value" ); @@ -138,9 +140,9 @@ void parsesArray() throws EntryException { @Test void parsesEmptyArray() throws EntryException { - Assertions.assertEquals( - "", - new EVal( + Assertions.assertThrows( + EntryExceptionUnchecked.class, + () -> new EVal( "ages", new EFake<>( "ages: []" diff --git a/src/test/java/io/github/artemget/entrys/profile/EValProfTest.java b/src/test/java/io/github/artemget/entrys/profile/EValProfTest.java new file mode 100644 index 0000000..5ae7121 --- /dev/null +++ b/src/test/java/io/github/artemget/entrys/profile/EValProfTest.java @@ -0,0 +1,75 @@ +/* + * MIT License + * + * Copyright (c) 2024-2025. Artem Getmanskii + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package io.github.artemget.entrys.profile; + +import io.github.artemget.entrys.EntryException; +import io.github.artemget.entrys.EntryExceptionUnchecked; +import io.github.artemget.entrys.fake.EFake; +import io.github.artemget.entrys.fake.EFakeErr; +import io.github.artemget.entrys.file.EVal; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * Test cases for {@link io.github.artemget.entrys.profile.EValProf}. + * + * @since 0.4.0 + */ +@SuppressWarnings({"PMD.AvoidDuplicateLiterals", "PMD.TooManyMethods"}) +final class EValProfTest { + + @Test + void throwsAtProfileIsBlank() { + Assertions.assertThrows( + EntryExceptionUnchecked.class, + () -> new EValProf( + "entrys.profile", new EFake<>("entrys:\n profile: \"\""), + null + ).value(), + "Profile node is blank" + ); + } + + @Test + void throwsAtProfileIsNull() { + Assertions.assertThrows( + EntryException.class, + () -> new EValProf("entrys.profile", new EFakeErr<>(), null).value(), + "Profile node is null" + ); + } + + @Test + void getValueIfProfileIsNull() throws EntryException { + Assertions.assertEquals( + "8080", + new EVal( + "port", + new EFake<>("port: 8080") + ).value(), + "Profile node is null" + ); + } +} diff --git a/src/test/java/io/github/artemget/entrys/profile/package-info.java b/src/test/java/io/github/artemget/entrys/profile/package-info.java new file mode 100644 index 0000000..b6f62e5 --- /dev/null +++ b/src/test/java/io/github/artemget/entrys/profile/package-info.java @@ -0,0 +1,28 @@ +/* + * MIT License + * + * Copyright (c) 2024-2025. Artem Getmanskii + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/** + * Tests for entries that read stuff from files with support profiles. + */ +package io.github.artemget.entrys.profile;