diff --git a/pom.xml b/pom.xml index 79ac2a9..180fc7a 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.rbkmoney service-parent-pom - 1.2.2 + 1.2.4 claim-management-api @@ -26,10 +26,11 @@ ${server.port} ${management.port} ${env.REGISTRY} - 1.29-d665bf2-server + 1.30-eafa8b6-server 12.0.1 1.68 2.2.4.RELEASE + 5.4.6 0.9.1 2.0.6 3.2.0 @@ -115,6 +116,16 @@ org.springframework.boot spring-boot-starter-security + + org.springframework.security + spring-security-config + ${spring-security.version} + + + org.springframework.security + spring-security-web + ${spring-security.version} + org.springframework.boot spring-boot-starter-actuator diff --git a/src/main/kotlin/com/rbkmoney/claimmanagementapi/controller/ClaimManagementController.kt b/src/main/kotlin/com/rbkmoney/claimmanagementapi/controller/ClaimManagementController.kt index 0ee4eea..2da0d52 100644 --- a/src/main/kotlin/com/rbkmoney/claimmanagementapi/controller/ClaimManagementController.kt +++ b/src/main/kotlin/com/rbkmoney/claimmanagementapi/controller/ClaimManagementController.kt @@ -17,12 +17,7 @@ import com.rbkmoney.damsel.claim_management.InvalidClaimStatus import com.rbkmoney.damsel.claim_management.LimitExceeded import com.rbkmoney.swag.claim_management.api.ProcessingApi import com.rbkmoney.swag.claim_management.model.Claim -import com.rbkmoney.swag.claim_management.model.GeneralError import com.rbkmoney.swag.claim_management.model.InlineResponse200 -import com.rbkmoney.swag.claim_management.model.InlineResponse400 -import com.rbkmoney.swag.claim_management.model.InlineResponse4001 -import com.rbkmoney.swag.claim_management.model.InlineResponse4002 -import com.rbkmoney.swag.claim_management.model.InlineResponse4003 import com.rbkmoney.swag.claim_management.model.Modification import mu.KotlinLogging import org.apache.thrift.TException @@ -53,54 +48,31 @@ class ClaimManagementController( @NotNull @Size(min = 1, max = 40) xRequestId: String?, @NotNull @Valid changeset: List?, @Size(min = 1, max = 40) xRequestDeadline: String? - ): ResponseEntity { - return try { + ): ResponseEntity = + performRequest("createClaim", xRequestId!!) { log.info { "Process 'createClaim' get started, xRequestId=$xRequestId" } partyManagementService.checkStatus(xRequestId) deadlineChecker.checkDeadline(xRequestDeadline, xRequestId) val claim = claimManagementService.createClaim(keycloakService.partyId, changeset!!) log.info { "Claim created, xRequestId=$xRequestId, claimId=${claim.id}" } ResponseEntity.ok(claim) - } catch (ex: DeadlineException) { - val msg = ex.message - val response: InlineResponse4001 = InlineResponse4001() - .code(InlineResponse4001.CodeEnum.INVALIDDEADLINE) - .message(msg) - throw BadRequestException(msg, ex, response) - } catch (ex: InvalidChangeset) { - val msg = "Invalid changeset, xRequestId=$xRequestId, reason='${ex.reason}'" - val response: InlineResponse4001 = InlineResponse4001() - .code(InlineResponse4001.CodeEnum.INVALIDCHANGESET) - .message(msg) - throw BadRequestException(msg, ex, response) - } catch (ex: TException) { - throw buildDarkApi5xxException("createClaim", xRequestId, ex) } - } @PreAuthorize("hasAuthority('party:read')") override fun getClaimByID( @NotNull @Size(min = 1, max = 40) xRequestId: String?, @NotNull @Valid claimId: Long?, @Size(min = 1, max = 40) xRequestDeadline: String? - ): ResponseEntity { - return try { + ): ResponseEntity = + performRequest("getClaimByID", xRequestId!!, claimId!!) { log.info { "Process 'getClaimByID' get started, xRequestId=$xRequestId, claimId=$claimId" } partyManagementService.checkStatus(xRequestId) deadlineChecker.checkDeadline(xRequestDeadline, xRequestId) - val claim = claimManagementService.getClaimById(keycloakService.partyId, claimId!!) + val claim = claimManagementService.getClaimById(keycloakService.partyId, claimId) log.info { "Got a claim, xRequestId=$xRequestId, claimId=$claimId" } + ResponseEntity.ok(claim) - } catch (ex: DeadlineException) { - val msg = ex.message - val response: GeneralError = GeneralError().message(msg) - throw BadRequestException(msg, ex, response) - } catch (ex: ClaimNotFound) { - throw buildNotFoundException(xRequestId!!, claimId!!, ex) - } catch (ex: TException) { - throw buildDarkApi5xxException("getClaimByID", xRequestId, ex) } - } @PreAuthorize("hasAuthority('party:write')") override fun revokeClaimByID( @@ -109,38 +81,16 @@ class ClaimManagementController( @NotNull @Valid claimRevision: Int?, @Size(min = 1, max = 40) xRequestDeadline: String?, @Valid reason: String? - ): ResponseEntity { - return try { + ): ResponseEntity = + performRequest("revokeClaimByID", xRequestId!!, claimId!!, claimRevision!!) { log.info { "Process 'revokeClaimByID' get started, xRequestId=$xRequestId, claimId=$claimId" } partyManagementService.checkStatus(xRequestId) deadlineChecker.checkDeadline(xRequestDeadline, xRequestId) - claimManagementService.revokeClaimById(keycloakService.partyId, claimId!!, claimRevision!!, reason) + claimManagementService.revokeClaimById(keycloakService.partyId, claimId, claimRevision, reason) log.info { "Successful revoke claim, xRequestId=$xRequestId, claimId=$claimId" } + ResponseEntity(HttpStatus.OK) - } catch (ex: DeadlineException) { - val msg = ex.message - val response: InlineResponse4002 = InlineResponse4002() - .code(InlineResponse4002.CodeEnum.INVALIDDEADLINE) - .message(msg) - throw BadRequestException(msg, ex, response) - } catch (ex: InvalidClaimStatus) { - val msg = "Invalid claim status, xRequestId=$xRequestId, status=${ex.status}" - val response: InlineResponse4002 = InlineResponse4002() - .code(InlineResponse4002.CodeEnum.INVALIDCLAIMSTATUS) - .message(msg) - throw BadRequestException(msg, ex, response) - } catch (ex: InvalidClaimRevision) { - val msg = "Invalid claim revision, xRequestId=$xRequestId, claimRevision=$claimRevision" - val response: InlineResponse4002 = InlineResponse4002() - .code(InlineResponse4002.CodeEnum.INVALIDCLAIMREVISION) - .message(msg) - throw BadRequestException(msg, ex, response) - } catch (ex: ClaimNotFound) { - throw buildNotFoundException(xRequestId!!, claimId!!, ex) - } catch (ex: TException) { - throw buildDarkApi5xxException("revokeClaimByID", xRequestId, ex) } - } @PreAuthorize("hasAuthority('party:write')") override fun requestReviewClaimByID( @@ -148,38 +98,16 @@ class ClaimManagementController( @NotNull @Valid claimId: Long?, @NotNull @Valid claimRevision: Int?, @Size(min = 1, max = 40) xRequestDeadline: String? - ): ResponseEntity { - return try { + ): ResponseEntity = + performRequest("requestClaimReviewById", xRequestId!!, claimId!!, claimRevision!!) { log.info { "Process 'requestReviewClaimByID' get started, xRequestId=$xRequestId, claimId=$claimId" } partyManagementService.checkStatus(xRequestId) deadlineChecker.checkDeadline(xRequestDeadline, xRequestId) - claimManagementService.requestClaimReviewById(keycloakService.partyId, claimId!!, claimRevision!!) + claimManagementService.requestClaimReviewById(keycloakService.partyId, claimId, claimRevision) log.info { "Successful request claim review, xRequestId=$xRequestId, claimId=$claimId" } + ResponseEntity(HttpStatus.OK) - } catch (ex: DeadlineException) { - val msg = ex.message - val response: InlineResponse4002 = InlineResponse4002() - .code(InlineResponse4002.CodeEnum.INVALIDDEADLINE) - .message(msg) - throw BadRequestException(msg, ex, response) - } catch (ex: InvalidClaimStatus) { - val msg = "Invalid claim status, xRequestId=$xRequestId, status=${ex.status}" - val response: InlineResponse4002 = InlineResponse4002() - .code(InlineResponse4002.CodeEnum.INVALIDCLAIMSTATUS) - .message(msg) - throw BadRequestException(msg, ex, response) - } catch (ex: InvalidClaimRevision) { - val msg = "Invalid claim revision, xRequestId=$xRequestId, claimRevision=$claimRevision" - val response: InlineResponse4002 = InlineResponse4002() - .code(InlineResponse4002.CodeEnum.INVALIDCLAIMREVISION) - .message(msg) - throw BadRequestException(msg, ex, response) - } catch (ex: ClaimNotFound) { - throw buildNotFoundException(xRequestId!!, claimId!!, ex) - } catch (ex: TException) { - throw buildDarkApi5xxException("requestClaimReviewById", xRequestId, ex) } - } @PreAuthorize("hasAuthority('party:read')") override fun searchClaims( @@ -189,12 +117,12 @@ class ClaimManagementController( @Size(min = 1, max = 40) continuationToken: String?, @Valid claimId: Long?, @Valid claimStatuses: List? - ): ResponseEntity { - return try { + ): ResponseEntity = + performRequest("searchClaims", xRequestId!!, claimId!!) { log.info { "Process 'searchClaims' get started, xRequestId=$xRequestId, claimId=$claimId" } partyManagementService.checkStatus(xRequestId) deadlineChecker.checkDeadline(xRequestDeadline, xRequestId) - val response: InlineResponse200 = claimManagementService.searchClaims( + val response = claimManagementService.searchClaims( keycloakService.partyId, limit!!, continuationToken, @@ -205,96 +133,75 @@ class ClaimManagementController( "For status list, xRequestId=$xRequestId, claimId=$claimId, list statuses=$claimStatuses, " + "size results=${response.result.size}" } + ResponseEntity.ok(response) - } catch (ex: DeadlineException) { - val msg = ex.message - val response: InlineResponse400 = InlineResponse400() - .code(InlineResponse400.CodeEnum.INVALIDDEADLINE) - .message(msg) - throw BadRequestException(msg, ex, response) - } catch (ex: LimitExceeded) { - val msg = "Limit exceeded, xRequestId=$xRequestId" - val response: InlineResponse400 = InlineResponse400() - .code(InlineResponse400.CodeEnum.LIMITEXCEEDED) - .message(msg) - throw BadRequestException(msg, ex, response) - } catch (ex: BadContinuationToken) { - val msg = "Bad continuation token, xRequestId=$xRequestId, reason=${ex.reason}" - val response: InlineResponse400 = InlineResponse400() - .code(InlineResponse400.CodeEnum.BADCONTINUATIONTOKEN) - .message(msg) - throw BadRequestException(msg, ex, response) - } catch (ex: TException) { - throw buildDarkApi5xxException("searchClaims", xRequestId, ex) } - } @PreAuthorize("hasAuthority('party:write')") override fun updateClaimByID( @NotNull @Size(min = 1, max = 40) xRequestId: String?, - @NotNull @Valid claimId: Long, + @NotNull @Valid claimId: Long?, @NotNull @Valid claimRevision: Int?, @NotNull @Valid changeset: List?, @Size(min = 1, max = 40) xRequestDeadline: String? - ): ResponseEntity { - return try { + ): ResponseEntity = + performRequest("updateClaimByID", xRequestId!!, claimId!!, claimRevision!!) { log.info { "Process 'updateClaimByID' get started, requestId=$xRequestId, claimId=$claimId" } partyManagementService.checkStatus(xRequestId) deadlineChecker.checkDeadline(xRequestDeadline, xRequestId) - claimManagementService.updateClaimById(keycloakService.partyId, claimId, claimRevision!!, changeset!!) + claimManagementService.updateClaimById(keycloakService.partyId, claimId, claimRevision, changeset!!) log.info { "Successful update claim, xRequestId=$xRequestId, claimId=$claimId" } + ResponseEntity(HttpStatus.OK) + } + + private fun performRequest( + methodName: String, + xRequestId: String, + claimId: Long? = null, + claimRevision: Int? = null, + operation: () -> ResponseEntity + ): ResponseEntity = + try { + operation.invoke() } catch (ex: DeadlineException) { val msg = ex.message - val response: InlineResponse4003 = InlineResponse4003() - .code(InlineResponse4003.CodeEnum.INVALIDDEADLINE) - .message(msg) + val response = ErrorData(ErrorData.Code.INVALIDDEADLINE, msg) throw BadRequestException(msg, ex, response) } catch (ex: InvalidClaimStatus) { val msg = "Invalid claim status, xRequestId=$xRequestId, status=${ex.status}" - val response: InlineResponse4003 = InlineResponse4003() - .code(InlineResponse4003.CodeEnum.INVALIDCLAIMSTATUS) - .message(msg) + val response = ErrorData(ErrorData.Code.INVALIDCLAIMSTATUS, msg) throw BadRequestException(msg, ex, response) } catch (ex: InvalidClaimRevision) { val msg = "Invalid claim revision, xRequestId=$xRequestId, claimRevision=$claimRevision" - val response: InlineResponse4003 = InlineResponse4003() - .code(InlineResponse4003.CodeEnum.INVALIDCLAIMREVISION) - .message(msg) + val response = ErrorData(ErrorData.Code.INVALIDCLAIMREVISION, msg) throw BadRequestException(msg, ex, response) } catch (ex: ChangesetConflict) { val msg = "Changeset conflict, xRequestId=$xRequestId, conflictedId=${ex.conflictedId}" - val response: InlineResponse4003 = InlineResponse4003() - .code(InlineResponse4003.CodeEnum.CHANGESETCONFLICT) - .message(msg) + val response = ErrorData(ErrorData.Code.CHANGESETCONFLICT, msg) throw BadRequestException(msg, ex, response) } catch (ex: InvalidChangeset) { val msg = "Invalid changeset, xRequestId=$xRequestId, reason='${ex.reason}'" - val response: InlineResponse4003 = InlineResponse4003() - .code(InlineResponse4003.CodeEnum.INVALIDCHANGESET) - .message(msg) + val response = ErrorData(ErrorData.Code.INVALIDCHANGESET, msg) + throw BadRequestException(msg, ex, response) + } catch (ex: LimitExceeded) { + val msg = "Limit exceeded, xRequestId=$xRequestId" + val response = ErrorData(ErrorData.Code.LIMITEXCEEDED, msg) + throw BadRequestException(msg, ex, response) + } catch (ex: BadContinuationToken) { + val msg = "Bad continuation token, xRequestId=$xRequestId, reason=${ex.reason}" + val response = ErrorData(ErrorData.Code.BADCONTINUATIONTOKEN, msg) throw BadRequestException(msg, ex, response) } catch (ex: ClaimNotFound) { - throw buildNotFoundException(xRequestId!!, claimId, ex) + val msg = "Claim not found, claimId=$claimId, xRequestId=$xRequestId" + val response = ErrorData(message = msg) + throw NotFoundException(msg, ex, response) } catch (ex: TException) { - throw buildDarkApi5xxException("updateClaimByID", xRequestId, ex) + throw DarkApi5xxException( + "Some TException while requesting api='$API_NAME', method='$methodName', xRequestId=$xRequestId", + ex + ) } - } - - private fun buildNotFoundException(xRequestId: String, claimId: Long, ex: ClaimNotFound): NotFoundException { - val msg = "Claim not found, claimId=$claimId, xRequestId=$xRequestId" - val response = GeneralError().message(msg) - return NotFoundException(msg, ex, response) - } - - private fun buildDarkApi5xxException( - methodName: String?, - xRequestId: String?, - ex: TException? - ) = DarkApi5xxException( - "Some TException while requesting api='$API_NAME', method='$methodName', xRequestId=$xRequestId", - ex - ) companion object { const val API_NAME = "claim-management" diff --git a/src/main/kotlin/com/rbkmoney/claimmanagementapi/controller/ErrorControllerAdvice.kt b/src/main/kotlin/com/rbkmoney/claimmanagementapi/controller/ErrorControllerAdvice.kt new file mode 100644 index 0000000..1eb6e81 --- /dev/null +++ b/src/main/kotlin/com/rbkmoney/claimmanagementapi/controller/ErrorControllerAdvice.kt @@ -0,0 +1,127 @@ +package com.rbkmoney.claimmanagementapi.controller + +import com.rbkmoney.claimmanagementapi.exception.client.BadRequestException +import com.rbkmoney.claimmanagementapi.exception.client.DarkApi4xxException +import com.rbkmoney.claimmanagementapi.exception.client.ForbiddenException +import com.rbkmoney.claimmanagementapi.exception.client.NotFoundException +import com.rbkmoney.claimmanagementapi.exception.server.DarkApi5xxException +import mu.KotlinLogging +import org.springframework.http.HttpHeaders +import org.springframework.http.HttpStatus +import org.springframework.http.ResponseEntity +import org.springframework.security.access.AccessDeniedException +import org.springframework.web.HttpMediaTypeNotAcceptableException +import org.springframework.web.HttpMediaTypeNotSupportedException +import org.springframework.web.bind.MethodArgumentNotValidException +import org.springframework.web.bind.MissingServletRequestParameterException +import org.springframework.web.bind.annotation.ExceptionHandler +import org.springframework.web.bind.annotation.ResponseStatus +import org.springframework.web.bind.annotation.RestControllerAdvice +import org.springframework.web.client.HttpClientErrorException +import org.springframework.web.context.request.WebRequest +import java.lang.Exception +import java.net.http.HttpTimeoutException + +@RestControllerAdvice +class ErrorControllerAdvice { + + private val log = KotlinLogging.logger { } + + // ----------------- 4xx ----------------------------------------------------- + @ExceptionHandler(BadRequestException::class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + fun handleBadRequestException(e: BadRequestException): Any { + log.warn(e) { "<- Res [400]: Not valid" } + return e.response + } + + @ExceptionHandler(MethodArgumentNotValidException::class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + fun handleMethodArgumentNotValidException(e: MethodArgumentNotValidException) { + log.warn(e) { "<- Res [400]: MethodArgument not valid" } + } + + @ExceptionHandler(MissingServletRequestParameterException::class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + fun handleMissingServletRequestParameterException(e: MissingServletRequestParameterException) { + log.warn(e) { "<- Res [400]: Missing ServletRequestParameter" } + } + + @ExceptionHandler(AccessDeniedException::class) + @ResponseStatus(HttpStatus.FORBIDDEN) + fun handleAccessDeniedException(e: AccessDeniedException) { + log.warn(e) { "<- Res [403]: Request denied access" } + } + + @ExceptionHandler(ForbiddenException::class) + @ResponseStatus(HttpStatus.FORBIDDEN) + fun handleForbiddenException(e: ForbiddenException) { + log.warn(e) { "<- Res [403]: Request denied access" } + } + + @ExceptionHandler(NotFoundException::class) + @ResponseStatus(HttpStatus.NOT_FOUND) + fun handleFileNotFoundException(e: NotFoundException): Any { + log.warn(e) { "<- Res [404]: Not found" } + return e.response + } + + @ExceptionHandler(HttpMediaTypeNotAcceptableException::class) + @ResponseStatus(HttpStatus.NOT_ACCEPTABLE) + fun handleHttpMediaTypeNotAcceptable(e: HttpMediaTypeNotAcceptableException) { + log.warn(e) { "<- Res [406]: MediaType not acceptable" } + } + + @ExceptionHandler(HttpMediaTypeNotSupportedException::class) + fun handleHttpMediaTypeNotSupported( + e: HttpMediaTypeNotSupportedException, + request: WebRequest + ): ResponseEntity<*> { + log.warn(e) { "<- Res [415]: MediaType not supported" } + return ResponseEntity.status(HttpStatus.UNSUPPORTED_MEDIA_TYPE) + .headers(httpHeaders(e)) + .build() + } + + @ExceptionHandler(DarkApi4xxException::class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + fun handleDarkApiClientException(e: DarkApi4xxException) { + log.warn(e) { "<- Res [400]: Unrecognized Error by controller" } + } + + // ----------------- 5xx ----------------------------------------------------- + @ExceptionHandler(HttpClientErrorException::class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + fun handleHttpClientErrorException(e: HttpClientErrorException) { + log.error(e) { + "<- Res [500]: Error with using inner http client, code=${e.statusCode}, body=${e.responseBodyAsString}" + } + } + + @ExceptionHandler(HttpTimeoutException::class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + fun handleHttpTimeoutException(e: HttpTimeoutException) { + log.error(e) { "<- Res [500]: Timeout with using inner http client" } + } + + @ExceptionHandler(DarkApi5xxException::class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + fun handleHttpTimeoutException(e: DarkApi5xxException) { + log.error(e) { "<- Res [500]: Unrecognized inner error" } + } + + @ExceptionHandler(Exception::class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + fun handleException(e: Exception) { + log.error(e) { "<- Res [500]: Unrecognized inner error" } + } + + private fun httpHeaders(e: HttpMediaTypeNotSupportedException): HttpHeaders { + val headers = HttpHeaders() + val mediaTypes = e.supportedMediaTypes + if (mediaTypes.isNotEmpty()) { + headers.accept = mediaTypes + } + return headers + } +} diff --git a/src/main/kotlin/com/rbkmoney/claimmanagementapi/controller/ErrorData.kt b/src/main/kotlin/com/rbkmoney/claimmanagementapi/controller/ErrorData.kt new file mode 100644 index 0000000..21d7b8a --- /dev/null +++ b/src/main/kotlin/com/rbkmoney/claimmanagementapi/controller/ErrorData.kt @@ -0,0 +1,26 @@ +package com.rbkmoney.claimmanagementapi.controller + +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.annotation.JsonValue + +@JsonInclude(JsonInclude.Include.NON_NULL) +data class ErrorData( + @field:JsonProperty("code") + val code: Code? = null, + @field:JsonProperty("message") + val message: String? = null +) { + enum class Code(private val value: String) { + INVALIDCLAIMSTATUS("invalidClaimStatus"), + CHANGESETCONFLICT("changesetConflict"), + INVALIDCHANGESET("invalidChangeset"), + INVALIDCLAIMREVISION("invalidClaimRevision"), + LIMITEXCEEDED("limitExceeded"), + BADCONTINUATIONTOKEN("badContinuationToken"), + INVALIDDEADLINE("invalidDeadline"); + + @JsonValue + override fun toString() = value + } +} diff --git a/src/main/kotlin/com/rbkmoney/claimmanagementapi/converter/claim/ClaimExternalInfoModificationUnitConverter.kt b/src/main/kotlin/com/rbkmoney/claimmanagementapi/converter/claim/ClaimExternalInfoModificationUnitConverter.kt new file mode 100644 index 0000000..d6ac579 --- /dev/null +++ b/src/main/kotlin/com/rbkmoney/claimmanagementapi/converter/claim/ClaimExternalInfoModificationUnitConverter.kt @@ -0,0 +1,25 @@ +package com.rbkmoney.claimmanagementapi.converter.claim + +import com.rbkmoney.claimmanagementapi.converter.DarkApiConverter +import org.springframework.stereotype.Component +import com.rbkmoney.damsel.claim_management.ExternalInfoModificationUnit as ThriftExternalInfoModificationUnit +import com.rbkmoney.swag.claim_management.model.ClaimModificationType as SwagClaimModificationType +import com.rbkmoney.swag.claim_management.model.ExternalInfoModificationUnit as SwagExternalInfoModificationUnit + +@Component +class ClaimExternalInfoModificationUnitConverter : + DarkApiConverter { + + override fun convertToThrift(value: SwagExternalInfoModificationUnit) = + ThriftExternalInfoModificationUnit().apply { + documentId = value.documentId + roistatId = value.roistatId + } + + override fun convertToSwag(value: ThriftExternalInfoModificationUnit) = + SwagExternalInfoModificationUnit().apply { + documentId = value.documentId + roistatId = value.roistatId + claimModificationType = SwagClaimModificationType.ClaimModificationTypeEnum.EXTERNALINFOMODIFICATIONUNIT + } +} diff --git a/src/main/kotlin/com/rbkmoney/claimmanagementapi/converter/claim/ClaimModificationConverter.kt b/src/main/kotlin/com/rbkmoney/claimmanagementapi/converter/claim/ClaimModificationConverter.kt index b12bbd5..cd9f0f7 100644 --- a/src/main/kotlin/com/rbkmoney/claimmanagementapi/converter/claim/ClaimModificationConverter.kt +++ b/src/main/kotlin/com/rbkmoney/claimmanagementapi/converter/claim/ClaimModificationConverter.kt @@ -8,6 +8,7 @@ import com.rbkmoney.damsel.claim_management.Modification as ThriftModification import com.rbkmoney.swag.claim_management.model.ClaimModification as SwagClaimModification import com.rbkmoney.swag.claim_management.model.CommentModificationUnit as SwagCommentModificationUnit import com.rbkmoney.swag.claim_management.model.DocumentModificationUnit as SwagDocumentModificationUnit +import com.rbkmoney.swag.claim_management.model.ExternalInfoModificationUnit as SwagExternalInfoModificationUnit import com.rbkmoney.swag.claim_management.model.FileModificationUnit as SwagFileModificationUnit import com.rbkmoney.swag.claim_management.model.Modification as SwagModification import com.rbkmoney.swag.claim_management.model.StatusModificationUnit as SwagStatusModificationUnit @@ -17,7 +18,8 @@ class ClaimModificationConverter( private val documentModificationUnitConverter: ClaimDocumentModificationUnitConverter, private val commentModificationUnitConverter: ClaimCommentModificationUnitConverter, private val statusModificationUnitConverter: ClaimStatusModificationUnitConverter, - private val fileModificationUnitConverter: ClaimFileModificationUnitConverter + private val fileModificationUnitConverter: ClaimFileModificationUnitConverter, + private val extInfoModificationUnitConverter: ClaimExternalInfoModificationUnitConverter ) : DarkApiConverter { override fun convertToThrift(value: SwagClaimModification): ThriftModification { @@ -44,6 +46,11 @@ class ClaimModificationConverter( val fileModificationUnit = swagClaimModificationType as SwagFileModificationUnit claimModification.fileModification = fileModificationUnitConverter.convertToThrift(fileModificationUnit) } + ClaimModificationTypeEnum.EXTERNALINFOMODIFICATIONUNIT -> { + val extInfoModificationUnit = swagClaimModificationType as SwagExternalInfoModificationUnit + claimModification.externalInfoModification = + extInfoModificationUnitConverter.convertToThrift(extInfoModificationUnit) + } else -> throw IllegalArgumentException("Unknown claim modification type: $swagClaimModificationType") } modification.claimModification = claimModification @@ -74,6 +81,11 @@ class ClaimModificationConverter( swagClaimModification.claimModificationType = fileModificationUnitConverter.convertToSwag(fileModificationUnit) } + claimModification.isSetExternalInfoModification -> { + val externalInfoModificationUnit = claimModification.externalInfoModification + swagClaimModification.claimModificationType = + extInfoModificationUnitConverter.convertToSwag(externalInfoModificationUnit) + } else -> throw IllegalArgumentException("Unknown claim modification type") } swagClaimModification.modificationType = SwagModification.ModificationTypeEnum.CLAIMMODIFICATION diff --git a/src/test/kotlin/com/rbkmoney/claimmanagementapi/controller/ErrorControllerTest.kt b/src/test/kotlin/com/rbkmoney/claimmanagementapi/controller/ErrorControllerTest.kt new file mode 100644 index 0000000..1e934f1 --- /dev/null +++ b/src/test/kotlin/com/rbkmoney/claimmanagementapi/controller/ErrorControllerTest.kt @@ -0,0 +1,173 @@ +package com.rbkmoney.claimmanagementapi.controller + +import com.fasterxml.jackson.databind.ObjectMapper +import com.rbkmoney.claimmanagementapi.config.AbstractKeycloakOpenIdAsWiremockConfig +import com.rbkmoney.claimmanagementapi.service.ClaimManagementService +import com.rbkmoney.claimmanagementapi.service.ClaimManagementServiceTest +import com.rbkmoney.claimmanagementapi.service.PartyManagementService +import com.rbkmoney.damsel.claim_management.ChangesetConflict +import com.rbkmoney.damsel.claim_management.ClaimNotFound +import com.rbkmoney.damsel.claim_management.InvalidChangeset +import com.rbkmoney.damsel.claim_management.InvalidClaimRevision +import com.rbkmoney.damsel.claim_management.InvalidClaimStatus +import org.apache.thrift.TException +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.mockito.ArgumentMatchers +import org.mockito.kotlin.doAnswer +import org.mockito.kotlin.doNothing +import org.mockito.kotlin.reset +import org.mockito.kotlin.whenever +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.mock.mockito.MockBean +import org.springframework.http.MediaType +import org.springframework.test.web.servlet.MockMvc +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders +import org.springframework.test.web.servlet.result.MockMvcResultMatchers +import java.time.Instant +import java.time.temporal.ChronoUnit +import java.util.UUID + +class ErrorControllerTest : AbstractKeycloakOpenIdAsWiremockConfig() { + + @Autowired + private lateinit var mockMvc: MockMvc + + @Autowired + private lateinit var objectMapper: ObjectMapper + + @MockBean + private lateinit var partyManagementService: PartyManagementService + + @MockBean + private lateinit var claimManagementService: ClaimManagementService + + @BeforeEach + fun setUp() { + doNothing().whenever(partyManagementService).checkStatus(ArgumentMatchers.anyString()) + doNothing().whenever(partyManagementService).checkStatus() + } + + @Test + fun testThenClaimManagementClientThrowingExceptions() { + whenever(claimManagementService.getClaimById(ArgumentMatchers.anyString(), ArgumentMatchers.anyLong())) + .doAnswer { throw ClaimNotFound() } + mockMvc.perform( + MockMvcRequestBuilders.get("/processing/claims/{claimID}", ArgumentMatchers.anyLong()) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .header("Authorization", "Bearer " + generateReadJwt()) + .header("X-Request-ID", string()) + .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) + ).andExpect(MockMvcResultMatchers.status().isNotFound) + reset(claimManagementService) + whenever( + claimManagementService.updateClaimById( + ArgumentMatchers.anyString(), + ArgumentMatchers.anyLong(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.anyList() + ) + ).doAnswer { throw ClaimNotFound() } + mockMvc.perform( + MockMvcRequestBuilders.put("/processing/claims/{claimID}/update", ArgumentMatchers.anyLong()) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .header("Authorization", "Bearer " + generateWriteJwt()) + .header("X-Request-ID", string()) + .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) + .param("claimRevision", 123.toString()) + .content(objectMapper.writeValueAsBytes(ClaimManagementServiceTest.modifications)) + ).andExpect(MockMvcResultMatchers.status().isNotFound) + reset(claimManagementService) + whenever( + claimManagementService.updateClaimById( + ArgumentMatchers.anyString(), + ArgumentMatchers.anyLong(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.anyList() + ) + ).doAnswer { throw InvalidClaimStatus() } + mockMvc.perform( + MockMvcRequestBuilders.put("/processing/claims/{claimID}/update", ArgumentMatchers.anyLong()) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .header("Authorization", "Bearer " + generateWriteJwt()) + .header("X-Request-ID", string()) + .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) + .param("claimRevision", 123.toString()) + .content(objectMapper.writeValueAsBytes(ClaimManagementServiceTest.modifications)) + ).andExpect(MockMvcResultMatchers.status().isBadRequest) + reset(claimManagementService) + whenever( + claimManagementService.updateClaimById( + ArgumentMatchers.anyString(), + ArgumentMatchers.anyLong(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.anyList() + ) + ).doAnswer { throw InvalidClaimRevision() } + mockMvc.perform( + MockMvcRequestBuilders.put("/processing/claims/{claimID}/update", ArgumentMatchers.anyLong()) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .header("Authorization", "Bearer " + generateWriteJwt()) + .header("X-Request-ID", string()) + .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) + .param("claimRevision", 123.toString()) + .content(objectMapper.writeValueAsBytes(ClaimManagementServiceTest.modifications)) + ).andExpect(MockMvcResultMatchers.status().isBadRequest) + reset(claimManagementService) + whenever( + claimManagementService.updateClaimById( + ArgumentMatchers.anyString(), + ArgumentMatchers.anyLong(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.anyList() + ) + ).doAnswer { throw ChangesetConflict() } + mockMvc.perform( + MockMvcRequestBuilders.put("/processing/claims/{claimID}/update", ArgumentMatchers.anyLong()) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .header("Authorization", "Bearer " + generateWriteJwt()) + .header("X-Request-ID", string()) + .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) + .param("claimRevision", 123.toString()) + .content(objectMapper.writeValueAsBytes(ClaimManagementServiceTest.modifications)) + ).andExpect(MockMvcResultMatchers.status().isBadRequest) + reset(claimManagementService) + whenever( + claimManagementService.updateClaimById( + ArgumentMatchers.anyString(), + ArgumentMatchers.anyLong(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.anyList() + ) + ).doAnswer { throw InvalidChangeset() } + mockMvc.perform( + MockMvcRequestBuilders.put("/processing/claims/{claimID}/update", ArgumentMatchers.anyLong()) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .header("Authorization", "Bearer " + generateWriteJwt()) + .header("X-Request-ID", string()) + .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) + .param("claimRevision", 123.toString()) + .content(objectMapper.writeValueAsBytes(ClaimManagementServiceTest.modifications)) + ).andExpect(MockMvcResultMatchers.status().isBadRequest) + reset(claimManagementService) + whenever( + claimManagementService.updateClaimById( + ArgumentMatchers.anyString(), + ArgumentMatchers.anyLong(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.anyList() + ) + ).doAnswer { throw TException() } + mockMvc.perform( + MockMvcRequestBuilders.put("/processing/claims/{claimID}/update", ArgumentMatchers.anyLong()) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .header("Authorization", "Bearer " + generateWriteJwt()) + .header("X-Request-ID", string()) + .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) + .param("claimRevision", 123.toString()) + .content(objectMapper.writeValueAsBytes(ClaimManagementServiceTest.modifications)) + ).andExpect(MockMvcResultMatchers.status().isInternalServerError) + } + + private fun string() = UUID.randomUUID().toString() +} diff --git a/src/test/kotlin/com/rbkmoney/claimmanagementapi/converter/claim/ClaimConvertersTest.kt b/src/test/kotlin/com/rbkmoney/claimmanagementapi/converter/claim/ClaimConvertersTest.kt index dbf5982..6b886c1 100644 --- a/src/test/kotlin/com/rbkmoney/claimmanagementapi/converter/claim/ClaimConvertersTest.kt +++ b/src/test/kotlin/com/rbkmoney/claimmanagementapi/converter/claim/ClaimConvertersTest.kt @@ -156,7 +156,8 @@ class ClaimConvertersTest { ClaimDocumentModificationUnitConverter(), ClaimCommentModificationUnitConverter(), ClaimStatusModificationUnitConverter(ClaimStatusModificationConverter()), - ClaimFileModificationUnitConverter() + ClaimFileModificationUnitConverter(), + ClaimExternalInfoModificationUnitConverter() ) val swagStatusModUnit = SwagStatusModificationUnit().apply { claimModificationType = ClaimModificationTypeEnum.STATUSMODIFICATIONUNIT diff --git a/src/test/kotlin/com/rbkmoney/claimmanagementapi/service/ClaimManagementServiceTest.kt b/src/test/kotlin/com/rbkmoney/claimmanagementapi/service/ClaimManagementServiceTest.kt index dbb0233..ee8fd9b 100644 --- a/src/test/kotlin/com/rbkmoney/claimmanagementapi/service/ClaimManagementServiceTest.kt +++ b/src/test/kotlin/com/rbkmoney/claimmanagementapi/service/ClaimManagementServiceTest.kt @@ -133,7 +133,7 @@ class ClaimManagementServiceTest { return changeset } - private val modifications: List + val modifications: List get() { val documentModification = DocumentModification() .apply { documentModificationType = DocumentModificationTypeEnum.DOCUMENTCREATED }