feature/undertow #4
2 changed files with 65 additions and 23 deletions
|
@ -56,6 +56,7 @@ public enum OutputType {
|
|||
private final Function<Labyrinth, byte[]> render;
|
||||
@Getter
|
||||
private final boolean attachment;
|
||||
@Getter
|
||||
@NonNull
|
||||
private final List<String> names;
|
||||
|
||||
|
|
|
@ -17,9 +17,11 @@ 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;
|
||||
|
@ -27,6 +29,7 @@ import java.time.temporal.ChronoUnit;
|
|||
import java.util.Deque;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.function.Function;
|
||||
|
||||
@Slf4j
|
||||
public class CreateHandler extends AbstractHttpHandler {
|
||||
|
@ -39,8 +42,10 @@ public class CreateHandler extends AbstractHttpHandler {
|
|||
this.createLabyrinthFromRequestParameters(exchange.getQueryParameters())
|
||||
.onFailure(e -> {
|
||||
log.error("Error creating Labyrinth from request", e);
|
||||
exchange.setStatusCode(StatusCodes.INTERNAL_SERVER_ERROR);
|
||||
exchange.setReasonPhrase(StatusCodes.INTERNAL_SERVER_ERROR_STRING);
|
||||
exchange.setStatusCode(StatusCodes.INTERNAL_SERVER_ERROR)
|
||||
.setReasonPhrase(StatusCodes.INTERNAL_SERVER_ERROR_STRING)
|
||||
.getResponseSender()
|
||||
.send(e.getMessage());
|
||||
})
|
||||
.forEach(tuple -> {
|
||||
final OutputType outputType = tuple._1();
|
||||
|
@ -50,30 +55,47 @@ public class CreateHandler extends AbstractHttpHandler {
|
|||
bytes = outputType.render(labyrinth);
|
||||
} catch (@NonNull final Exception e) {
|
||||
log.error("Error rendering Labyrinth", e);
|
||||
exchange.setStatusCode(StatusCodes.INTERNAL_SERVER_ERROR);
|
||||
exchange.setReasonPhrase(StatusCodes.INTERNAL_SERVER_ERROR_STRING);
|
||||
exchange.setStatusCode(StatusCodes.INTERNAL_SERVER_ERROR)
|
||||
.setReasonPhrase(StatusCodes.INTERNAL_SERVER_ERROR_STRING)
|
||||
.getResponseSender()
|
||||
.send("Error creating the Labyrinth. Please contact the administrator. Request id=%s".formatted(MDC.get("correlationId")));
|
||||
return;
|
||||
}
|
||||
final long durationMillis = start.until(Instant.now(), ChronoUnit.MILLIS);
|
||||
exchange.getResponseHeaders()
|
||||
.put(Headers.CONTENT_TYPE, outputType.getContentType())
|
||||
.put(HttpString.tryFromString("X-Labyrinth-ID"), String.valueOf(labyrinth.getRandomSeed()))
|
||||
.put(HttpString.tryFromString("X-Labyrinth-Width"), String.valueOf(labyrinth.getWidth()))
|
||||
.put(HttpString.tryFromString("X-Labyrinth-Height"), String.valueOf(labyrinth.getHeight()))
|
||||
.put(HttpString.tryFromString("X-Labyrinth-Generation-Duration-millis"), String.valueOf(start.until(Instant.now(), ChronoUnit.MILLIS)));
|
||||
.put(HttpString.tryFromString("X-Labyrinth-Generation-Duration-millis"), String.valueOf(durationMillis));
|
||||
exchange.getResponseSender().send(ByteBuffer.wrap(bytes));
|
||||
log.debug("Create request handled.");
|
||||
log.debug("Create request handled in {}ms.", durationMillis);
|
||||
});
|
||||
}
|
||||
|
||||
private @NonNull Try<Tuple2<OutputType, Labyrinth>> createLabyrinthFromRequestParameters(final Map<String, Deque<String>> queryParameters) {
|
||||
@NonNull
|
||||
private Try<Tuple2<OutputType, Labyrinth>> createLabyrinthFromRequestParameters(final Map<String, Deque<String>> queryParameters) {
|
||||
return new ParametersToLabyrinthExtractor(queryParameters).createLabyrinth();
|
||||
}
|
||||
|
||||
private enum RequestParameter {
|
||||
WIDTH("w", "width"), HEIGHT("h", "height"), ID("i", "id"), OUTPUT("o", "output");
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -83,6 +105,10 @@ public class CreateHandler extends AbstractHttpHandler {
|
|||
}
|
||||
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 {
|
||||
|
@ -98,22 +124,37 @@ public class CreateHandler extends AbstractHttpHandler {
|
|||
|
||||
@NonNull
|
||||
Try<Tuple2<OutputType, Labyrinth>> createLabyrinth() {
|
||||
final Option<OutputType> output = getParameterValues(RequestParameter.OUTPUT)
|
||||
.foldLeft(Option.none(), (type, param) -> type.orElse(() -> OutputType.ofString(param)));
|
||||
final Option<Integer> width = getParameterValues(RequestParameter.WIDTH)
|
||||
.foldLeft(Option.none(), (value, param) -> value.orElse(() -> Try.of(() -> Integer.parseInt(param)).toOption()));
|
||||
final Option<Integer> height = getParameterValues(RequestParameter.HEIGHT)
|
||||
.foldLeft(Option.none(), (value, param) -> value.orElse(() -> Try.of(() -> Integer.parseInt(param)).toOption()));
|
||||
final Option<Long> id = getParameterValues(RequestParameter.ID)
|
||||
.foldLeft(Option.none(), (value, param) -> value.orElse(() -> Try.of(() -> Long.parseLong(param)).toOption()));
|
||||
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);
|
||||
|
||||
return Try.of(() -> {
|
||||
final OutputType t1 = output.get();
|
||||
final Integer width1 = width.get();
|
||||
final Integer height1 = height.get();
|
||||
final Long orElse = id.getOrElse(() -> new Random().nextLong());
|
||||
return Tuple.of(t1, new Labyrinth(width1, height1, orElse));
|
||||
});
|
||||
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
|
||||
|
|
Loading…
Reference in a new issue