-
Notifications
You must be signed in to change notification settings - Fork 34
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1516 from jcarranzan/vertx-web-validation
Implement Vert.x web validation module
- Loading branch information
Showing
8 changed files
with
412 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
33 changes: 33 additions & 0 deletions
33
http/vertx-web-validation/src/main/java/io/quarkus/ts/vertx/web/validation/ShopResource.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
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 static List<ShoppingList> shoppingList = createSampleProductList(); | ||
|
||
private static 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() { | ||
return shoppingList; | ||
} | ||
|
||
} |
60 changes: 60 additions & 0 deletions
60
http/vertx-web-validation/src/main/java/io/quarkus/ts/vertx/web/validation/ShoppingList.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()); | ||
} | ||
} |
153 changes: 153 additions & 0 deletions
153
...alidation/src/main/java/io/quarkus/ts/vertx/web/validation/ValidationHandlerOnRoutes.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
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.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.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; | ||
|
||
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 static ShopResource shopResource = new 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() { | ||
schemaParser = createSchema(); | ||
} | ||
|
||
private SchemaParser createSchema() { | ||
schemaRouter = SchemaRouter.create(vertx, new SchemaRouterOptions()); | ||
schemaParser = SchemaParser.createDraft7SchemaParser(schemaRouter); | ||
return schemaParser; | ||
} | ||
|
||
public void validateHandlerShoppingList(@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)); | ||
} else { | ||
routingContext.next(); | ||
} | ||
|
||
}); | ||
// 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); | ||
} | ||
|
||
} |
7 changes: 7 additions & 0 deletions
7
...ation/src/test/java/io/quarkus/ts/vertx/web/validation/OpenShiftVertxWebValidationIT.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 { | ||
} |
Oops, something went wrong.