resolve placeholders in uriPath

This commit is contained in:
Michael Hoennig 2024-10-30 14:14:47 +01:00
parent 3b94f117fb
commit 1cb0ea1018
4 changed files with 129 additions and 36 deletions

View File

@ -12,6 +12,7 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.TestInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.testcontainers.shaded.org.apache.commons.lang3.ObjectUtils;
import java.lang.reflect.Method;
import java.util.HashMap;
@ -35,7 +36,7 @@ public abstract class ScenarioTest extends ContextBasedTest {
@Override
public String toString() {
return uuid.toString();
return ObjectUtils.toString(uuid);
}
}

View File

@ -1,9 +1,49 @@
package net.hostsharing.hsadminng.hs.office.scenarios;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Map;
public class TemplateResolver {
enum PlaceholderPrefix {
RAW('%') {
@Override
String convert(final Object value) {
return value.toString();
}
},
JSON_QUOTED('$'){
@Override
String convert(final Object value) {
return jsonQuoted(value);
}
},
URI_ENCODED('&'){
@Override
String convert(final Object value) {
return URLEncoder.encode(value.toString(), StandardCharsets.UTF_8);
}
};
private final char prefixChar;
PlaceholderPrefix(final char prefixChar) {
this.prefixChar = prefixChar;
}
static boolean contains(final char givenChar) {
return Arrays.stream(values()).anyMatch(p -> p.prefixChar == givenChar);
}
static PlaceholderPrefix ofPrefixChar(final char givenChar) {
return Arrays.stream(values()).filter(p -> p.prefixChar == givenChar).findFirst().orElseThrow();
}
abstract String convert(final Object value);
}
private final String template;
private final Map<String, Object> properties;
private final StringBuilder resolved = new StringBuilder();
@ -21,7 +61,7 @@ public class TemplateResolver {
private void copy() {
while (hasMoreChars()) {
if ((currentChar() == '$' || currentChar() == '%') && nextChar() == '{') {
if (PlaceholderPrefix.contains(currentChar()) && nextChar() == '{') {
startPlaceholder(currentChar());
} else {
resolved.append(fetchChar());
@ -41,7 +81,7 @@ public class TemplateResolver {
if (currentChar() == '}') {
--nested;
placeholder.append(fetchChar());
} else if ((currentChar() == '$' || currentChar() == '%') && nextChar() == '{') {
} else if (PlaceholderPrefix.contains (currentChar()) && nextChar() == '{') {
++nested;
placeholder.append(fetchChar());
} else {
@ -50,11 +90,9 @@ public class TemplateResolver {
}
final var name = new TemplateResolver(placeholder.toString(), properties).resolve();
final var value = propVal(name);
if ( intro == '%') {
resolved.append(value);
} else {
resolved.append(optionallyQuoted(value));
}
resolved.append(
PlaceholderPrefix.ofPrefixChar(intro).convert(value)
);
skipChar('}');
}
@ -104,7 +142,7 @@ public class TemplateResolver {
return template.charAt(position+1);
}
private static String optionallyQuoted(final Object value) {
private static String jsonQuoted(final Object value) {
return switch (value) {
case Boolean bool -> bool.toString();
case Number number -> number.toString();
@ -112,27 +150,4 @@ public class TemplateResolver {
default -> "\"" + value + "\"";
};
}
public static void main(String[] args) {
System.out.println(
new TemplateResolver("""
etwas davor,
${einfacher Platzhalter},
${verschachtelter %{Name}},
und nochmal ohne Quotes:
%{einfacher Platzhalter},
%{verschachtelter %{Name}},
etwas danach.
""",
Map.ofEntries(
Map.entry("Name", "placeholder"),
Map.entry("einfacher Platzhalter", "simple placeholder"),
Map.entry("verschachtelter placeholder", "nested placeholder")
)).resolve());
}
}

View File

@ -0,0 +1,73 @@
package net.hostsharing.hsadminng.hs.office.scenarios;
import org.junit.jupiter.api.Test;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
class TemplateResolverUnitTest {
@Test
void resolveTemplate() {
final var resolved = new TemplateResolver("""
with optional JSON quotes:
${boolean},
${numeric},
${simple placeholder},
${nested %{name}},
${with-special-chars}
and without quotes:
%{boolean},
%{numeric},
%{simple placeholder},
%{nested %{name}},
%{with-special-chars}
and uri-encoded:
&{boolean},
&{numeric},
&{simple placeholder},
&{nested %{name}},
&{with-special-chars}
""",
Map.ofEntries(
Map.entry("name", "placeholder"),
Map.entry("boolean", true),
Map.entry("numeric", 42),
Map.entry("simple placeholder", "einfach"),
Map.entry("nested placeholder", "verschachtelt"),
Map.entry("with-special-chars", "3&3 AG")
)).resolve();
assertThat(resolved).isEqualTo("""
with optional JSON quotes:
true,
42,
"einfach",
"verschachtelt",
"3&3 AG"
and without quotes:
true,
42,
einfach,
verschachtelt,
3&3 AG
and uri-encoded:
true,
42,
einfach,
verschachtelt,
3%263+AG
""");
}
}

View File

@ -120,7 +120,8 @@ public abstract class UseCase<T extends UseCase<?>> {
}
@SneakyThrows
public final HttpResponse httpGet(final String uriPath) {
public final HttpResponse httpGet(final String uriPathWithPlaceholders) {
final var uriPath = ScenarioTest.resolve(uriPathWithPlaceholders);
final var request = HttpRequest.newBuilder()
.GET()
.uri(new URI("http://localhost:" + testSuite.port + uriPath))
@ -132,7 +133,8 @@ public abstract class UseCase<T extends UseCase<?>> {
}
@SneakyThrows
public final HttpResponse httpPost(final String uriPath, final JsonTemplate bodyJsonTemplate) {
public final HttpResponse httpPost(final String uriPathWithPlaceholders, final JsonTemplate bodyJsonTemplate) {
final var uriPath = ScenarioTest.resolve(uriPathWithPlaceholders);
final var requestBody = bodyJsonTemplate.resolvePlaceholders();
final var request = HttpRequest.newBuilder()
.POST(BodyPublishers.ofString(requestBody))
@ -146,7 +148,8 @@ public abstract class UseCase<T extends UseCase<?>> {
}
@SneakyThrows
public final HttpResponse httpPatch(final String uriPath, final JsonTemplate bodyJsonTemplate) {
public final HttpResponse httpPatch(final String uriPathWithPlaceholders, final JsonTemplate bodyJsonTemplate) {
final var uriPath = ScenarioTest.resolve(uriPathWithPlaceholders);
final var requestBody = bodyJsonTemplate.resolvePlaceholders();
final var request = HttpRequest.newBuilder()
.method(HttpMethod.PATCH.toString(), BodyPublishers.ofString(requestBody))
@ -160,7 +163,8 @@ public abstract class UseCase<T extends UseCase<?>> {
}
@SneakyThrows
public final HttpResponse httpDelete(final String uriPath) {
public final HttpResponse httpDelete(final String uriPathWithPlaceholders) {
final var uriPath = ScenarioTest.resolve(uriPathWithPlaceholders);
final var request = HttpRequest.newBuilder()
.DELETE()
.uri(new URI("http://localhost:" + testSuite.port + uriPath))