Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exception handling #12

Merged
merged 10 commits into from
Aug 10, 2023
7 changes: 6 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ dependencies {
testImplementation("org.mockito.kotlin:mockito-kotlin:4.0.0")
testImplementation("com.squareup.okhttp3:mockwebserver:$mockWebServerVersion")
testImplementation("com.squareup.okhttp3:okhttp:$mockWebServerVersion")
testImplementation("it.pagopa:pagopa-ecommerce-commons:$ecommerceCommonsVersion:tests")
}

configurations {
Expand Down Expand Up @@ -117,7 +118,10 @@ tasks.create("applySemanticVersionPlugin") {
apply(plugin = "com.dipien.semantic-version")
}

tasks.withType(JavaCompile::class.java).configureEach { options.encoding = "UTF-8" }
tasks.withType(JavaCompile::class.java).configureEach {
options.encoding = "UTF-8"
options.compilerArgs.add("--enable-preview")
}

tasks.withType(Javadoc::class.java).configureEach { options.encoding = "UTF-8" }

Expand Down Expand Up @@ -191,6 +195,7 @@ tasks.named<Jar>("jar") { enabled = false }
tasks.test {
useJUnitPlatform()
finalizedBy(tasks.jacocoTestReport) // report is always generated after tests run
jvmArgs(listOf("--enable-preview"))
}

tasks.jacocoTestReport {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,14 @@ class EcommerceController(@Autowired val ecommerceService: EcommerceService) : E
exchange: ServerWebExchange
): Mono<ResponseEntity<SearchTransactionResponseDto>> {
logger.info("[HelpDesk controller] ecommerceSearchTransaction")
return ecommerceService
.searchTransaction(pageNumber, pageSize, ecommerceSearchTransactionRequestDto)
return ecommerceSearchTransactionRequestDto
.flatMap {
ecommerceService.searchTransaction(
pageNumber = pageNumber,
pageSize = pageSize,
ecommerceSearchTransactionRequestDto = it
)
}
.map { ResponseEntity.ok(it) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ package it.pagopa.ecommerce.helpdesk.controllers
import it.pagopa.ecommerce.helpdesk.services.EcommerceService
import it.pagopa.ecommerce.helpdesk.services.PmService
import it.pagopa.generated.ecommerce.helpdesk.api.HelpdeskApi
import it.pagopa.generated.ecommerce.helpdesk.model.*
import it.pagopa.generated.ecommerce.helpdesk.model.EcommerceSearchTransactionRequestDto
import it.pagopa.generated.ecommerce.helpdesk.model.HelpDeskSearchTransactionRequestDto
import it.pagopa.generated.ecommerce.helpdesk.model.PmSearchTransactionRequestDto
import it.pagopa.generated.ecommerce.helpdesk.model.SearchTransactionResponseDto
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.RestController
Expand All @@ -29,15 +32,15 @@ class HelpdeskController(
.searchTransaction(
pageNumber = pageNumber,
pageSize = pageSize,
ecommerceSearchTransactionRequestDto = Mono.just(it)
ecommerceSearchTransactionRequestDto = it
)
.map { response -> ResponseEntity.ok(response) }
is PmSearchTransactionRequestDto ->
pmService
.searchTransaction(
pageNumber = pageNumber,
pageSize = pageSize,
pmSearchTransactionRequestDto = Mono.just(it)
pmSearchTransactionRequestDto = it
)
.map { response -> ResponseEntity.ok(response) }
else -> Mono.error(RuntimeException("Unknown search criteria"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,14 @@ class PmController(@Autowired val pmService: PmService) : PmApi {
exchange: ServerWebExchange
): Mono<ResponseEntity<SearchTransactionResponseDto>> {
logger.info("[HelpDesk controller] pmSearchTransaction")
return pmService
.searchTransaction(pageSize, pageNumber, pmSearchTransactionRequestDto)
return pmSearchTransactionRequestDto
.flatMap {
pmService.searchTransaction(
pageSize = pageSize,
pageNumber = pageNumber,
pmSearchTransactionRequestDto = it
)
}
.map { ResponseEntity.ok(it) }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package it.pagopa.ecommerce.helpdesk.exceptionhandler

import it.pagopa.ecommerce.helpdesk.exceptions.ApiError
import it.pagopa.ecommerce.helpdesk.exceptions.RestApiException
import it.pagopa.generated.ecommerce.helpdesk.model.ProblemJsonDto
import jakarta.xml.bind.ValidationException
import java.util.*
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.http.converter.HttpMessageNotReadableException
import org.springframework.web.bind.MethodArgumentNotValidException
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.method.annotation.MethodArgumentTypeMismatchException
import org.springframework.web.server.ServerWebInputException

/**
* Exception handler used to output a custom message in case an incoming request is invalid or an
* api encounter an error and throw an RestApiException
*/
@RestControllerAdvice
class ExceptionHandler {

val logger: Logger = LoggerFactory.getLogger(javaClass)

/** RestApiException exception handler */
@ExceptionHandler(RestApiException::class)
fun handleException(e: RestApiException): ResponseEntity<ProblemJsonDto> {
logger.error("Exception processing request", e)
return ResponseEntity.status(e.httpStatus)
.body(
ProblemJsonDto().status(e.httpStatus.value()).title(e.title).detail(e.description)
)
}

/** ApiError exception handler */
@ExceptionHandler(ApiError::class)
fun handleException(e: ApiError): ResponseEntity<ProblemJsonDto> {
return handleException(e.toRestException())
}

/** Validation request exception handler */
@ExceptionHandler(
MethodArgumentNotValidException::class,
MethodArgumentTypeMismatchException::class,
ServerWebInputException::class,
ValidationException::class,
HttpMessageNotReadableException::class,
WebExchangeBindException::class
)
fun handleRequestValidationException(e: Exception): ResponseEntity<ProblemJsonDto> {

logger.error("Input request is not valid", e)
return ResponseEntity.badRequest()
.body(
ProblemJsonDto()
.status(HttpStatus.BAD_REQUEST.value())
.title("Bad request")
.detail(e.localizedMessage)
)
}

/** Handler for generic exception */
@ExceptionHandler(Exception::class)
fun handleGenericException(e: Exception): ResponseEntity<ProblemJsonDto> {
logger.error("Exception processing the request", e)
return ResponseEntity.internalServerError()
.body(
ProblemJsonDto()
.status(HttpStatus.INTERNAL_SERVER_ERROR.value())
.title("Error processing the request")
.detail(e.localizedMessage)
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package it.pagopa.ecommerce.helpdesk.exceptions

/**
* Class that bridges business-related exception to `RestException`. Business-related exceptions
* should extend this class.
*/
abstract class ApiError(message: String?) : RuntimeException(message) {
/** Convert this ApiError to RestApiException */
abstract fun toRestException(): RestApiException
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package it.pagopa.ecommerce.helpdesk.exceptions

import org.springframework.http.HttpStatus

class NoResultFoundException(private val searchCriteriaType: String) :
ApiError("No result found for criteria: $searchCriteriaType") {

override fun toRestException() =
RestApiException(
httpStatus = HttpStatus.NOT_FOUND,
description = "No result can be found searching for criteria $searchCriteriaType",
title = "No result found"
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package it.pagopa.ecommerce.helpdesk.exceptions

import org.springframework.http.HttpStatus

/** Rest api exception, used to return an error specific HttpStatus and reason code to the caller */
class RestApiException(val httpStatus: HttpStatus, val title: String, val description: String) :
RuntimeException(title)
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package it.pagopa.ecommerce.helpdesk.services

import it.pagopa.ecommerce.commons.documents.v1.TransactionActivatedEvent
import it.pagopa.generated.ecommerce.helpdesk.model.*
import java.time.OffsetDateTime
import org.slf4j.LoggerFactory
Expand All @@ -15,62 +14,56 @@ class EcommerceService {
fun searchTransaction(
pageNumber: Int,
pageSize: Int,
ecommerceSearchTransactionRequestDto: Mono<EcommerceSearchTransactionRequestDto>
ecommerceSearchTransactionRequestDto: EcommerceSearchTransactionRequestDto
): Mono<SearchTransactionResponseDto> {
logger.info("[helpDesk ecommerce service] searchTransaction method")
return ecommerceSearchTransactionRequestDto
.doOnNext {
logger.info("Search type: ${it.type}")
val transactionEvent = TransactionActivatedEvent()
println(transactionEvent)
}
.map {
SearchTransactionResponseDto()
.page(PageInfoDto().current(0).results(3).total(1))
.transactions(
listOf(
TransactionResultDto()
.product(ProductDto.ECOMMERCE)
.userInfo(
UserInfoDto()
.userFiscalCode("userFiscalCode")
.notificationEmail("notificationEmail")
.surname("surname")
.name("name")
.username("username")
.authenticationType("auth type")
)
.transactionInfo(
TransactionInfoDto()
.amount(100)
.fee(100)
.creationDate(OffsetDateTime.now())
.status("status")
.statusDetails("status details")
.grandTotal(200)
.rrn("rrn")
.authotizationCode("authCode")
.paymentMethodName("paymentMethodName")
.brand("brand")
)
.paymentDetailInfo(
PaymentDetailInfoDto()
.iuv("IUV")
.rptIds(listOf("rptId1", "rptId2"))
.idTransaction("paymentContextCode")
.paymentToken("paymentToken")
.creditorInstitution("creditor institution")
.paFiscalCode("77777777777")
)
.paymentInfo(PaymentInfoDto().origin("origin").subject("subject"))
.pspInfo(
PspInfoDto()
.pspId("pspId")
.businessName("business name")
.idChannel("id channel")
)
)
return Mono.just(
SearchTransactionResponseDto()
.page(PageInfoDto().current(0).results(3).total(1))
.transactions(
listOf(
TransactionResultDto()
.product(ProductDto.ECOMMERCE)
.userInfo(
UserInfoDto()
.userFiscalCode("userFiscalCode")
.notificationEmail("notificationEmail")
.surname("surname")
.name("name")
.username("username")
.authenticationType("auth type")
)
.transactionInfo(
TransactionInfoDto()
.amount(100)
.fee(100)
.creationDate(OffsetDateTime.now())
.status("status")
.statusDetails("status details")
.grandTotal(200)
.rrn("rrn")
.authotizationCode("authCode")
.paymentMethodName("paymentMethodName")
.brand("brand")
)
.paymentDetailInfo(
PaymentDetailInfoDto()
.iuv("IUV")
.rptIds(listOf("rptId1", "rptId2"))
.idTransaction("paymentContextCode")
.paymentToken("paymentToken")
.creditorInstitution("creditor institution")
.paFiscalCode("77777777777")
)
.paymentInfo(PaymentInfoDto().origin("origin").subject("subject"))
.pspInfo(
PspInfoDto()
.pspId("pspId")
.businessName("business name")
.idChannel("id channel")
)
)
}
)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,23 @@ class PmService(@Autowired val connectionFactory: ConnectionFactory) {
fun searchTransaction(
pageNumber: Int,
pageSize: Int,
pmSearchTransactionRequestDto: Mono<PmSearchTransactionRequestDto>
pmSearchTransactionRequestDto: PmSearchTransactionRequestDto
): Mono<SearchTransactionResponseDto> {
logger.info("[helpDesk pm service] searchTransaction method")

return pmSearchTransactionRequestDto
.doOnNext { logger.info("Search type: ${it.type}") }
.flatMapMany {
Flux.usingWhen(
connectionFactory.create(),
{ connection ->
Flux.from(
connection
.createStatement("SELECT 'Hello, Oracle' FROM sys.dual")
.execute()
)
.flatMap { result -> result.map { row -> row[0, String::class.java] } }
.doOnNext { logger.info("Read from DB: $it") }
},
{ it.close() }
)
}
return Flux.usingWhen(
connectionFactory.create(),
{ connection ->
Flux.from(
connection
.createStatement("SELECT 'Hello, Oracle' FROM sys.dual")
.execute()
)
.flatMap { result -> result.map { row -> row[0, String::class.java] } }
.doOnNext { logger.info("Read from DB: $it") }
},
{ it.close() }
)
.collectList()
.map {
SearchTransactionResponseDto()
Expand Down
Loading