From 1cc6864a3c15c9a6e8b3c24437f79a31f79e0aeb Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Mon, 13 Jul 2020 14:20:52 +0200 Subject: [PATCH] add jexl parser to parse expressions in urls --- CHANGELOG.md | 1 + scm-core/pom.xml | 16 +++++ .../sonia/scm/util/JexlUrlExpression.java | 55 ++++++++++++++++ .../java/sonia/scm/util/JexlUrlParser.java | 40 +++++++++++ .../sonia/scm/web/data/EncodedStringList.java | 47 +++++++++++++ .../main/java/sonia/scm/web/data/Encoder.java | 58 ++++++++++++++++ .../web/data/ImmutableEncodedChangeset.java | 66 +++++++++++++++++++ .../data/ImmutableEncodedModifications.java | 60 +++++++++++++++++ .../scm/web/data/ImmutableEncodedPerson.java | 43 ++++++++++++ .../web/data/ImmutableEncodedRepository.java | 64 ++++++++++++++++++ .../sonia/scm/util/JexlUrlParserTest.java | 59 +++++++++++++++++ 11 files changed, 509 insertions(+) create mode 100644 scm-core/src/main/java/sonia/scm/util/JexlUrlExpression.java create mode 100644 scm-core/src/main/java/sonia/scm/util/JexlUrlParser.java create mode 100644 scm-core/src/main/java/sonia/scm/web/data/EncodedStringList.java create mode 100644 scm-core/src/main/java/sonia/scm/web/data/Encoder.java create mode 100644 scm-core/src/main/java/sonia/scm/web/data/ImmutableEncodedChangeset.java create mode 100644 scm-core/src/main/java/sonia/scm/web/data/ImmutableEncodedModifications.java create mode 100644 scm-core/src/main/java/sonia/scm/web/data/ImmutableEncodedPerson.java create mode 100644 scm-core/src/main/java/sonia/scm/web/data/ImmutableEncodedRepository.java create mode 100644 scm-core/src/test/java/sonia/scm/util/JexlUrlParserTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e9e88694a..cfa98f4782 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Add branch link provider to access branch links in plugins ([#1243](https://github.com/scm-manager/scm-manager/pull/1243)) - Add key value input field component ([#1246](https://github.com/scm-manager/scm-manager/pull/1246)) +- Add Jexl parser ([#1251](https://github.com/scm-manager/scm-manager/pull/1251)) ### Changed - Adding start delay to liveness and readiness probes in helm chart template diff --git a/scm-core/pom.xml b/scm-core/pom.xml index d3bce80abf..2ada8e2e76 100644 --- a/scm-core/pom.xml +++ b/scm-core/pom.xml @@ -216,6 +216,22 @@ 2.6 + + org.apache.commons + commons-jexl + 2.1.1 + + + commons-logging + commons-logging + + + org.slf4j + jcl-over-slf4j + + + + diff --git a/scm-core/src/main/java/sonia/scm/util/JexlUrlExpression.java b/scm-core/src/main/java/sonia/scm/util/JexlUrlExpression.java new file mode 100644 index 0000000000..a6b8742cc3 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/util/JexlUrlExpression.java @@ -0,0 +1,55 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * 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 sonia.scm.util; + +import org.apache.commons.jexl2.MapContext; +import org.apache.commons.jexl2.UnifiedJEXL.Expression; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; + +public class JexlUrlExpression { + + private static final Logger logger = LoggerFactory.getLogger(JexlUrlExpression.class); + + private final Expression expression; + + public JexlUrlExpression(Expression expression) { + this.expression = expression; + } + + public String evaluate(Map environment) { + String url = Util.EMPTY_STRING; + Object result = expression.evaluate(new MapContext(environment)); + + if (result != null) { + url = result.toString(); + } + + logger.trace("result of expression evaluation: {}", url); + + return url; + } +} diff --git a/scm-core/src/main/java/sonia/scm/util/JexlUrlParser.java b/scm-core/src/main/java/sonia/scm/util/JexlUrlParser.java new file mode 100644 index 0000000000..c4657450ea --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/util/JexlUrlParser.java @@ -0,0 +1,40 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * 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 sonia.scm.util; + +import org.apache.commons.jexl2.JexlEngine; +import org.apache.commons.jexl2.UnifiedJEXL; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class JexlUrlParser { + + private static final Logger logger = LoggerFactory.getLogger(JexlUrlParser.class); + private final UnifiedJEXL uel = new UnifiedJEXL(new JexlEngine()); + + public JexlUrlExpression parse(String urlPattern) { + logger.trace("try to parse url pattern: {}", urlPattern); + return new JexlUrlExpression(uel.parse(urlPattern)); + } +} diff --git a/scm-core/src/main/java/sonia/scm/web/data/EncodedStringList.java b/scm-core/src/main/java/sonia/scm/web/data/EncodedStringList.java new file mode 100644 index 0000000000..9f652dd65c --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/web/data/EncodedStringList.java @@ -0,0 +1,47 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * 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 sonia.scm.web.data; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.common.base.Joiner; + +import java.util.List; + +public final class EncodedStringList { + private final List list; + + public EncodedStringList(List list) { + this.list = list; + } + + @Override + public String toString() { + return Joiner.on(',').join(Encoder.encode(list)); + } + + public List getList() { + return list; + } +} diff --git a/scm-core/src/main/java/sonia/scm/web/data/Encoder.java b/scm-core/src/main/java/sonia/scm/web/data/Encoder.java new file mode 100644 index 0000000000..d69bfc31a8 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/web/data/Encoder.java @@ -0,0 +1,58 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * 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 sonia.scm.web.data; + +import com.google.common.collect.Lists; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.List; + +public final class Encoder { + + private static final String ENCODING = "UTF-8"; + private static final Logger logger = LoggerFactory.getLogger(Encoder.class); + + private Encoder() { + } + + public static String encode(String value) { + if (value != null) { + try { + value = URLEncoder.encode(value, ENCODING); + } catch (UnsupportedEncodingException ex) { + logger.error("encoding is not supported", ex); + } + + } + + return value; + } + + public static List encode(List values) { + return Lists.transform(values, Encoder::encode); + } +} diff --git a/scm-core/src/main/java/sonia/scm/web/data/ImmutableEncodedChangeset.java b/scm-core/src/main/java/sonia/scm/web/data/ImmutableEncodedChangeset.java new file mode 100644 index 0000000000..d074ade672 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/web/data/ImmutableEncodedChangeset.java @@ -0,0 +1,66 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * 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 sonia.scm.web.data; + +//~--- non-JDK imports -------------------------------------------------------- + +import sonia.scm.repository.Changeset; + +public final class ImmutableEncodedChangeset { + + private final Changeset changeset; + + public ImmutableEncodedChangeset(Changeset changeset) { + this.changeset = changeset; + } + + public ImmutableEncodedPerson getAuthor() { + return new ImmutableEncodedPerson(changeset.getAuthor()); + } + + public EncodedStringList getBranches() { + return new EncodedStringList(changeset.getBranches()); + } + + public Long getDate() { + return changeset.getDate(); + } + + public String getDescription() { + return Encoder.encode(changeset.getDescription()); + } + + public String getId() { + return changeset.getId(); + } + + public EncodedStringList getParents() { + return new EncodedStringList(changeset.getParents()); + } + + public EncodedStringList getTags() { + return new EncodedStringList(changeset.getTags()); + } +} diff --git a/scm-core/src/main/java/sonia/scm/web/data/ImmutableEncodedModifications.java b/scm-core/src/main/java/sonia/scm/web/data/ImmutableEncodedModifications.java new file mode 100644 index 0000000000..7b412cbc98 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/web/data/ImmutableEncodedModifications.java @@ -0,0 +1,60 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * 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 sonia.scm.web.data; + +import sonia.scm.repository.Added; +import sonia.scm.repository.Modifications; +import sonia.scm.repository.Modified; +import sonia.scm.repository.Removed; + +import java.util.stream.Collectors; + +public final class ImmutableEncodedModifications { + + private final Modifications modifications; + + public ImmutableEncodedModifications(Modifications modifications) { + this.modifications = modifications; + } + + @Override + public String toString() { + return String.format("A:%s;M:%s;R:%s", getAdded(), getModified(), getRemoved()); + } + + public EncodedStringList getAdded() { + return new EncodedStringList(modifications.getAdded().stream().map(Added::getPath).collect(Collectors.toList())); + + } + + public EncodedStringList getModified() { + return new EncodedStringList(modifications.getModified().stream().map(Modified::getPath).collect(Collectors.toList())); + } + + public EncodedStringList getRemoved() { + return new EncodedStringList(modifications.getRemoved().stream().map(Removed::getPath).collect(Collectors.toList())); + } + +} diff --git a/scm-core/src/main/java/sonia/scm/web/data/ImmutableEncodedPerson.java b/scm-core/src/main/java/sonia/scm/web/data/ImmutableEncodedPerson.java new file mode 100644 index 0000000000..c5ede4cdfa --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/web/data/ImmutableEncodedPerson.java @@ -0,0 +1,43 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * 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 sonia.scm.web.data; + +import sonia.scm.repository.Person; + +public final class ImmutableEncodedPerson { + + private final Person person; + + public ImmutableEncodedPerson(Person person) { + this.person = person; + } + + public String getMail() { + return Encoder.encode(person.getMail()); + } + + public String getName() { + return Encoder.encode(person.getName()); + } +} diff --git a/scm-core/src/main/java/sonia/scm/web/data/ImmutableEncodedRepository.java b/scm-core/src/main/java/sonia/scm/web/data/ImmutableEncodedRepository.java new file mode 100644 index 0000000000..965bdc4754 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/web/data/ImmutableEncodedRepository.java @@ -0,0 +1,64 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * 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 sonia.scm.web.data; + +import sonia.scm.repository.Repository; + +public final class ImmutableEncodedRepository { + + private final Repository repository; + + public ImmutableEncodedRepository(Repository repository) { + this.repository = repository; + } + + public String getContact() { + return Encoder.encode(repository.getContact()); + } + + public Long getCreationDate() { + return repository.getCreationDate(); + } + + public String getDescription() { + return Encoder.encode(repository.getDescription()); + } + + public String getId() { + return repository.getId(); + } + + public Long getLastModified() { + return repository.getLastModified(); + } + + public String getName() { + return repository.getName(); + } + + public String getType() { + return repository.getType(); + } + +} diff --git a/scm-core/src/test/java/sonia/scm/util/JexlUrlParserTest.java b/scm-core/src/test/java/sonia/scm/util/JexlUrlParserTest.java new file mode 100644 index 0000000000..2d5ec98e0e --- /dev/null +++ b/scm-core/src/test/java/sonia/scm/util/JexlUrlParserTest.java @@ -0,0 +1,59 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * 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 sonia.scm.util; + +import org.junit.jupiter.api.Test; +import sonia.scm.repository.Changeset; +import sonia.scm.repository.Person; + +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +class JexlUrlParserTest { + + @Test + void shouldParseUrlWithoutExpression() { + JexlUrlParser jexlUrlParser = new JexlUrlParser(); + Map env = new HashMap<>(); + env.put("changeset", new Changeset("1", 1L, Person.toPerson("trillian"))); + + String parsedUrl = jexlUrlParser.parse("http://hitchhiker.org").evaluate(env); + + assertThat(parsedUrl).isEqualTo("http://hitchhiker.org"); + } + + @Test + void shouldParseUrlWithExpression() { + JexlUrlParser jexlUrlParser = new JexlUrlParser(); + Map env = new HashMap<>(); + env.put("changeset", new Changeset("1", 1L, Person.toPerson("trillian"))); + + String parsedUrl = jexlUrlParser.parse("http://${changeset.author.name}.org").evaluate(env); + + assertThat(parsedUrl).isEqualTo("http://trillian.org"); + } +}