How to return Generic List in FallbackHandler using Kotlin - kotlin

I am trying to return List with Generic type from handle(context: ExecutionContext?) method of MicroProfile FallbackHandler using Kotlin. But it's throwing exception like " org.eclipse.microprofile.faulttolerance.exceptions.FaultToleranceDefinitionException: Invalid #Fallback on getDistanceData(java.lang.String): fallback handler's type java.util.List<? extends java.lang.String> is not the same as method's return type
"
RestClient :
#GET
#Path("/testFallbackUrl")
#Fallback(DistanceServiceFallback::class)
#CircuitBreaker(
requestVolumeThreshold = 4, failureRatio = 0.75, delay = 5000, successThreshold = 3
)
fun getDistanceData(#QueryParam("date") date: String) : List<String>
Handler:
#RegisterForReflection
class DistanceServiceFallback : FallbackHandler<List<String>> {
#field:Default
#field:Inject
lateinit var logger: Logger
override fun handle(context: ExecutionContext?): List<String> {
logger.error("Inside DistanceServiceFallback handler. ")
return listOf("Hello")
}
}

This is because of a difference in type inference when your kotlin code is processed in Java. The return type of getDistanceData is java.util.List<String> and the handler's return type is as mentioned in the exception java.util.List<? extends java.lang.String>.
The return type java.util.List<String> is observed from the CDI interceptor so I am not sure how exactly it is extracted but obviously it is not carrying <? extends E> information.
The handler's type java.util.List<? extends java.lang.String> is on the other hand extracted from kotlin.collections.List which is defined as public interface List<out E> : Collection<E> which I think is correct as <out E> should translate to <? extends E>.
However, there is an easy workaround:
#GET
#Path("/testFallbackUrl")
#Fallback(DistanceServiceFallback::class)
#CircuitBreaker(
requestVolumeThreshold = 4, failureRatio = 0.75, delay = 5000, successThreshold = 3
)
fun getDistanceData(#QueryParam("date") date: String) : MutableList<String> {
return mutableListOf("Default")
}
#RegisterForReflection
class DistanceServiceFallback : FallbackHandler<MutableList<String>> {
#field:Default
#field:Inject
lateinit var logger: Logger
override fun handle(context: ExecutionContext?): MutableList<String> {
logger.error("Inside DistanceServiceFallback handler. ")
return mutableListOf("Hello")
}
}
which works because MutableList is defined as public interface MutableList<E> : List<E>, MutableCollection<E> and thus it has now an exact generic type.

Related

get Not enough information to infer type variable T when initializing kotlin raw types

I tried to write raw types for. kotlin in a class and initialized the raw type
class RawType {
interface I<T : RawType?> {
fun f(t: T) {}
}
class RawSubclass: I<T: RawType?> {
override fun f(t: RawType?) {}
}
fun test() {
val unbound = Unbound<T: RawType?>()
}
}
the java I can write
class RawType {
interface I<T extends RawType> {
default void f(T t) {}
}
class BoundRecursively<T extends BoundRecursively<T>> {}
static class RawSubclass implements I {
#Override
public void f(RawType t) {}
}
test() {
Unbound unbound = new Unbound();
}
}
but it gives error Expecting a '>' Expecting member declaration for line class RawSubclass: I<T: RawType?> and Not enough information to infer type variable T for val unbound = Unbound<T: RawType?>(). Any suggestion why the error occurs and how to fix it? Thanks

How do I override sealed class fields?

