Refactoring.
This commit is contained in:
parent
741894163b
commit
a194e5db62
5 changed files with 123 additions and 105 deletions
6
pom.xml
6
pom.xml
|
@ -20,9 +20,11 @@
|
|||
<java.target.version>17</java.target.version>
|
||||
<jetbrains-annotations.version>24.0.1</jetbrains-annotations.version>
|
||||
<junit-jupiter.version>5.9.2</junit-jupiter.version>
|
||||
<labyrinth-generator.version>0.0.3</labyrinth-generator.version>
|
||||
<logback.version>1.4.6</logback.version>
|
||||
<lombok.version>1.18.26</lombok.version>
|
||||
<slf4j.version>2.0.7</slf4j.version>
|
||||
<undertow.version>2.3.5.Final</undertow.version>
|
||||
<vavr.version>0.10.4</vavr.version>
|
||||
</properties>
|
||||
|
||||
|
@ -46,7 +48,7 @@
|
|||
<dependency>
|
||||
<groupId>ch.fritteli.labyrinth</groupId>
|
||||
<artifactId>labyrinth-generator</artifactId>
|
||||
<version>0.0.3</version>
|
||||
<version>${labyrinth-generator.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.vavr</groupId>
|
||||
|
@ -73,7 +75,7 @@
|
|||
<dependency>
|
||||
<groupId>io.undertow</groupId>
|
||||
<artifactId>undertow-core</artifactId>
|
||||
<version>2.3.5.Final</version>
|
||||
<version>${undertow.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
package ch.fritteli.labyrinth.server;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
@UtilityClass
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
LabyrinthServer.createAndStartServer()
|
||||
.onFailure(e -> log.error("Failed to create server. Stopping.", e));
|
||||
|
|
|
@ -88,7 +88,7 @@ public enum OutputType {
|
|||
}
|
||||
|
||||
@NonNull
|
||||
public byte[] render(@NonNull final Labyrinth labyrinth) throws Exception {
|
||||
public byte[] render(@NonNull final Labyrinth labyrinth) {
|
||||
return this.render.apply(labyrinth);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,33 +6,20 @@ import io.undertow.server.HttpServerExchange;
|
|||
import io.undertow.util.Headers;
|
||||
import io.undertow.util.HttpString;
|
||||
import io.undertow.util.StatusCodes;
|
||||
import io.vavr.Tuple;
|
||||
import io.vavr.Tuple2;
|
||||
import io.vavr.collection.HashMap;
|
||||
import io.vavr.collection.HashMultimap;
|
||||
import io.vavr.collection.HashSet;
|
||||
import io.vavr.collection.List;
|
||||
import io.vavr.collection.Multimap;
|
||||
import io.vavr.collection.Set;
|
||||
import io.vavr.collection.Stream;
|
||||
import io.vavr.control.Option;
|
||||
import io.vavr.control.Try;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.MDC;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Deque;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.function.Function;
|
||||
import lombok.NonNull;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.slf4j.MDC;
|
||||
|
||||
@Slf4j
|
||||
public class CreateHandler extends AbstractHttpHandler {
|
||||
|
||||
public static final String PATH_TEMPLATE = "/create/{output}";
|
||||
|
||||
@Override
|
||||
|
@ -77,89 +64,4 @@ public class CreateHandler extends AbstractHttpHandler {
|
|||
private Try<Tuple2<OutputType, Labyrinth>> createLabyrinthFromRequestParameters(final Map<String, Deque<String>> queryParameters) {
|
||||
return new ParametersToLabyrinthExtractor(queryParameters).createLabyrinth();
|
||||
}
|
||||
|
||||
private enum RequestParameter {
|
||||
WIDTH(p -> Try.of(() -> Integer.parseInt(p)).toOption(), "w", "width"),
|
||||
HEIGHT(p -> Try.of(() -> Integer.parseInt(p)).toOption(), "h", "height"),
|
||||
ID(p -> Try.of(() -> Long.parseLong(p)).toOption(), "i", "id"),
|
||||
OUTPUT(OutputType::ofString, "o", "output");
|
||||
@NonNull
|
||||
private final Function<String, Option<?>> extractor;
|
||||
@Getter
|
||||
@NonNull
|
||||
private final Set<String> names;
|
||||
|
||||
RequestParameter(@NonNull final String... names) {
|
||||
this.extractor = null;
|
||||
this.names = HashSet.of(names);
|
||||
}
|
||||
|
||||
RequestParameter(@NonNull final Function<String, Option<?>> extractor, @NonNull final String... names) {
|
||||
this.extractor = extractor;
|
||||
this.names = HashSet.of(names);
|
||||
}
|
||||
|
||||
static Option<RequestParameter> parseName(@Nullable final String name) {
|
||||
if (name == null) {
|
||||
return Option.none();
|
||||
}
|
||||
return Stream.of(values()).find(param -> param.names.exists(name::equalsIgnoreCase));
|
||||
}
|
||||
|
||||
@NonNull <T> Option<T> extractParameterValue(@NonNull final String parameter) {
|
||||
return (Option<T>) this.extractor.apply(parameter);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ParametersToLabyrinthExtractor {
|
||||
@NonNull
|
||||
private final Multimap<String, String> queryParameters;
|
||||
|
||||
ParametersToLabyrinthExtractor(@NonNull final Map<String, Deque<String>> queryParameters) {
|
||||
this.queryParameters = HashMap.ofAll(queryParameters).foldLeft(HashMultimap.<String>withSet().empty(), (map, tuple) -> {
|
||||
final String key = tuple._1();
|
||||
return Stream.ofAll(tuple._2()).foldLeft(map, (m, value) -> m.put(key, value));
|
||||
});
|
||||
}
|
||||
|
||||
@NonNull
|
||||
Try<Tuple2<OutputType, Labyrinth>> createLabyrinth() {
|
||||
final Option<OutputType> output = getParameterValue(RequestParameter.OUTPUT);
|
||||
final Option<Integer> width = getParameterValue(RequestParameter.WIDTH);
|
||||
final Option<Integer> height = getParameterValue(RequestParameter.HEIGHT);
|
||||
final Option<Long> id = getParameterValue(RequestParameter.ID);
|
||||
|
||||
if (output.isEmpty()) {
|
||||
return Try.failure(new IllegalArgumentException("Path parameter %s is required and must be one of: %s".formatted(
|
||||
RequestParameter.OUTPUT.getNames().mkString("'", " / ", "'"),
|
||||
Stream.of(OutputType.values())
|
||||
.flatMap(OutputType::getNames)
|
||||
.mkString(", ")
|
||||
)));
|
||||
}
|
||||
if (width.isEmpty()) {
|
||||
return Try.failure(new IllegalArgumentException("Query parameter %s is required and must be a positive integer value".formatted(
|
||||
RequestParameter.WIDTH.getNames().mkString("'", " / ", "'")
|
||||
)));
|
||||
}
|
||||
if (height.isEmpty()) {
|
||||
return Try.failure(new IllegalArgumentException("Query parameter %s is required and must be a positive integer value".formatted(
|
||||
RequestParameter.HEIGHT.getNames().mkString("'", " / ", "'")
|
||||
)));
|
||||
}
|
||||
|
||||
return Try.of(() -> Tuple.of(output.get(), new Labyrinth(width.get(), height.get(), id.getOrElse(() -> new Random().nextLong()))));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private <T> Option<T> getParameterValue(@NonNull final RequestParameter parameter) {
|
||||
return this.getParameterValues(parameter)
|
||||
.foldLeft(Option.none(), (type, param) -> type.orElse(() -> parameter.extractParameterValue(param)));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Stream<String> getParameterValues(@NonNull final RequestParameter parameter) {
|
||||
return parameter.names.toStream().flatMap(name -> Stream.ofAll(this.queryParameters.getOrElse(name, List.empty())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
package ch.fritteli.labyrinth.server.handler;
|
||||
|
||||
import ch.fritteli.labyrinth.generator.model.Labyrinth;
|
||||
import ch.fritteli.labyrinth.server.OutputType;
|
||||
import io.vavr.Tuple;
|
||||
import io.vavr.Tuple2;
|
||||
import io.vavr.collection.HashMap;
|
||||
import io.vavr.collection.HashMultimap;
|
||||
import io.vavr.collection.HashSet;
|
||||
import io.vavr.collection.List;
|
||||
import io.vavr.collection.Multimap;
|
||||
import io.vavr.collection.Set;
|
||||
import io.vavr.collection.Stream;
|
||||
import io.vavr.control.Option;
|
||||
import io.vavr.control.Try;
|
||||
import java.util.Deque;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.function.Function;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
class ParametersToLabyrinthExtractor {
|
||||
|
||||
@NonNull
|
||||
private final Multimap<RequestParameter, ?> queryParameters;
|
||||
|
||||
ParametersToLabyrinthExtractor(@NonNull final Map<String, Deque<String>> queryParameters) {
|
||||
this.queryParameters = HashMap.ofAll(queryParameters)
|
||||
.foldLeft(
|
||||
HashMultimap.withSet().empty(),
|
||||
(map, tuple) -> RequestParameter.parseName(tuple._1()).map(parameter -> Stream.ofAll(tuple._2())
|
||||
.flatMap(parameter::extractParameterValue)
|
||||
.foldLeft(map, (m, value) -> m.put(parameter, value)))
|
||||
.getOrElse(map)
|
||||
);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
Try<Tuple2<OutputType, Labyrinth>> createLabyrinth() {
|
||||
final Option<OutputType> output = getParameterValue(RequestParameter.OUTPUT);
|
||||
final Option<Integer> width = getParameterValue(RequestParameter.WIDTH);
|
||||
final Option<Integer> height = getParameterValue(RequestParameter.HEIGHT);
|
||||
final Option<Long> id = getParameterValue(RequestParameter.ID);
|
||||
|
||||
if (output.isEmpty()) {
|
||||
return Try.failure(new IllegalArgumentException("Path parameter %s is required and must be one of: %s".formatted(
|
||||
RequestParameter.OUTPUT.getNames().mkString("'", " / ", "'"),
|
||||
Stream.of(OutputType.values())
|
||||
.flatMap(OutputType::getNames)
|
||||
.mkString(", ")
|
||||
)));
|
||||
}
|
||||
if (width.isEmpty()) {
|
||||
return Try.failure(new IllegalArgumentException("Query parameter %s is required and must be a positive integer value".formatted(
|
||||
RequestParameter.WIDTH.getNames().mkString("'", " / ", "'")
|
||||
)));
|
||||
}
|
||||
if (height.isEmpty()) {
|
||||
return Try.failure(new IllegalArgumentException("Query parameter %s is required and must be a positive integer value".formatted(
|
||||
RequestParameter.HEIGHT.getNames().mkString("'", " / ", "'")
|
||||
)));
|
||||
}
|
||||
|
||||
return Try.of(() -> Tuple.of(output.get(), new Labyrinth(width.get(), height.get(), id.getOrElse(() -> new Random().nextLong()))));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private <T> Option<T> getParameterValue(@NonNull final RequestParameter parameter) {
|
||||
return (Option<T>) this.queryParameters.getOrElse(parameter, List.empty())
|
||||
.headOption();
|
||||
}
|
||||
|
||||
@Slf4j
|
||||
private enum RequestParameter {
|
||||
WIDTH(p -> Try.of(() -> Integer.parseInt(p))
|
||||
.toOption()
|
||||
.onEmpty(() -> log.debug("Unparseable value for parameter 'width': '{}'", p)), "w", "width"),
|
||||
HEIGHT(p -> Try.of(() -> Integer.parseInt(p))
|
||||
.toOption()
|
||||
.onEmpty(() -> log.debug("Unparseable value for parameter 'height': '{}'", p)), "h", "height"),
|
||||
ID(p -> Try.of(() -> Long.parseLong(p))
|
||||
.toOption()
|
||||
.onEmpty(() -> log.debug("Unparseable value for parameter 'id': '{}'", p)), "i", "id"),
|
||||
OUTPUT(p -> OutputType.ofString(p)
|
||||
.onEmpty(() -> log.debug("Unparseable value for parameter 'output': '{}'", p)), "o", "output");
|
||||
@NonNull
|
||||
private final Function<String, Option<?>> extractor;
|
||||
@Getter
|
||||
@NonNull
|
||||
private final Set<String> names;
|
||||
|
||||
RequestParameter(@NonNull final Function<String, Option<?>> extractor, @NonNull final String... names) {
|
||||
this.extractor = extractor;
|
||||
this.names = HashSet.of(names);
|
||||
}
|
||||
|
||||
static Option<RequestParameter> parseName(@Nullable final String name) {
|
||||
if (name == null) {
|
||||
return Option.none();
|
||||
}
|
||||
return Stream.of(values()).find(param -> param.names.exists(name::equalsIgnoreCase));
|
||||
}
|
||||
|
||||
@NonNull Option<?> extractParameterValue(@NonNull final String parameter) {
|
||||
return this.extractor.apply(parameter);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue