Skip to content

Commit bbae004

Browse files
authored
BAEL-9433 Example code for constraint validation (#18790)
1 parent 1c4d035 commit bbae004

18 files changed

+580
-0
lines changed

spring-boot-modules/spring-boot-validation/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@
3030
<groupId>com.h2database</groupId>
3131
<artifactId>h2</artifactId>
3232
</dependency>
33+
34+
<dependency>
35+
<groupId>org.assertj</groupId>
36+
<artifactId>assertj-core</artifactId>
37+
<scope>test</scope>
38+
</dependency>
3339
</dependencies>
3440

3541
<build>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.baeldung.customstatefulvalidation;
2+
3+
import org.springframework.boot.SpringApplication;
4+
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
6+
7+
@SpringBootApplication
8+
@ConfigurationPropertiesScan
9+
public class Application {
10+
11+
public static void main(String[] args) {
12+
SpringApplication.run(Application.class, args);
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.baeldung.customstatefulvalidation.configuration;
2+
3+
import org.springframework.boot.context.properties.ConfigurationProperties;
4+
5+
@ConfigurationProperties("com.baeldung.tenant")
6+
public class TenantChannels {
7+
private String[] channels;
8+
9+
public String[] getChannels() {
10+
return channels;
11+
}
12+
13+
public void setChannels(String[] channels) {
14+
this.channels = channels;
15+
}
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.baeldung.customstatefulvalidation.controllers;
2+
3+
import com.baeldung.customstatefulvalidation.model.PurchaseOrderItem;
4+
import jakarta.validation.Valid;
5+
import org.springframework.http.ResponseEntity;
6+
import org.springframework.web.bind.annotation.PostMapping;
7+
import org.springframework.web.bind.annotation.RequestBody;
8+
import org.springframework.web.bind.annotation.RestController;
9+
10+
@RestController
11+
public class PurchaseOrderController {
12+
13+
@PostMapping("/api/purchasing/")
14+
public ResponseEntity<String> createPurchaseOrder(@Valid @RequestBody PurchaseOrderItem item) {
15+
// start processing this purchase order and tell the caller we've accepted it
16+
17+
return ResponseEntity.accepted().build();
18+
}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package com.baeldung.customstatefulvalidation.model;
2+
3+
import com.baeldung.customstatefulvalidation.validators.AvailableChannel;
4+
import com.baeldung.customstatefulvalidation.validators.AvailableWarehouseRoute;
5+
import com.baeldung.customstatefulvalidation.validators.ChoosePacksOrIndividuals;
6+
import com.baeldung.customstatefulvalidation.validators.ProductCheckDigit;
7+
import jakarta.validation.constraints.NotNull;
8+
import jakarta.validation.constraints.Pattern;
9+
10+
@ChoosePacksOrIndividuals
11+
@AvailableWarehouseRoute
12+
public class PurchaseOrderItem {
13+
14+
@ProductCheckDigit
15+
@NotNull
16+
@Pattern(regexp = "A-\\d{8}-\\d")
17+
private String productId;
18+
19+
private String sourceWarehouse;
20+
private String destinationCountry;
21+
22+
@AvailableChannel
23+
private String tenantChannel;
24+
25+
private int numberOfIndividuals;
26+
private int numberOfPacks;
27+
private int itemsPerPack;
28+
29+
@org.hibernate.validator.constraints.UUID
30+
private String clientUuid;
31+
32+
public String getProductId() {
33+
return productId;
34+
}
35+
36+
public void setProductId(String productId) {
37+
this.productId = productId;
38+
}
39+
40+
public String getSourceWarehouse() {
41+
return sourceWarehouse;
42+
}
43+
44+
public void setSourceWarehouse(String sourceWarehouse) {
45+
this.sourceWarehouse = sourceWarehouse;
46+
}
47+
48+
public String getDestinationCountry() {
49+
return destinationCountry;
50+
}
51+
52+
public void setDestinationCountry(String destinationCountry) {
53+
this.destinationCountry = destinationCountry;
54+
}
55+
56+
public String getTenantChannel() {
57+
return tenantChannel;
58+
}
59+
60+
public void setTenantChannel(String tenantChannel) {
61+
this.tenantChannel = tenantChannel;
62+
}
63+
64+
public int getNumberOfIndividuals() {
65+
return numberOfIndividuals;
66+
}
67+
68+
public void setNumberOfIndividuals(int numberOfIndividuals) {
69+
this.numberOfIndividuals = numberOfIndividuals;
70+
}
71+
72+
public int getNumberOfPacks() {
73+
return numberOfPacks;
74+
}
75+
76+
public void setNumberOfPacks(int numberOfPacks) {
77+
this.numberOfPacks = numberOfPacks;
78+
}
79+
80+
public int getItemsPerPack() {
81+
return itemsPerPack;
82+
}
83+
84+
public void setItemsPerPack(int itemsPerPack) {
85+
this.itemsPerPack = itemsPerPack;
86+
}
87+
88+
public String getClientUuid() {
89+
return clientUuid;
90+
}
91+
92+
public void setClientUuid(String clientUuid) {
93+
this.clientUuid = clientUuid;
94+
}
95+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.baeldung.customstatefulvalidation.repository;
2+
3+
import org.springframework.stereotype.Repository;
4+
5+
import java.util.Set;
6+
import java.util.stream.Stream;
7+
8+
import static java.util.stream.Collectors.toSet;
9+
10+
@Repository
11+
public class WarehouseRouteRepository {
12+
private Set<String> availableRoutes = Stream.of(
13+
"Springfield:USA",
14+
"Hartley:USA",
15+
"Gentoo:PL",
16+
"Mercury:GR")
17+
.collect(toSet());
18+
19+
public boolean isWarehouseRouteAvailable(String sourceWarehouse, String destinationCountry) {
20+
return availableRoutes.contains(sourceWarehouse + ":" + destinationCountry);
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.baeldung.customstatefulvalidation.validators;
2+
3+
import jakarta.validation.Constraint;
4+
import jakarta.validation.Payload;
5+
6+
import java.lang.annotation.ElementType;
7+
import java.lang.annotation.Retention;
8+
import java.lang.annotation.RetentionPolicy;
9+
import java.lang.annotation.Target;
10+
11+
@Constraint(validatedBy = AvailableChannelValidator.class)
12+
@Target({ ElementType.FIELD })
13+
@Retention(RetentionPolicy.RUNTIME)
14+
public @interface AvailableChannel {
15+
String message() default "must be available tenant channel";
16+
Class<?>[] groups() default {};
17+
Class<? extends Payload>[] payload() default {};
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.baeldung.customstatefulvalidation.validators;
2+
3+
import com.baeldung.customstatefulvalidation.configuration.TenantChannels;
4+
import jakarta.validation.ConstraintValidator;
5+
import jakarta.validation.ConstraintValidatorContext;
6+
import org.springframework.beans.factory.annotation.Autowired;
7+
8+
import java.util.Arrays;
9+
import java.util.Set;
10+
import java.util.stream.IntStream;
11+
12+
import static java.util.stream.Collectors.toSet;
13+
14+
public class AvailableChannelValidator implements ConstraintValidator<AvailableChannel, String> {
15+
16+
@Autowired
17+
private TenantChannels tenantChannels;
18+
19+
private Set<String> channels;
20+
21+
@Override
22+
public void initialize(AvailableChannel constraintAnnotation) {
23+
channels = Arrays.stream(tenantChannels.getChannels()).collect(toSet());
24+
}
25+
26+
@Override
27+
public boolean isValid(String value, ConstraintValidatorContext context) {
28+
return channels.contains(value);
29+
}
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.baeldung.customstatefulvalidation.validators;
2+
3+
import jakarta.validation.Constraint;
4+
import jakarta.validation.Payload;
5+
6+
import java.lang.annotation.ElementType;
7+
import java.lang.annotation.Retention;
8+
import java.lang.annotation.RetentionPolicy;
9+
import java.lang.annotation.Target;
10+
11+
@Constraint(validatedBy = AvailableWarehouseRouteValidator.class)
12+
@Target({ ElementType.TYPE })
13+
@Retention(RetentionPolicy.RUNTIME)
14+
public @interface AvailableWarehouseRoute {
15+
String message() default "chosen warehouse route must be active";
16+
Class<?>[] groups() default {};
17+
Class<? extends Payload>[] payload() default {};
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.baeldung.customstatefulvalidation.validators;
2+
3+
import com.baeldung.customstatefulvalidation.model.PurchaseOrderItem;
4+
import com.baeldung.customstatefulvalidation.repository.WarehouseRouteRepository;
5+
import jakarta.validation.ConstraintValidator;
6+
import jakarta.validation.ConstraintValidatorContext;
7+
import org.springframework.beans.factory.annotation.Autowired;
8+
9+
public class AvailableWarehouseRouteValidator implements ConstraintValidator<AvailableWarehouseRoute, PurchaseOrderItem> {
10+
@Autowired
11+
private WarehouseRouteRepository warehouseRouteRepository;
12+
13+
@Override
14+
public boolean isValid(PurchaseOrderItem value, ConstraintValidatorContext context) {
15+
return warehouseRouteRepository.isWarehouseRouteAvailable(value.getSourceWarehouse(), value.getDestinationCountry());
16+
}
17+
}

0 commit comments

Comments
 (0)