I created a custom Result field (And not Kotlin's Result) in my service so I can return a message field in Both Success & Failure Cases:
sealed class Result<T> {
data class Success<T>(val value: T, val message: String) : Result<T>()
data class Failure<T>(val throwable: Throwable? = null, val message: String) : Result<T>() {
val isExceptional = throwable != null
val error: Throwable
get() = throwable ?: error("Error is undefined in [$this]")
}
}
Then in another class I called a method that produces this result and wanted to log Result.message
logger.info { "Finished with message [${result.message}]." }
Only, kotlin compiler doesn't recognize "message" since it's not a property of Result directly, but a property of Success and Failure.
I tried to override the message field and define it in Result class. But I get an Error.
Error:(10, 38) Kotlin: 'message' in 'Result' is final and cannot be overridden
So, how can one accesss Result.message without casting the result instance to it's derived implementing class (Success or Failure)?
A clean solution that I found is the following.
While Kotlin does not allow us to override sealed class members. It does allow us to override interface members.
So, I Created a simple interface for the message field, and implemented it from Result class:
interface ResultMessage {
val message: String
}
sealed class Result<T> : ResultMessage
// Now I'm able to override the message field simply, with no error
data class Success<T>(val value: T, override val message: String) : Result<T>()
data class Failure<T>(val throwable: Throwable? = null, override val message: String) : Result<T>() {
val isExceptional = throwable != null
val error: Throwable
get() = throwable ?: error("Error is undefined in [$this]")
}
You can declare an abstract property in the Result class:
sealed class Result<T> {
abstract val message: String
data class Success<T>(val value: T, override val message: String) : Result<T>()
data class Failure<T>(val throwable: Throwable? = null, override val message: String) : Result<T>() {
val isExceptional = throwable != null
val error: Throwable
get() = throwable ?: error("Error is undefined in [$this]")
}
}
Though, your solution is also an option
1- you can mark them open
open val message: String = ""
2- you can define them abstract
abstract val message: String

how to make mockito test case with Constructor injection

I want to make junit test case with mockito
there are Classes.
MyProps : the properties data class #Configuration
MyService : my main logic #Service class
MyClient : webClient class #Component
how to test myService result logic with mockito?? and how to make data class(MyProps) simply???
there error is like this :
You haven't provided the instance at field declaration so I tried to construct the instance.
However the constructor or the initialization block threw an exception : Parameter specified as non-null is null:
#Configuration
#ConfigurationProperties("application.something")
class MyProps {
lateinit var broker: String
lateinit var url: String
}
#Service
class MyService(private val client: MyClient,
private val myProps: MyProps
){
fun getReulst(params: Params): Flux<MyEntity> {
// some logic, and I want check this part result
return client.get(params)
}
}
#Component
class MyClient(val myProps: MyProps) {
private val webClient: WebClient = WebClient.create()
fun get(params: Params): Flux<MyEntity> {
val params = BodyInserters.fromValue(query)
return webClient
.post()
.uri(myProps.broker)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.body(params)
.retrieve()
.bodyToFlux(MyEntity::class.java)
}
}
here is test code
//----------------
#SpringBootTest(classes = [MyService::class, MyProps::class, MyClient::class])
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
class MyServiceTest {
#MockBean
private lateinit var myProps: MyProps
#MockBean
private lateinit var myClient: MyClient
#InjectMocks
private lateinit var countService: MyService
#BeforeEach
fun mock() {
given(myProps.broker).willReturn("http://url.com")
given(myProps.web).willReturn("something")
given(myClient.get2(query)).willReturn(
Flux.fromIterable(listOf(
MyEntity("2020-03-22", Count(1)),
MyEntity("2020-03-23", Count(2)),
MyEntity("2020-03-24", Count(3)),
MyEntity("2020-03-25", Count(6)),
MyEntity("2020-03-26", Count(5)),
MyEntity("2020-03-27", Count(4))
))
)
MockitoAnnotations.initMocks(this)
}
#Test
fun `I want to check!! here`(){
val param = Params(1,2,3) // some params
myService.getReulst(param).subscribe() // <- here is error maybe param is null... why??
}
}
Since you're injecting mocks into your service you don't really need a full-blown spring boot test. This will make your tests faster.
Furthermore, since you're mocking MyClient, you're not really calling its code. Therefore whatever happens in MyClient can be ignored here. You'll cover it in its own test.
Here's how I'd write this test with JUnit 5 (you'll have to translate it to JUnit 4 if you're still on it):
#ExtendWith(MockitoExtension::class)
class MyServiceTest(
#Mock val myProps: MyProps,
#Mock val myClient: MyClient
) {
// you can use #InjectMocks if you prefer
val countService: MyService = MyService(myProps, myClient)
#BeforeEach
fun mock() {
// you only need these if they're called in MyService
`when`(myProps.broker).thenReturn("http://url.com")
`when`(myProps.web).thenReturn("something")
`when`(myClient.get(Params(1,2,3))).thenReturn(
Flux.fromIterable(listOf(
MyEntity("2020-03-22", Count(1)),
MyEntity("2020-03-23", Count(2)),
MyEntity("2020-03-24", Count(3)),
MyEntity("2020-03-25", Count(6)),
MyEntity("2020-03-26", Count(5)),
MyEntity("2020-03-27", Count(4))
))
)
}
#Test
fun `I want to check!! here`(){
val param = Params(1,2,3)
myService.getReulst(param).subscribe()
}
}

How do I use TestPropertyProvider and inject RxHttpClient with Micronaut using Kotlin

Note - I'm a Java+Spring guy trying out Kotlin+Micronaut.
I'm trying to use the TestPropertyProvider to set properties after my embedded service starts.
It works ok, as long as there are no constructor parameters in my test class.
I can add the RxHttpClient as a constructor parameter and it gets injected fine.
But, I'd like to inject the RxHttpClient from Micronaut and also implement TestPropertyProvider.
I tried adding #Inject to the RxHttpClient but get the error This annotation is not applicable to target 'local variable' [because the test body is a lambda passed to the superclass]
Without the #Inject I get the error lateinit property client has not been initialized
My base class has the TestPropertyProvider implementation .
abstract class ZeebeSpecification(body: AbstractStringSpec.() -> Unit): StringSpec(body), TestPropertyProvider {
override fun getProperties(): MutableMap<String, String> {
return mutableMapOf("orchestrator.management.client.brokerContactPoint" to IntegrationTestHarness.instance.getBroker())
}
}
TestPropertyProvider works, but RxHttpClient not injected
#MicronautTest
class ZeebeBroker1Test() : ZeebeSpecification({
#Client("/") lateinit var client: RxHttpClient;
...tests
}) {}
RxHttpClient injected, but TestPropertyProvider not evaluated
#MicronautTest
class ZeebeBroker1Test(#Client("/" val client: RxHttpClient) : ZeebeSpecification({
...tests
}) {}
I removed the base class from the equation and made my test directly implement the TestPropertyProvider but it still fails.
#MicronautTest
class ZeebeBroker1Test(#Client("/") var client: HttpClient) : BehaviorSpec(), TestPropertyProvider {
init {
...tests
}
private fun getBroker(): String {
return IntegrationTestHarness.instance.getBroker()
}
override fun getProperties(): MutableMap<String, String> {
return mutableMapOf("orchestrator.management.client.brokerContactPoint" to getBroker())
}
}
Seems like it's the same issue as this, but I'm already using v1.1.2
https://github.com/micronaut-projects/micronaut-test/issues/82
If I tried to use #Inject #Client("/") client: RxHttpClient it would throw the error message: Missing bean argument [LoadBalancer loadBalancer] for type: io.micronaut.http.client.DefaultHttpClient. Required arguments: LoadBalancer
How do I use both TestPropertyProvider and injected RxHttpClient?
I resolved the issue by moving the body of the spec into the init, and injecting the RxHttpClient as a field.
#MicronautTest
class ZeebeBroker1Test() : ZeebeSpecification() {
#Inject #field:Client("/") lateinit var client: RxHttpClient
private val log: Logger = LoggerFactory.getLogger(ZeebeBroker1Test::class.java)
init {
"test case 1" {
...
}
"test case 2" {
...
}
}
}
And I let the base class implement the TestPropertyProvider interface .
abstract class ZeebeSpecification(): StringSpec(), TestPropertyProvider {
open fun getBroker(): String {
return IntegrationTestHarness.instance.getBroker()
}
override fun getProperties(): MutableMap<String, String> {
return mutableMapOf("orchestrator.management.client.brokerContactPoint" to getBroker())
}
}

