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 }