-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
8ee12c8
commit 50bd17e
Showing
13 changed files
with
253 additions
and
6 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
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
46 changes: 46 additions & 0 deletions
46
src/main/java/it/gov/pagopa/payhub/auth/config/MongoConfig.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,46 @@ | ||
package it.gov.pagopa.payhub.auth.config; | ||
|
||
import lombok.Setter; | ||
import org.springframework.boot.autoconfigure.mongo.MongoClientSettingsBuilderCustomizer; | ||
import org.springframework.boot.context.properties.ConfigurationProperties; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; | ||
|
||
import java.util.concurrent.TimeUnit; | ||
|
||
@Configuration | ||
@EnableMongoRepositories(basePackages = "it.gov.pagopa.payhub.auth.repository") | ||
public class MongoConfig { | ||
|
||
@Configuration | ||
@ConfigurationProperties(prefix = "spring.data.mongodb.config") | ||
@Setter | ||
public static class MongoDbCustomProperties { | ||
private ConnectionPoolSettings connectionPool; | ||
|
||
static class ConnectionPoolSettings { | ||
int maxSize; | ||
int minSize; | ||
long maxWaitTimeMS; | ||
long maxConnectionLifeTimeMS; | ||
long maxConnectionIdleTimeMS; | ||
int maxConnecting; | ||
} | ||
|
||
} | ||
|
||
@Bean | ||
public MongoClientSettingsBuilderCustomizer customizer(MongoDbCustomProperties mongoDbCustomProperties) { | ||
return builder -> builder.applyToConnectionPoolSettings( | ||
connectionPool -> { | ||
connectionPool.maxSize(mongoDbCustomProperties.connectionPool.maxSize); | ||
connectionPool.minSize(mongoDbCustomProperties.connectionPool.minSize); | ||
connectionPool.maxWaitTime(mongoDbCustomProperties.connectionPool.maxWaitTimeMS, TimeUnit.MILLISECONDS); | ||
connectionPool.maxConnectionLifeTime(mongoDbCustomProperties.connectionPool.maxConnectionLifeTimeMS, TimeUnit.MILLISECONDS); | ||
connectionPool.maxConnectionIdleTime(mongoDbCustomProperties.connectionPool.maxConnectionIdleTimeMS, TimeUnit.MILLISECONDS); | ||
connectionPool.maxConnecting(mongoDbCustomProperties.connectionPool.maxConnecting); | ||
}); | ||
} | ||
} | ||
|
2 changes: 1 addition & 1 deletion
2
...ayhub/auth/configuration/RedisConfig.java → ...agopa/payhub/auth/config/RedisConfig.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
2 changes: 1 addition & 1 deletion
2
...hub/auth/configuration/SwaggerConfig.java → ...opa/payhub/auth/config/SwaggerConfig.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
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
64 changes: 64 additions & 0 deletions
64
src/main/java/it/gov/pagopa/payhub/auth/exception/MongoTooManyRequestsExceptionHandler.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,64 @@ | ||
package it.gov.pagopa.payhub.auth.exception; | ||
|
||
import it.gov.pagopa.payhub.model.generated.AuthErrorDTO; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.dao.DataAccessException; | ||
import org.springframework.http.HttpHeaders; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.ExceptionHandler; | ||
import org.springframework.web.bind.annotation.RestControllerAdvice; | ||
|
||
import java.util.regex.Matcher; | ||
import java.util.regex.Pattern; | ||
|
||
@RestControllerAdvice | ||
@Slf4j | ||
public class MongoTooManyRequestsExceptionHandler { | ||
private static final Pattern RETRY_AFTER_MS_PATTERN = Pattern.compile("RetryAfterMs=(\\d+)"); | ||
|
||
@ExceptionHandler(DataAccessException.class) | ||
protected ResponseEntity<AuthErrorDTO> handleDataAccessException( | ||
DataAccessException ex, HttpServletRequest request) { | ||
|
||
if (isRequestRateTooLargeException(ex)) { | ||
Long retryAfterMs = getRetryAfterMs(ex); | ||
return handleRequestRateTooLargeException(ex, request, retryAfterMs); | ||
} else { | ||
return AuthExceptionHandler.handleAuthErrorException(ex, request, HttpStatus.INTERNAL_SERVER_ERROR, AuthErrorDTO.ErrorEnum.AUTH_GENERIC_ERROR); | ||
} | ||
} | ||
|
||
private ResponseEntity<AuthErrorDTO> handleRequestRateTooLargeException(Exception ex, HttpServletRequest request, Long retryAfterMs) { | ||
String message = ex.getMessage(); | ||
|
||
log.info( | ||
"A MongoQueryException (RequestRateTooLarge) occurred handling request {}: HttpStatus 429 - {}", | ||
AuthExceptionHandler.getRequestDetails(request), message); | ||
|
||
final ResponseEntity.BodyBuilder responseBuilder = ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS) | ||
.contentType(MediaType.APPLICATION_JSON); | ||
|
||
if (retryAfterMs != null) { | ||
long retryAfter = (long) Math.ceil((double) retryAfterMs / 1000); | ||
responseBuilder.header(HttpHeaders.RETRY_AFTER, String.valueOf(retryAfter)) | ||
.header("Retry-After-Ms", String.valueOf(retryAfterMs)); | ||
} | ||
|
||
return responseBuilder.build(); | ||
} | ||
|
||
public static Long getRetryAfterMs(DataAccessException ex) { | ||
Matcher matcher = RETRY_AFTER_MS_PATTERN.matcher(ex.getMessage()); | ||
if (matcher.find()) { | ||
return Long.parseLong(matcher.group(1)); | ||
} | ||
return null; | ||
} | ||
|
||
public static boolean isRequestRateTooLargeException(DataAccessException ex) { | ||
return ex.getMessage().contains("TooManyRequests") || ex.getMessage().contains("Error=16500,"); | ||
} | ||
} |
2 changes: 1 addition & 1 deletion
2
src/main/java/it/gov/pagopa/payhub/auth/service/TokenStoreServiceImpl.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
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
2 changes: 1 addition & 1 deletion
2
...b/auth/configuration/RedisConfigTest.java → ...a/payhub/auth/config/RedisConfigTest.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
114 changes: 114 additions & 0 deletions
114
...st/java/it/gov/pagopa/payhub/auth/exception/MongoTooManyRequestsExceptionHandlerTest.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,114 @@ | ||
package it.gov.pagopa.payhub.auth.exception; | ||
|
||
import com.mongodb.MongoQueryException; | ||
import com.mongodb.MongoWriteException; | ||
import com.mongodb.ServerAddress; | ||
import com.mongodb.WriteError; | ||
import org.bson.BsonDocument; | ||
import org.junit.jupiter.api.Test; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; | ||
import org.springframework.boot.test.mock.mockito.SpyBean; | ||
import org.springframework.dao.DataIntegrityViolationException; | ||
import org.springframework.data.mongodb.UncategorizedMongoDbException; | ||
import org.springframework.http.HttpHeaders; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.test.context.ContextConfiguration; | ||
import org.springframework.test.web.servlet.MockMvc; | ||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; | ||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers; | ||
|
||
import static org.mockito.Mockito.doThrow; | ||
|
||
@WebMvcTest(value = { | ||
MongoTooManyRequestsExceptionHandler.class, | ||
AuthExceptionHandlerTest.TestController.class}) | ||
@ContextConfiguration(classes = {MongoTooManyRequestsExceptionHandler.class, | ||
AuthExceptionHandlerTest.TestController.class}) | ||
class MongoTooManyRequestsExceptionHandlerTest { | ||
@Autowired | ||
private MockMvc mockMvc; | ||
|
||
@SpyBean | ||
private AuthExceptionHandlerTest.TestController testControllerSpy; | ||
|
||
@Test | ||
void handleUncategorizedMongoDbException() throws Exception { | ||
|
||
String mongoFullErrorResponse = """ | ||
{"ok": 0.0, "errmsg": "Error=16500, RetryAfterMs=34,\s | ||
Details='Response status code does not indicate success: TooManyRequests (429) Substatus: 3200 ActivityId: 46ba3855-bc3b-4670-8609-17e1c2c87778 Reason:\s | ||
(\\r\\nErrors : [\\r\\n \\"Request rate is large. More Request Units may be needed, so no changes were made. Please retry this request later. Learn more: | ||
http://aka.ms/cosmosdb-error-429\\"\\r\\n]\\r\\n) ", "code": 16500, "codeName": "RequestRateTooLarge"} | ||
"""; | ||
|
||
final MongoQueryException mongoQueryException = new MongoQueryException( | ||
BsonDocument.parse(mongoFullErrorResponse), new ServerAddress()); | ||
doThrow( | ||
new UncategorizedMongoDbException(mongoQueryException.getMessage(), mongoQueryException)) | ||
.when(testControllerSpy).testEndpoint("DATA"); | ||
|
||
mockMvc.perform(MockMvcRequestBuilders.get("/test") | ||
.param(AuthExceptionHandlerTest.DATA, "DATA") | ||
.contentType(MediaType.APPLICATION_JSON)) | ||
.andExpect(MockMvcResultMatchers.status().isTooManyRequests()) | ||
.andExpect(MockMvcResultMatchers.header().exists(HttpHeaders.RETRY_AFTER)) | ||
.andExpect(MockMvcResultMatchers.header().string(HttpHeaders.RETRY_AFTER, "1")) | ||
.andExpect(MockMvcResultMatchers.header().string("Retry-After-Ms", "34")); | ||
} | ||
|
||
@Test | ||
void handleWriteDbWithoutTooManyRequestsException() throws Exception { | ||
|
||
String writeErrorMessage = """ | ||
Error=16500, Substatus: 3200; ActivityId: 822d212d-5aac-4f5d-a2d4-76d6da7b619e; Reason: ( | ||
Errors : [ | ||
"Request rate is large. More Request Units may be needed, so no changes were made. Please retry this request later. Learn more: http://aka.ms/cosmosdb-error-429" | ||
] | ||
); | ||
"""; | ||
|
||
handleMongoWriteException(writeErrorMessage); | ||
} | ||
|
||
@Test | ||
void handleTooManyRequestsWriteDbException() throws Exception { | ||
|
||
String writeErrorMessage = """ | ||
RetryAfterMs=34, Details='Response status code does not indicate success: TooManyRequests (429); Substatus: 3200; ActivityId: 822d212d-5aac-4f5d-a2d4-76d6da7b619e; Reason: ( | ||
Errors : [ | ||
"Request rate is large. More Request Units may be needed, so no changes were made. Please retry this request later. Learn more: http://aka.ms/cosmosdb-error-429" | ||
] | ||
); | ||
"""; | ||
|
||
handleMongoWriteException(writeErrorMessage); | ||
} | ||
|
||
@Test | ||
void handleUncategorizedMongoDbExceptionNotRequestRateTooLarge() throws Exception { | ||
|
||
doThrow(new UncategorizedMongoDbException("DUMMY", new Exception())) | ||
.when(testControllerSpy).testEndpoint("DATA"); | ||
|
||
mockMvc.perform(MockMvcRequestBuilders.get("/test") | ||
.param(AuthExceptionHandlerTest.DATA, "DATA") | ||
.contentType(MediaType.APPLICATION_JSON)) | ||
.andExpect(MockMvcResultMatchers.status().isInternalServerError()) | ||
.andExpect(MockMvcResultMatchers.content().json("{\"error_description\":\"DUMMY\"}", false)); | ||
} | ||
|
||
|
||
private void handleMongoWriteException(String writeErrorMessage) throws Exception { | ||
final MongoWriteException mongoWriteException = new MongoWriteException( | ||
new WriteError(16500, writeErrorMessage, BsonDocument.parse("{}")), new ServerAddress()); | ||
doThrow( | ||
new DataIntegrityViolationException(mongoWriteException.getMessage(), mongoWriteException)) | ||
.when(testControllerSpy).testEndpoint("DATA"); | ||
|
||
mockMvc.perform(MockMvcRequestBuilders.get("/test") | ||
.param(AuthExceptionHandlerTest.DATA, "DATA") | ||
.contentType(MediaType.APPLICATION_JSON)) | ||
.andExpect(MockMvcResultMatchers.status().isTooManyRequests()); | ||
} | ||
} |