I am using import java.net.http.HttpClient
and my code is as follows:
try {
val response = httpClient.send(httpRequest, BodyHandlers.ofString())
...
when (response.statusCode()) {
200 -> {
result = decodedResponse
} else -> {
val errorResponse = Json.decodeFromString<ErrorObject>(response.body())
throw handleCustomError(errorResponse.error, errorResponse.error_description)
}
}
return result
} catch (ex: Exception) {
throw Exception("Service is unavailable!")
}
my handleCustomError iterates over the different types of status codes 401,403,404 etc and throws user friendly exception. But I dont think that code is ever reached. Instead I see the generic exception thrown by the catch block.
How can I make sure to iterate over the different status code?
Related
i am new to reactor, i tried to create a flux from Iterable. then i want to convert my object into string by using object mapper. then the ide warns a message like this in this part of the code new ObjectMapper().writeValueAsString(event). the message Inappropriate blocking method call. there is no compile error. could you suggest a solution.
Flux.fromIterable(Arrays.asList(new Event(), new Event()))
.flatMap(event -> {
try {
return Mono.just(new ObjectMapper().writeValueAsString(event));
} catch (JsonProcessingException e) {
return Mono.error(e);
}
})
.subscribe(jsonStrin -> {
System.out.println("jsonStrin = " + jsonStrin);
});
I will give you an answer, but I don't pretty sure this is what you want. it seems like block the thread. so then you can't get the exact benefits of reactive if you block the thread. that's why the IDE warns you. you can create the mono with monoSink. like below.
AtomicReference<ObjectMapper> objectMapper = new AtomicReference<>(new ObjectMapper());
Flux.fromIterable(Arrays.asList(new Event(), new Event()))
.flatMap(event -> {
return Mono.create(monoSink -> {
try {
monoSink.success(objectMapper .writeValueAsString(event));
} catch (JsonProcessingException e) {
monoSink.error(e);
}
});
})
.cast(String.class) // this cast will help you to axact data type that you want to continue the pipeline
.subscribe(jsonString -> {
System.out.println("jsonString = " + jsonString);
});
please try out this method and check that error will be gone.
it doesn't matter if objectMapper is be a normal java object as you did. (if you don't change). it is not necessary for your case.
You need to do it like this:
Flux.fromIterable(Arrays.asList(new Event(), new Event()))
.flatMap(event -> {
try {
return Mono.just(new ObjectMapper().writeValueAsString(event));
} catch (JsonProcessingException e) {
return Mono.error(e);
}
})
.subscribe(jsonStrin -> {
System.out.println("jsonStrin = " + jsonStrin);
});
there're several elements inside configTypeBuilderList, if the value in ruleAttributes not same as the destinationField in ConfigTypeBuilder, it will log the error
ruleCriteriaList.forEach { configRuleCriteria ->
validateConfigTypeBuilder(configRuleCriteria.configTypeBuilderList, ruleAttributesNames)
}
private fun validateConfigTypeBuilder(configTypeBuilderList: List<ConfigTypeBuilder>, ruleAttributes: List<String>) {
val missAttributeList: MutableList<String> = mutableListOf()
configTypeBuilderList.forEach { configTypeBuilder ->
if(configTypeBuilder!= null) {
if (ruleAttributes.firstOrNull { ruleAttribute -> ruleAttribute == configTypeBuilder.destinationField } == null) {
if(!ruleAttributes.contains(configTypeBuilder.destinationField)) {
missAttributeList.add(configTypeBuilder.destinationField)
}
logger.error("{} is wrong", configTypeBuilder.destinationField)
}
}
}
The problem is each time there's only one element(configTypeBuilderList) go into validateConfigTypeBuilder, so the logger shows like this
logger.error("field1 is wrong")
logger.error("field2 is wrong")
...
What I need is, how can I modify my code in order to do this?
logger.error("field1, field2, field3 are wrong")
Edit
I tried the first solution, but I stuck here, I still get the same error result, the reason is because each time there's only one "destinationField", how can I make the list have all the error field, and then log the error, can I use continue or something?
Here are a couple of alternatives:
Add them to a list and log later.
fun foo()
val incorrectItems = mutableListOf<Any>()
// Do some stuff
// on error:
incorrectItems.add(someIncorrectItem)
// Do more stuff
// log the accumulated errors:
logger.error("${incorrectItems.joinToString("")} are wrong")
}
Partition your list into valid and invalid values. Log the invalid ones and process the good ones.
fun foo(someList: List<MyClass>) {
val (goodItems, badItems) = someList.partition { it.isValid() }
// ...where isValid() is whatever code you need to check is OK.
if (badItems.isNotEmpty()) {
logger.error("${badItems.joinToString("")} are wrong")
}
// Do stuff with goodItems
}
Iam catch FeignException, but im throws FeingClientException(customized exception).
Sonar says that this FeingClientExceptionis not coveraged by junit test.
Any idea to cover it.
My exception is like this:
public class FeignClientException extends RuntimeException {
public FeignClientException(FeignException ex, ErrorMessageEnum errorMessageEnum, ContextExceptionEnum contextExceptionEnum, String cpf) {
throwException(ex, contextExceptionEnum, errorMessageEnum, cpf);
}
private void throwException(FeignException ex, ContextExceptionEnum contextExceptionEnum, ErrorMessageEnum errorMessageEnum, String cpf) {
if (ex.status() == HttpStatus.NOT_FOUND.value() || ex instanceof FeignException.NotFound) {
throw new FeignClientNotFoundException(
errorMessageEnum != null ? errorMessageEnum.msgUserFormatted(cpf) : ErrorMessageEnum.REGISTRO_NAO_ENCONTRADO.msgUserFormatted(cpf),
errorMessageEnum != null ? errorMessageEnum.msgDeveloper() : ErrorMessageEnum.REGISTRO_NAO_ENCONTRADO.msgDeveloper(),
String.valueOf(HttpStatus.NOT_FOUND.value()),
contextExceptionEnum.value());
}
if (ex.status() == HttpStatus.SERVICE_UNAVAILABLE.value() || ex instanceof RetryableException) {
throw new ServicoForaDoArException(
ErrorMessageEnum.SERVICO_FORA_AR.msgUserFormatted(contextExceptionEnum.value()),
ErrorMessageEnum.SERVICO_FORA_AR.msgDeveloper(),
String.valueOf(HttpStatus.SERVICE_UNAVAILABLE.value()),
contextExceptionEnum.value());
}
if (ex.status() == HttpStatus.INTERNAL_SERVER_ERROR.value()) {
throw new FeignClientInternalServerErrorException(
ErrorMessageEnum.SERVICO_RETORNOU_ERRO.msgUserFormatted(contextExceptionEnum.value()),
ErrorMessageEnum.SERVICO_RETORNOU_ERRO.msgDeveloper(),
String.valueOf(HttpStatus.INTERNAL_SERVER_ERROR.value()),
contextExceptionEnum.value());
}
throw new FeignBadGatewayException (
ErrorMessageEnum.SERVICO_RETORNOU_ERRO.msgUserFormatted(contextExceptionEnum.value()),
ErrorMessageEnum.SERVICO_RETORNOU_ERRO.msgDeveloper(),
String.valueOf(HttpStatus.BAD_GATEWAY.value()),
contextExceptionEnum.value());
}
}
My test is:
void test2() {
doThrow(FeignException.NotFound.class).when(preAprovadoClient).consultaPreAprovadoCDC(Mockito.anyString(), Mockito.anyString());
assertThrows(FeignClientException.FeignClientNotFoundException.class, () ->{
PreAprovado preAprovado = preAprovadoGateway.consultarPreAprovadoCDC("01234567890");
});
}
I think you're mocking the wrong method. You're calling the method consultarPreAprovadoCDC with one parameter, but you have mocked it with two String parameters, maybe that is causing this confusion.
Try to either changing the call to use two String parameters or mock the method with only one anyString() as parameter.
I need to verify that a certain call is not made, when a previous method call throws an Exception.
// GIVEN
every { relaxedMock.eats() }.throws(NotHungryException())
// WHEN
sut.live()
// THEN
verify (exactly = 0) { relaxedMock2.sleeps() }
Problem with this code, it fails because of the Exception thrown and not because of the failed verification.
I understand that your WHEN block will always throw an exception.
In that case you have multiple options from my point of view:
Simple plain Kotlin. Wrap the WHEN block with a try-catch block, e.g. like this:
// GIVEN
every { relaxedMock.eats() }.throws(NotHungryException())
// WHEN
var exceptionThrown: Boolean = false
try {
sut.live()
} catch(exception: NotHungryException) {
// Maybe put some assertions on the exception here.
exceptionThrown = true
}
assertTrue(exceptionThrown)
// THEN
verify (exactly = 0) { relaxedMock2.sleeps() }
For a bit nicer code, you can use JUnit5 API's Assertions. assertThrows will expect an exception being thrown by a specific piece of code. It will fail the test, if no exception is thrown. Also it will return the thrown exception, for you to inspect it.
import org.junit.jupiter.api.Assertions
// GIVEN
every { relaxedMock.eats() }.throws(NotHungryException())
// WHEN
val exception = Assertions.assertThrows(NotHungryException::class.java) { sut.live() }
// THEN
verify (exactly = 0) { relaxedMock2.sleeps() }
If you're using Kotest you can use the shouldThrow assertion. Which also allows you to retrieve the thrown exception and validate its type.
import io.kotest.assertions.throwables.shouldThrow
// GIVEN
every { relaxedMock.eats() }.throws(NotHungryException())
// WHEN
val exception = shouldThrow<NotHungryException> { sut.live() }
// THEN
verify (exactly = 0) { relaxedMock2.sleeps() }
I had similar issue and found that my method is not surrounded by try catch. This mean the method will always throw exception.
Test
The unit test to verify the result when the following method is called while stubbing it with predefine Exception
#Test
fun returnSearchError() {
every { searchService.search(query) }.throws(BadSearchException())
val result = searchRepository.search(query)
assertEquals(SearchStates.SearchError, result)
}
Faulty code
fun search(query: String): SearchStates {
val result = searchService.search(query) // No try catch for the thrown exception
return try {
SearchStates.MatchingResult(result)
} catch (badSearchException: BadSearchException) {
SearchStates.SearchError
}
}
Refactored it to
fun search(query: String): SearchStates {
return try {
val result = searchService.search(query)
SearchStates.MatchingResult(result)
} catch (badSearchException: BadSearchException) {
SearchStates.SearchError
}
}
I'm new in kotlin world. So I have some problem. I'm using ktor framework and try to use ktor-locations (https://ktor.io/servers/features/locations.html#route-classes)
And as example
#Location("/show/{id}")
data class Show(val id: Int)
routing {
get<Show> { show ->
call.respondText(show.id)
}
}
Everything is good, when I try to get /show/1
But if route will be /show/test there is NumberFormatException, cause DefaultConversionService try to convert id to Int and can't do it.
So my question is, how can I catch this exception and return Json with some error data. For example, if not using locations I can do smt like this
routing {
get("/{id}") {
val id = call.parameters["id"]!!.toIntOrNull()
call.respond(when (id) {
null -> JsonResponse.failure(HttpStatusCode.BadRequest.value, "wrong id parameter")
else -> JsonResponse.success(id)
})
}
}
Thx for help!
You can do a simple try-catch in order to catch the parsing exception which is thrown when a string can not be converted to an integer.
routing {
get("/{id}") {
val id = try {
call.parameters["id"]?.toInt()
} catch (e : NumberFormatException) {
null
}
call.respond(when (id) {
null -> HttpStatusCode.BadRequest
else -> "The value of the id is $id"
})
}
}
Other way of handling exception is to use StatusPages module:
install(StatusPages) {
// catch NumberFormatException and send back HTTP code 400
exception<NumberFormatException> { cause ->
call.respond(HttpStatusCode.BadRequest)
}
}
This should work with using Location feature. Please note that Location is experimental above ktor version 1.0.