diff --git a/src/main/java/edu/kit/provideq/toolbox/api/SolutionRouter.java b/src/main/java/edu/kit/provideq/toolbox/api/SolutionRouter.java new file mode 100644 index 00000000..7dc1e0a7 --- /dev/null +++ b/src/main/java/edu/kit/provideq/toolbox/api/SolutionRouter.java @@ -0,0 +1,88 @@ +package edu.kit.provideq.toolbox.api; + +import static org.springdoc.core.fn.builders.apiresponse.Builder.responseBuilder; +import static org.springdoc.core.fn.builders.parameter.Builder.parameterBuilder; +import static org.springdoc.webflux.core.fn.SpringdocRouteBuilder.route; +import static org.springframework.http.MediaType.APPLICATION_JSON; +import static org.springframework.web.reactive.function.server.RequestPredicates.accept; +import static org.springframework.web.reactive.function.server.ServerResponse.ok; + +import edu.kit.provideq.toolbox.MetaSolverProvider; +import edu.kit.provideq.toolbox.Solution; +import edu.kit.provideq.toolbox.meta.MetaSolver; +import edu.kit.provideq.toolbox.meta.ProblemType; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import org.springdoc.core.fn.builders.operation.Builder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpStatus; +import org.springframework.web.reactive.config.EnableWebFlux; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; +import org.springframework.web.server.ResponseStatusException; +import reactor.core.publisher.Mono; + +/** + * This router handles getting the status of an existing solve request to + * the GET {@code /solution/{problemIndex}} endpoints. + * Requests are validated and relayed to the corresponding {@link MetaSolver}. + */ +@Configuration +@EnableWebFlux +public class SolutionRouter { + private final MetaSolverProvider metaSolverProvider; + + public SolutionRouter(MetaSolverProvider metaSolverProvider) { + this.metaSolverProvider = metaSolverProvider; + } + + @Bean + RouterFunction getSolutionRoutes() { + return metaSolverProvider.getMetaSolvers().stream() + .map(this::defineSolutionRouteForMetaSolver) + .reduce(RouterFunction::and) + .orElseThrow(); // we should always have at least one route or the toolbox is useless + } + + private RouterFunction defineSolutionRouteForMetaSolver( + MetaSolver metaSolver) { + var problemType = metaSolver.getProblemType(); + return route().GET( + getSolutionRouteForProblemType(problemType), + accept(APPLICATION_JSON), + req -> handleSolutionRouteForMetaSolver(metaSolver, req), + ops -> handleSolversRouteDocumentation(ops, problemType) + ).build(); + } + + private void handleSolversRouteDocumentation(Builder ops, ProblemType problemType) { + ops + .operationId(getSolutionRouteForProblemType(problemType)) + .tag(problemType.getId()) + .parameter(parameterBuilder().in(ParameterIn.QUERY).name("id")) + .response(responseBuilder() + .responseCode(String.valueOf(HttpStatus.OK.value())) + .implementation(Solution.class)) + .response(responseBuilder() + .responseCode(String.valueOf(HttpStatus.NOT_FOUND.value()))); + } + + private Mono handleSolutionRouteForMetaSolver( + MetaSolver metaSolver, ServerRequest req) { + var solution = req.queryParam("id") + .map(Long::parseLong) + .map(solutionId -> metaSolver.getSolutionManager().getSolution(solutionId)) + .map(Solution::toStringSolution) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, + "Could not find a solution for this problem with this solution id!")); + + return ok().body(Mono.just(solution), new ParameterizedTypeReference<>() { + }); + } + + private String getSolutionRouteForProblemType(ProblemType type) { + return "/solution/" + type.getId(); + } +} diff --git a/src/main/java/edu/kit/provideq/toolbox/api/SolveRouter.java b/src/main/java/edu/kit/provideq/toolbox/api/SolveRouter.java index 18fded04..04b12a2f 100644 --- a/src/main/java/edu/kit/provideq/toolbox/api/SolveRouter.java +++ b/src/main/java/edu/kit/provideq/toolbox/api/SolveRouter.java @@ -3,7 +3,6 @@ import static org.springdoc.core.fn.builders.apiresponse.Builder.responseBuilder; import static org.springdoc.core.fn.builders.arrayschema.Builder.arraySchemaBuilder; import static org.springdoc.core.fn.builders.content.Builder.contentBuilder; -import static org.springdoc.core.fn.builders.parameter.Builder.parameterBuilder; import static org.springdoc.core.fn.builders.requestbody.Builder.requestBodyBuilder; import static org.springdoc.core.fn.builders.schema.Builder.schemaBuilder; import static org.springdoc.webflux.core.fn.SpringdocRouteBuilder.route; @@ -21,7 +20,6 @@ import edu.kit.provideq.toolbox.meta.ProblemSolver; import edu.kit.provideq.toolbox.meta.ProblemType; import edu.kit.provideq.toolbox.meta.SubRoutineDefinition; -import io.swagger.v3.oas.annotations.enums.ParameterIn; import java.util.function.Consumer; import java.util.stream.Collectors; import org.springdoc.core.fn.builders.operation.Builder; @@ -36,7 +34,6 @@ import org.springframework.web.reactive.function.server.RouterFunction; import org.springframework.web.reactive.function.server.ServerRequest; import org.springframework.web.reactive.function.server.ServerResponse; -import org.springframework.web.server.ResponseStatusException; import org.springframework.web.server.ServerWebInputException; import reactor.core.publisher.Mono; @@ -93,47 +90,6 @@ private void validate(SolveRequest request) { } } - @Bean - RouterFunction getSolutionRoutes() { - return metaSolverProvider.getMetaSolvers().stream() - .map(this::defineSolutionRouteForMetaSolver) - .reduce(RouterFunction::and) - .orElseThrow(); // we should always have at least one route or the toolbox is useless - } - - private RouterFunction defineSolutionRouteForMetaSolver( - MetaSolver metaSolver) { - var problemType = metaSolver.getProblemType(); - return route().GET( - // FIXME this is intentionally SOLVE instead of SOLUTION to avoid breaking things - // but maybe we should switch the name at some point - getSolveRouteForProblemType(problemType), - accept(APPLICATION_JSON), - req -> handleSolutionRouteForMetaSolver(metaSolver, req), - ops -> ops - .operationId(getSolutionRouteForProblemType(problemType)) - .tag(problemType.getId()) - .parameter(parameterBuilder().in(ParameterIn.QUERY).name("id")) - .response(responseBuilder() - .responseCode(String.valueOf(HttpStatus.OK.value())) - .implementation(Solution.class) - ) - ).build(); - } - - private Mono handleSolutionRouteForMetaSolver(MetaSolver metaSolver, - ServerRequest req) { - var solution = req.queryParam("id") - .map(Long::parseLong) - .map(solutionId -> metaSolver.getSolutionManager().getSolution(solutionId)) - .map(Solution::toStringSolution) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, - "Could not find a solution for this problem with this solution id!")); - - return ok().body(Mono.just(solution), new ParameterizedTypeReference<>() { - }); - } - private void handleRouteDocumentation(MetaSolver metaSolver, Builder ops) { var problemType = metaSolver.getProblemType(); ops @@ -262,8 +218,4 @@ private org.springdoc.core.fn.builders.content.Builder getRequestContent( private String getSolveRouteForProblemType(ProblemType type) { return "/solve/" + type.getId(); } - - private String getSolutionRouteForProblemType(ProblemType type) { - return "/solution/" + type.getId(); - } }