Skip to content

Commit

Permalink
create module vertx-web-validation
Browse files Browse the repository at this point in the history
making some progress

rename methods names,  modify router.get path and  price logic

modify SHOPLIS1_URL to d it simpler

include vertx-web-validation in README

pass @observes Router router to the validateHandlerSoppingList

remove reactive routes in pom , they are deprecated

improve readability in README

adapt method without @route reactive

improve tests

add handling error and test parameter missing in request

rm quarkus-resteasy-reactive dependency

use static final variables and include error tests validations

include /filterByArrayItem and /createShoppingList logic and test coverage and modify README

extend of VertxWebValidationIT on OpenshiftVertxWebValidationIT

rename OpenShift class and fix/rename failed RestService name for openshift deployment to lowercase valid
  • Loading branch information
jcarranzan committed Nov 15, 2023
1 parent 168efff commit a122069
Show file tree
Hide file tree
Showing 8 changed files with 502 additions and 81 deletions.
169 changes: 88 additions & 81 deletions README.md

Large diffs are not rendered by default.

27 changes: 27 additions & 0 deletions http/vertx-web-validation/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.quarkus.ts.qe</groupId>
<artifactId>parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../..</relativePath>
</parent>
<artifactId>vertx-web-validation</artifactId>
<packaging>jar</packaging>
<name>Quarkus QE TS: HTTP: Vert.x-Web-Validation</name>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web-validation</artifactId>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-json-schema</artifactId>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package io.quarkus.ts.vertx.web.validation;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