Type inference only works for extension function

The following code works fine and the call to the foo.get() extension function returns the correct type BarImpl.
open class Bar
class BarImpl: Bar()
class Foo<T : Bar>
inline fun <reified T : Bar> Foo<T>.get(): T {
return SomeMap(this).get(T::class)
}
class Activity {
lateinit var foo: Foo<BarImpl>
val barImpl = foo.get()
}
But when I try to move Foo<T>.get() into the class the type inference fails
class Foo<T : Bar> {
inline fun <reified T : Bar> get(): T {
return SomeMap(this).get(T::class)
}
}
class Activity {
lateinit var foo: Foo<BarImpl>
val barImpl = foo.get()
}
error: type inference failed: Not enough information to infer parameter T in inline fun get(): T
Please specify it explicitly.
val vm = foo.get()
^
How can I move the function into the class?
The extension function returns the result of the Foo type parameter. So the result type can be inferred from the receiver type.
And the member function result type has nothing in common with Foo type parameter except the name, which means nothing for a compiler. You can see that T in method and T in class are different types by writing and compiling the following code:
Foo<BarImpl>().get<BarImpl2>()
If you want to make get to be a member function which returns the result of Foo type parameter, you should remove type parameter from function and inject class instance via the constructor:
class Foo<T : Bar>(private val clazz: KClass<T>) {
fun get(): T {
return SomeMap(this).get(clazz)
}
companion object {
inline operator fun <reified T : Bar> invoke() = Foo(T::class)
}
}