diff --git a/boot-mongodb-elasticsearch/README.md b/boot-mongodb-elasticsearch/README.md
index fd6ce68a4..3e39226e1 100644
--- a/boot-mongodb-elasticsearch/README.md
+++ b/boot-mongodb-elasticsearch/README.md
@@ -158,8 +158,8 @@ docker compose -f docker/docker-compose.yml up -d
## Configuration Properties
Key application properties:
```properties
-spring.data.mongodb.database=mongoes
-spring.data.mongodb.uri=mongodb://localhost:27017/mongoes?replicaSet=rs0
+spring.mongodb.database=mongoes
+spring.mongodb.uri=mongodb://localhost:27017/mongoes?replicaSet=rs0&readPreference=primary&directConnection=true
spring.elasticsearch.uris=localhost:9200
spring.elasticsearch.socket-timeout=10s
```
diff --git a/boot-mongodb-elasticsearch/pom.xml b/boot-mongodb-elasticsearch/pom.xml
index 546c8c544..929c70e7f 100644
--- a/boot-mongodb-elasticsearch/pom.xml
+++ b/boot-mongodb-elasticsearch/pom.xml
@@ -6,7 +6,7 @@
org.springframework.boot
spring-boot-starter-parent
- 4.0.0-M1
+ 4.0.0
com.example.mongoes
@@ -48,7 +48,7 @@
org.springframework.boot
- spring-boot-starter-aop
+ spring-boot-starter-aspectj
org.springframework.boot
@@ -89,7 +89,7 @@
org.springframework.boot
- spring-boot-starter-test
+ spring-boot-starter-webflux-test
test
@@ -97,24 +97,19 @@
spring-boot-testcontainers
test
-
- io.projectreactor
- reactor-test
- test
-
org.testcontainers
- junit-jupiter
+ testcontainers-junit-jupiter
test
org.testcontainers
- mongodb
+ testcontainers-mongodb
test
org.testcontainers
- elasticsearch
+ testcontainers-elasticsearch
test
diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/config/GlobalExceptionHandler.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/config/GlobalExceptionHandler.java
index c3dae559f..68380e7c9 100644
--- a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/config/GlobalExceptionHandler.java
+++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/config/GlobalExceptionHandler.java
@@ -3,40 +3,50 @@
import com.example.mongoes.web.exception.DuplicateRestaurantException;
import com.example.mongoes.web.exception.RestaurantNotFoundException;
import jakarta.validation.ConstraintViolationException;
+import java.net.URI;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
-import org.springframework.http.HttpStatusCode;
+import org.jspecify.annotations.NonNull;
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+import org.springframework.http.HttpStatus;
import org.springframework.http.ProblemDetail;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.bind.support.WebExchangeBindException;
+import org.springframework.web.server.MissingRequestValueException;
import reactor.core.publisher.Mono;
@RestControllerAdvice
+@Order(Ordered.HIGHEST_PRECEDENCE)
public class GlobalExceptionHandler {
@ExceptionHandler(DuplicateRestaurantException.class)
- public Mono handleDuplicateRestaurantException(DuplicateRestaurantException ex) {
+ public Mono<@NonNull ProblemDetail> handleDuplicateRestaurantException(
+ DuplicateRestaurantException ex) {
ProblemDetail problemDetail =
ProblemDetail.forStatusAndDetail(ex.getHttpStatus(), ex.getMessage());
+ problemDetail.setType(URI.create("https://api.mongoes.com/errors/duplicate-restaurant"));
return Mono.just(problemDetail);
}
@ExceptionHandler(RestaurantNotFoundException.class)
- Mono handleRestaurantNotFoundException(RestaurantNotFoundException ex) {
+ Mono<@NonNull ProblemDetail> handleRestaurantNotFoundException(RestaurantNotFoundException ex) {
ProblemDetail problemDetail =
ProblemDetail.forStatusAndDetail(ex.getHttpStatus(), ex.getMessage());
+ problemDetail.setType(URI.create("https://api.mongoes.com/errors/restaurant-not-found"));
return Mono.just(problemDetail);
}
@ExceptionHandler(WebExchangeBindException.class)
- Mono handleValidationErrors(WebExchangeBindException ex) {
+ Mono<@NonNull ProblemDetail> handleValidationErrors(WebExchangeBindException ex) {
ProblemDetail problemDetail =
ProblemDetail.forStatusAndDetail(
- HttpStatusCode.valueOf(400), "Request failed validation checks.");
- problemDetail.setTitle("Constraint Violation");
+ HttpStatus.BAD_REQUEST, "Invalid request content.");
+ problemDetail.setTitle("Bad Request");
+ problemDetail.setType(URI.create("https://api.mongoes.com/errors/validation-error"));
List validationErrorsList =
ex.getAllErrors().stream()
.map(
@@ -56,11 +66,12 @@ Mono handleValidationErrors(WebExchangeBindException ex) {
}
@ExceptionHandler(ConstraintViolationException.class)
- Mono handleConstraintViolation(ConstraintViolationException ex) {
+ Mono<@NonNull ProblemDetail> handleConstraintViolation(ConstraintViolationException ex) {
ProblemDetail problemDetail =
- ProblemDetail.forStatusAndDetail(HttpStatusCode.valueOf(400), "Validation failed");
+ ProblemDetail.forStatusAndDetail(HttpStatus.BAD_REQUEST, "Validation failed");
problemDetail.setTitle("Constraint Violation");
+ problemDetail.setType(URI.create("https://api.mongoes.com/errors/validation-error"));
List validationErrorsList =
ex.getConstraintViolations().stream()
.map(
@@ -76,5 +87,13 @@ Mono handleConstraintViolation(ConstraintViolationException ex) {
return Mono.just(problemDetail);
}
+ @ExceptionHandler(MissingRequestValueException.class)
+ Mono<@NonNull ProblemDetail> handleException(MissingRequestValueException ex) {
+ ProblemDetail problemDetail =
+ ProblemDetail.forStatusAndDetail(ex.getStatusCode(), ex.getBody().getDetail());
+ problemDetail.setType(URI.create("https://api.mongoes.com/errors/validation-error"));
+ return Mono.just(problemDetail);
+ }
+
record ApiValidationError(String object, String field, Object rejectedValue, String message) {}
}
diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/config/WebFluxConfig.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/config/WebFluxConfig.java
index d57785cf1..ef0986567 100644
--- a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/config/WebFluxConfig.java
+++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/config/WebFluxConfig.java
@@ -1,8 +1,9 @@
package com.example.mongoes.config;
+import org.jspecify.annotations.NonNull;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import org.springframework.data.geo.GeoModule;
+import org.springframework.data.geo.GeoJacksonModule;
import org.springframework.web.reactive.config.CorsRegistry;
import org.springframework.web.reactive.config.WebFluxConfigurer;
@@ -16,16 +17,16 @@ public WebFluxConfig(ApplicationProperties properties) {
}
@Override
- public void addCorsMappings(CorsRegistry registry) {
+ public void addCorsMappings(@NonNull CorsRegistry registry) {
registry.addMapping(properties.getCors().getPathPattern())
- .allowedMethods(properties.getCors().getAllowedMethods())
- .allowedHeaders(properties.getCors().getAllowedHeaders())
- .allowedOriginPatterns(properties.getCors().getAllowedOriginPatterns())
+ .allowedMethods(properties.getCors().getAllowedMethods().split(","))
+ .allowedHeaders(properties.getCors().getAllowedHeaders().split(","))
+ .allowedOriginPatterns(properties.getCors().getAllowedOriginPatterns().split(","))
.allowCredentials(properties.getCors().isAllowCredentials());
}
@Bean
- GeoModule jacksonGeoModule() {
- return new GeoModule();
+ GeoJacksonModule geoJacksonModule() {
+ return new GeoJacksonModule();
}
}
diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/document/Grades.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/document/Grades.java
index 283c5c3e3..5633022cc 100644
--- a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/document/Grades.java
+++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/document/Grades.java
@@ -1,11 +1,11 @@
package com.example.mongoes.document;
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import java.time.LocalDateTime;
import java.util.StringJoiner;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
+import tools.jackson.databind.annotation.JsonDeserialize;
+import tools.jackson.databind.ext.javatime.deser.LocalDateTimeDeserializer;
public class Grades {
private String grade;
diff --git a/boot-mongodb-elasticsearch/src/main/resources/application-cluster.properties b/boot-mongodb-elasticsearch/src/main/resources/application-cluster.properties
index 6d6d3e52b..3d257a9c9 100644
--- a/boot-mongodb-elasticsearch/src/main/resources/application-cluster.properties
+++ b/boot-mongodb-elasticsearch/src/main/resources/application-cluster.properties
@@ -1,2 +1,2 @@
#clusterURL
-spring.data.mongodb.uri=mongodb://localhost:27017,localhost:27018,localhost:27019/mongoes?replicaSet=myReplicaSet
+spring.mongodb.uri=mongodb://localhost:27017,localhost:27018,localhost:27019/mongoes?replicaSet=myReplicaSet
diff --git a/boot-mongodb-elasticsearch/src/main/resources/application.properties b/boot-mongodb-elasticsearch/src/main/resources/application.properties
index a3b6d9d92..8d1566e61 100644
--- a/boot-mongodb-elasticsearch/src/main/resources/application.properties
+++ b/boot-mongodb-elasticsearch/src/main/resources/application.properties
@@ -23,8 +23,8 @@ spring.jackson.serialization.fail-on-empty-beans=false
#spring.data.mongodb.authentication-database=admin
#spring.data.mongodb.username=admin
#spring.data.mongodb.password=passcode
-spring.data.mongodb.database=mongoes
-spring.data.mongodb.uri=mongodb://localhost:27017/mongoes?replicaSet=rs0&readPreference=primary&directConnection=true
+spring.mongodb.database=mongoes
+spring.mongodb.uri=mongodb://localhost:27017/mongoes?replicaSet=rs0&readPreference=primary&directConnection=true
spring.elasticsearch.uris=localhost:9200
spring.elasticsearch.socket-timeout=10s
diff --git a/boot-mongodb-elasticsearch/src/test/java/com/example/mongoes/common/AbstractIntegrationTest.java b/boot-mongodb-elasticsearch/src/test/java/com/example/mongoes/common/AbstractIntegrationTest.java
index 2d01ce46a..3c83bdb71 100644
--- a/boot-mongodb-elasticsearch/src/test/java/com/example/mongoes/common/AbstractIntegrationTest.java
+++ b/boot-mongodb-elasticsearch/src/test/java/com/example/mongoes/common/AbstractIntegrationTest.java
@@ -5,8 +5,8 @@
import com.example.mongoes.repository.elasticsearch.RestaurantESRepository;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.webtestclient.autoconfigure.AutoConfigureWebTestClient;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.reactive.server.WebTestClient;
diff --git a/boot-mongodb-elasticsearch/src/test/java/com/example/mongoes/common/ContainersConfig.java b/boot-mongodb-elasticsearch/src/test/java/com/example/mongoes/common/ContainersConfig.java
index 9e4ed6314..fd49380d8 100644
--- a/boot-mongodb-elasticsearch/src/test/java/com/example/mongoes/common/ContainersConfig.java
+++ b/boot-mongodb-elasticsearch/src/test/java/com/example/mongoes/common/ContainersConfig.java
@@ -5,8 +5,8 @@
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.context.annotation.Bean;
-import org.testcontainers.containers.MongoDBContainer;
import org.testcontainers.elasticsearch.ElasticsearchContainer;
+import org.testcontainers.mongodb.MongoDBContainer;
import org.testcontainers.utility.DockerImageName;
@TestConfiguration(proxyBeanMethods = false)
diff --git a/boot-mongodb-elasticsearch/src/test/java/com/example/mongoes/model/request/RestaurantRequestTest.java b/boot-mongodb-elasticsearch/src/test/java/com/example/mongoes/model/request/RestaurantRequestTest.java
index 7900121d7..99beed82d 100644
--- a/boot-mongodb-elasticsearch/src/test/java/com/example/mongoes/model/request/RestaurantRequestTest.java
+++ b/boot-mongodb-elasticsearch/src/test/java/com/example/mongoes/model/request/RestaurantRequestTest.java
@@ -13,10 +13,10 @@
import org.springframework.boot.test.json.JsonContent;
import org.springframework.context.annotation.Import;
import org.springframework.data.geo.Point;
-import org.springframework.data.web.config.SpringDataJacksonConfiguration;
+import org.springframework.data.web.config.SpringDataJackson3Configuration;
@JsonTest
-@Import(SpringDataJacksonConfiguration.class)
+@Import(SpringDataJackson3Configuration.class)
class RestaurantRequestTest {
@Autowired private JacksonTester jacksonTester;
diff --git a/boot-mongodb-elasticsearch/src/test/java/com/example/mongoes/web/controller/RestaurantControllerIntTest.java b/boot-mongodb-elasticsearch/src/test/java/com/example/mongoes/web/controller/RestaurantControllerIntTest.java
index bd96d32d1..8f19cecbc 100644
--- a/boot-mongodb-elasticsearch/src/test/java/com/example/mongoes/web/controller/RestaurantControllerIntTest.java
+++ b/boot-mongodb-elasticsearch/src/test/java/com/example/mongoes/web/controller/RestaurantControllerIntTest.java
@@ -72,7 +72,7 @@ void createRestaurant_WithDuplicateName_ShouldReturnConflict() {
.json(
"""
{
- "type":"about:blank",
+ "type":"https://api.mongoes.com/errors/duplicate-restaurant",
"title":"Conflict",
"status":409,
"detail":"Restaurant with name 'Restaurant2' already exists",
@@ -243,7 +243,7 @@ void findRestaurantById_WithNonExistentId_ShouldReturnNotFound() {
.json(
"""
{
- "type":"about:blank",
+ "type":"https://api.mongoes.com/errors/restaurant-not-found",
"title":"Not Found",
"status":404,
"detail":"Restaurant not found with id: 999999",
@@ -265,7 +265,7 @@ void findRestaurantByName_WithNonExistentName_ShouldReturnNotFound() {
.json(
"""
{
- "type":"about:blank",
+ "type":"https://api.mongoes.com/errors/restaurant-not-found",
"title":"Not Found",
"status":404,
"detail":"Restaurant not found with name: Non Existent Restaurant",
diff --git a/boot-mongodb-elasticsearch/src/test/java/com/example/mongoes/web/controller/RestaurantControllerTest.java b/boot-mongodb-elasticsearch/src/test/java/com/example/mongoes/web/controller/RestaurantControllerTest.java
index 86a7d1b8b..f8697a8b0 100644
--- a/boot-mongodb-elasticsearch/src/test/java/com/example/mongoes/web/controller/RestaurantControllerTest.java
+++ b/boot-mongodb-elasticsearch/src/test/java/com/example/mongoes/web/controller/RestaurantControllerTest.java
@@ -18,7 +18,7 @@
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
+import org.springframework.boot.webflux.test.autoconfigure.WebFluxTest;
import org.springframework.data.geo.Point;
import org.springframework.http.MediaType;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
@@ -79,7 +79,7 @@ void findAllRestaurants_WithInvalidLimit_ShouldReturnBadRequest() {
.json(
"""
{
- "type": "about:blank",
+ "type": "https://api.mongoes.com/errors/validation-error",
"title": "Constraint Violation",
"status": 400,
"detail": "Validation failed",
@@ -114,7 +114,7 @@ void whenRestaurantRequestWithNullName_thenBadRequest() {
.expectBody()
.json(
"""
- {"type":"about:blank","title":"Bad Request","status":400,"detail":"Invalid request content.","instance":"/api/restaurant"}
+ {"type":"https://api.mongoes.com/errors/validation-error","title":"Bad Request","status":400,"detail":"Invalid request content.","instance":"/api/restaurant"}
""");
}
@@ -136,7 +136,7 @@ void whenRestaurantRequestWithEmptyBorough_thenBadRequest() {
.expectBody()
.json(
"""
- {"type":"about:blank","title":"Bad Request","status":400,"detail":"Invalid request content.","instance":"/api/restaurant"}
+ {"type":"https://api.mongoes.com/errors/validation-error","title":"Bad Request","status":400,"detail":"Invalid request content.","instance":"/api/restaurant"}
""");
}
@@ -180,7 +180,7 @@ void whenInvalidGrade_willReturns400() {
.expectBody()
.json(
"""
- {"type":"about:blank","title":"Bad Request","status":400,"detail":"Invalid request content.","instance":"/api/restaurant/1/grade"}
+ {"type":"https://api.mongoes.com/errors/validation-error","title":"Bad Request","status":400,"detail":"Invalid request content.","instance":"/api/restaurant/1/grade"}
""");
}
@@ -204,7 +204,7 @@ void whenNegativeScore_willReturns400() {
.expectBody()
.json(
"""
- {"type":"about:blank","title":"Bad Request","status":400,"detail":"Invalid request content.","instance":"/api/restaurant/1/grade"}
+ {"type":"https://api.mongoes.com/errors/validation-error","title":"Bad Request","status":400,"detail":"Invalid request content.","instance":"/api/restaurant/1/grade"}
""");
}
@@ -281,7 +281,7 @@ void findRestaurantByName_WithTooLongName_ShouldReturnBadRequest() {
.expectBody()
.json(
"""
- {"type":"about:blank","title":"Constraint Violation","status":400,"detail":"Validation failed","instance":"/api/restaurant/name/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","violations":[{"object":"RestaurantController","field":"findRestaurantByName.restaurantName","rejectedValue":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","message":"size must be between 0 and 255"}]}
+ {"type":"https://api.mongoes.com/errors/validation-error","title":"Constraint Violation","status":400,"detail":"Validation failed","instance":"/api/restaurant/name/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","violations":[{"object":"RestaurantController","field":"findRestaurantByName.restaurantName","rejectedValue":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","message":"size must be between 0 and 255"}]}
""");
}
@@ -316,7 +316,7 @@ void findRestaurantByName_WithInvalidCharacters_ShouldReturnBadRequest(
.isBadRequest()
.expectBody()
.jsonPath("$.type")
- .isEqualTo("about:blank")
+ .isEqualTo("https://api.mongoes.com/errors/validation-error")
.jsonPath("$.title")
.isEqualTo("Constraint Violation")
.jsonPath("$.status")
@@ -451,7 +451,7 @@ void createRestaurant_WithInvalidRequest_ShouldReturnBadRequest() {
.expectBody()
.json(
"""
- {"type":"about:blank","title":"Bad Request","status":400,"detail":"Invalid request content.","instance":"/api/restaurant"}
+ {"type":"https://api.mongoes.com/errors/validation-error","title":"Bad Request","status":400,"detail":"Invalid request content.","instance":"/api/restaurant"}
""");
}
@@ -493,7 +493,7 @@ void updateGradesOfRestaurant_WithInvalidRequest_ShouldReturnBadRequest() {
.expectBody()
.json(
"""
- {"type":"about:blank","title":"Constraint Violation","status":400,"detail":"Validation failed","instance":"/api/restaurant/1/grades","violations":[{"object":"RestaurantController","field":"updateGradesOfRestaurant.grades[0].date","rejectedValue":null,"message":"Date cannot be null"},{"object":"RestaurantController","field":"updateGradesOfRestaurant.grades[0].grade","rejectedValue":null,"message":"Grade cannot be blank"},{"object":"RestaurantController","field":"updateGradesOfRestaurant.grades[0].score","rejectedValue":null,"message":"Score cannot be null"}]}
+ {"type":"https://api.mongoes.com/errors/validation-error","title":"Constraint Violation","status":400,"detail":"Validation failed","instance":"/api/restaurant/1/grades","violations":[{"object":"RestaurantController","field":"updateGradesOfRestaurant.grades[0].date","rejectedValue":null,"message":"Date cannot be null"},{"object":"RestaurantController","field":"updateGradesOfRestaurant.grades[0].grade","rejectedValue":null,"message":"Grade cannot be blank"},{"object":"RestaurantController","field":"updateGradesOfRestaurant.grades[0].score","rejectedValue":null,"message":"Score cannot be null"}]}
""");
}
diff --git a/boot-mongodb-elasticsearch/src/test/java/com/example/mongoes/web/controller/SearchControllerTest.java b/boot-mongodb-elasticsearch/src/test/java/com/example/mongoes/web/controller/SearchControllerTest.java
index a67c892ac..386107a43 100644
--- a/boot-mongodb-elasticsearch/src/test/java/com/example/mongoes/web/controller/SearchControllerTest.java
+++ b/boot-mongodb-elasticsearch/src/test/java/com/example/mongoes/web/controller/SearchControllerTest.java
@@ -13,7 +13,7 @@
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
+import org.springframework.boot.webflux.test.autoconfigure.WebFluxTest;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.reactive.server.WebTestClient;
import reactor.core.publisher.Flux;
@@ -310,7 +310,7 @@ class CoordinateValidationTests {
private static final String MISSING_PARAMETER_ERROR_JSON =
"""
{
- "type": "about:blank",
+ "type": "https://api.mongoes.com/errors/validation-error",
"title": "Bad Request",
"status": 400,
"detail": "Required query parameter '%s' is not present.",
@@ -348,7 +348,7 @@ void whenCoordinatesOutOfRange_thenReturns400(
.isBadRequest()
.expectBody()
.jsonPath("$.type")
- .isEqualTo("about:blank")
+ .isEqualTo("https://api.mongoes.com/errors/validation-error")
.jsonPath("$.title")
.isEqualTo("Constraint Violation")
.jsonPath("$.status")
@@ -432,7 +432,7 @@ void whenDistanceMissing_thenReturns400() {
.json(
"""
{
- "type": "about:blank",
+ "type": "https://api.mongoes.com/errors/validation-error",
"title": "Bad Request",
"status": 400,
"detail": "Required query parameter 'distance' is not present.",
@@ -462,7 +462,7 @@ void whenDistanceNegative_thenReturns400() {
.json(
"""
{
- "type": "about:blank",
+ "type": "https://api.mongoes.com/errors/validation-error",
"title": "Constraint Violation",
"status": 400,
"detail": "Validation failed",
@@ -709,7 +709,7 @@ void searchRestaurantIdRange_WithNullLowerLimit_ShouldReturnBadRequest() {
.json(
"""
{
- "type": "about:blank",
+ "type": "https://api.mongoes.com/errors/validation-error",
"title": "Bad Request",
"status": 400,
"detail": "Required query parameter 'lowerLimit' is not present.",
@@ -735,7 +735,7 @@ void searchRestaurantIdRange_WithNullUpperLimit_ShouldReturnBadRequest() {
.json(
"""
{
- "type": "about:blank",
+ "type": "https://api.mongoes.com/errors/validation-error",
"title": "Bad Request",
"status": 400,
"detail": "Required query parameter 'upperLimit' is not present.",
@@ -762,7 +762,7 @@ void searchRestaurantIdRange_WithZeroLowerLimit_ShouldReturnBadRequest() {
.json(
"""
{
- "type": "about:blank",
+ "type": "https://api.mongoes.com/errors/validation-error",
"title": "Constraint Violation",
"status": 400,
"detail": "Validation failed",
@@ -797,7 +797,7 @@ void searchRestaurantIdRange_WithZeroUpperLimit_ShouldReturnBadRequest() {
.json(
"""
{
- "type": "about:blank",
+ "type": "https://api.mongoes.com/errors/validation-error",
"title": "Constraint Violation",
"status": 400,
"detail": "Validation failed",
@@ -832,7 +832,7 @@ void searchRestaurantIdRange_WithNegativeLowerLimit_ShouldReturnBadRequest() {
.json(
"""
{
- "type": "about:blank",
+ "type": "https://api.mongoes.com/errors/validation-error",
"title": "Constraint Violation",
"status": 400,
"detail": "Validation failed",
@@ -867,7 +867,7 @@ void searchRestaurantIdRange_WithNegativeUpperLimit_ShouldReturnBadRequest() {
.json(
"""
{
- "type": "about:blank",
+ "type": "https://api.mongoes.com/errors/validation-error",
"title": "Constraint Violation",
"status": 400,
"detail": "Validation failed",
diff --git a/scheduler/boot-scheduler-quartz/pom.xml b/scheduler/boot-scheduler-quartz/pom.xml
index b6233e075..2e07dc570 100644
--- a/scheduler/boot-scheduler-quartz/pom.xml
+++ b/scheduler/boot-scheduler-quartz/pom.xml
@@ -111,6 +111,11 @@
org.springframework.boot
spring-boot-starter-webmvc-test
test
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator-test
+ test
org.springframework.boot
diff --git a/scheduler/boot-scheduler-quartz/src/main/java/com/scheduler/quartz/config/WebMvcConfig.java b/scheduler/boot-scheduler-quartz/src/main/java/com/scheduler/quartz/config/WebMvcConfig.java
index c115f3dc1..036f17fd1 100644
--- a/scheduler/boot-scheduler-quartz/src/main/java/com/scheduler/quartz/config/WebMvcConfig.java
+++ b/scheduler/boot-scheduler-quartz/src/main/java/com/scheduler/quartz/config/WebMvcConfig.java
@@ -1,7 +1,7 @@
package com.scheduler.quartz.config;
+import org.jspecify.annotations.NonNull;
import org.springframework.context.annotation.Configuration;
-import org.springframework.lang.NonNull;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;