@Path("/shoppinglist")
@Produces(MediaType.APPLICATION_JSON)
public class ShopResource {

private List<ShoppingList> shoppingList;

private List<ShoppingList> createSampleProductList() {
shoppingList = new ArrayList<>();
shoppingList.add(new ShoppingList(UUID.randomUUID(), "ListName1", 25,
new ArrayList<>(Arrays.asList("Carrots", "Water", "Cheese", "Beer"))));
shoppingList.add(new ShoppingList(UUID.randomUUID(), "ListName2", 80,
new ArrayList<>(Arrays.asList("Meat", "Wine", "Almonds", "Potatoes", "Cake"))));
return shoppingList;
}

@GET
public List<ShoppingList> get() {
if (shoppingList == null) {
createSampleProductList();
}
return shoppingList;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package io.quarkus.ts.vertx.web.validation;

import java.util.ArrayList;
import java.util.UUID;

public class ShoppingList {

public UUID id;

public String name;

public ArrayList<String> products;

public double price;

public ShoppingList(UUID id, String name, double price, ArrayList<String> products) {
this.id = id;
this.name = name;
this.price = price;
this.products = products;
}

public ArrayList<String> getProducts() {
return products;
}

public void setProducts(ArrayList<String> products) {
this.products = products;
}

public UUID getId() {
return id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public double getPrice() {
return price;
}

public void setPrice(double price) {
this.price = price;
}

@Override
public String toString() {
return String.format(
"Shopping list{id=%s, name=%s, products=%s, price=%s}",
getId(),
getName(),
getProducts().toString(),
getPrice());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package io.quarkus.ts.vertx.web.validation;

import static io.vertx.ext.web.validation.builder.Parameters.param;
import static io.vertx.json.schema.common.dsl.Schemas.arraySchema;
import static io.vertx.json.schema.common.dsl.Schemas.numberSchema;
import static io.vertx.json.schema.common.dsl.Schemas.objectSchema;
import static io.vertx.json.schema.common.dsl.Schemas.stringSchema;
import static io.vertx.json.schema.draft7.dsl.Keywords.maximum;

import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes;
import jakarta.inject.Inject;
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.MediaType;

import io.netty.handler.codec.http.HttpResponseStatus;
import io.vertx.core.Vertx;
import io.vertx.core.json.Json;
import io.vertx.core.json.JsonArray;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.ext.web.validation.BadRequestException;
import io.vertx.ext.web.validation.BodyProcessorException;
import io.vertx.ext.web.validation.ParameterProcessorException;
import io.vertx.ext.web.validation.RequestParameters;
import io.vertx.ext.web.validation.RequestPredicate;
import io.vertx.ext.web.validation.RequestPredicateException;
import io.vertx.ext.web.validation.ValidationHandler;
import io.vertx.ext.web.validation.builder.Bodies;
import io.vertx.ext.web.validation.builder.Parameters;
import io.vertx.ext.web.validation.builder.ValidationHandlerBuilder;
import io.vertx.json.schema.SchemaParser;
import io.vertx.json.schema.SchemaRouter;
import io.vertx.json.schema.SchemaRouterOptions;
import io.vertx.json.schema.common.dsl.ObjectSchemaBuilder;

@ApplicationScoped
public class ValidationHandlerOnRoutes {
//TODO when Quarkus use vert.x version 4.4.6 we can use SchemaRepository instead of SchemaParser with SchemaRouter
//private SchemaRepository schemaRepository =SchemaRepository.create(new JsonSchemaOptions().setDraft(Draft.DRAFT7).setBaseUri(BASEURI));
private SchemaParser schemaParser;
private SchemaRouter schemaRouter;

@Inject
Vertx vertx;

private Router router;

@Inject
ShopResource shopResource;

private static final String ERROR_MESSAGE = "{\"error\": \"%s\"}";
private static final String SHOPPINGLIST_NOT_FOUND = "Shopping list not found in the list or does not exist with that name or price";

@PostConstruct
void initialize() {
router = Router.router(vertx);
router.route().handler(BodyHandler.create());
schemaParser = createSchema();
validateHandlerSoppingList(router);
}

private SchemaParser createSchema() {
schemaRouter = SchemaRouter.create(vertx, new SchemaRouterOptions());
schemaParser = SchemaParser.createDraft7SchemaParser(schemaRouter);
return schemaParser;
}

public void validateHandlerSoppingList(@Observes Router router) {
AtomicReference<String> queryAnswer = new AtomicReference<>();
router.get("/filterList")
.handler(ValidationHandlerBuilder
.create(schemaParser)
.queryParameter(param("shoppingListName", stringSchema()))
.queryParameter(param("shoppingListPrice", numberSchema().with(maximum(100)))).build())
.handler(routingContext -> {
RequestParameters parameters = routingContext.get(ValidationHandler.REQUEST_CONTEXT_KEY);
String shoppingListName = parameters.queryParameter("shoppingListName").getString();
Double totalPrice = parameters.queryParameter("shoppingListPrice").getDouble();

// Logic to list shoppingList based on shoppingListName and totalPrice
String shoppingListFound = fetchProductDetailsFromQuery(shoppingListName, totalPrice);
queryAnswer.set(shoppingListFound);

if (queryAnswer.get().equalsIgnoreCase(SHOPPINGLIST_NOT_FOUND)) {
routingContext.response().setStatusCode(HttpResponseStatus.NOT_FOUND.code());
}

routingContext.response().putHeader(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN).end(queryAnswer.get());
}).failureHandler(routingContext -> {
// Error handling:
if (routingContext.failure() instanceof BadRequestException ||
routingContext.failure() instanceof ParameterProcessorException ||
routingContext.failure() instanceof BodyProcessorException ||
routingContext.failure() instanceof RequestPredicateException) {

String errorMessage = routingContext.failure().toString();
routingContext.response()
.setStatusCode(HttpResponseStatus.BAD_REQUEST.code())
.putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
.end(String.format(ERROR_MESSAGE, errorMessage));
}
});
// Create a ValidationHandlerBuilder with explodedParam and arraySchema to filter by array items
ObjectSchemaBuilder bodySchemaBuilder = objectSchema()
.property("shoppingListName", stringSchema());
ValidationHandlerBuilder
.create(schemaParser)
.body(Bodies.json(bodySchemaBuilder));
router.get("/filterByArrayItem")
.handler(
ValidationHandlerBuilder
.create(schemaParser)
.queryParameter(Parameters.explodedParam("shoppingArray", arraySchema().items(stringSchema())))
.body(Bodies.json(bodySchemaBuilder))
.build())
.handler(routingContext -> {
RequestParameters parameters = routingContext.get(ValidationHandler.REQUEST_CONTEXT_KEY);
JsonArray myArray = parameters.queryParameter("shoppingArray").getJsonArray();
// Retrieve the list of all shoppingLists
List<ShoppingList> shoppingLists = fetchProductDetailsFromArrayQuery(myArray);

routingContext.response().putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
.end(Json.encodeToBuffer(shoppingLists));
});
// Let's allow to create a new item
router.post("/createShoppingList").handler(
ValidationHandlerBuilder
.create(schemaParser)
.predicate(RequestPredicate.BODY_REQUIRED)
.queryParameter(param("shoppingListName", stringSchema()))
.queryParameter(param("shoppingListPrice", numberSchema().with(maximum(100))))
.build())
.handler(routingContext -> {
routingContext.response().setStatusCode(HttpResponseStatus.OK.code()).end("Shopping list created");
});

}

public List<ShoppingList> fetchProductDetailsFromArrayQuery(JsonArray myArray) {
return shopResource.get().stream()
.filter(shoppingList -> myArray.contains(shoppingList.getName()))
.collect(Collectors.toList());
}

public String fetchProductDetailsFromQuery(String name, Double price) {
return shopResource.get().stream()
.filter(product -> name.equalsIgnoreCase(product.getName()) && price.equals(product.getPrice()))
.map(ShoppingList::toString)
.findFirst()
.orElse(SHOPPINGLIST_NOT_FOUND);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.quarkus.ts.vertx.web.validation;

import io.quarkus.test.scenarios.OpenShiftScenario;

@OpenShiftScenario
public class OpenShiftVertxWebValidationIT extends VertxWebValidationIT {
}
Loading

0 comments on commit a122069

Please sign in to comment.