Akka-Http: how to timeout a HttpResponse strict entity in a test - kotlin

Here is my code
import akka.http.javadsl.Http
// some initialization omitted
inline fun <reified T> executeRequest(request: HttpRequest, crossinline onError: (HttpResponse) -> Unit): CompletionStage<T?> {
val unmarshaller = GsonMarshaller.unmarshaller(T::class.java)
return http.singleRequest(request).thenCompose { httpResponse: HttpResponse ->
if (httpResponse.status() == StatusCodes.OK || httpResponse.status() == StatusCodes.CREATED) {
unmarshaller.unmarshal(httpResponse.entity().withContentType(ContentTypes.APPLICATION_JSON), dispatcher, materializer)
} else {
onError(httpResponse) // invoke lambda to notify of error
httpResponse.discardEntityBytes(materializer)
CompletableFuture.completedFuture(null as T?)
}
}
}
class TradingActor(
val materializer: ActorMaterializer,
val dispatcher: ExecutionContextExecutor
): AbstractLoggingActor() {
fun submitNewOrder(request: Request, onFailed: (text: String) -> Unit) {
executeRequest<OrderAnswer>(request) {
it.entity().toStrict(5_000, materializer).thenApply { entity ->
onFailed("API Call Failed")
}
}.thenAccept {
println("OK")
}
}
}
I have to write a test checking that if .entity().toStrict(5_000, materializer) timeout expires then onFailed("API Call Failed") is called. The current code do not call onFailed("") in case of timeout, therefore I want this test.
my test contains
val response = akka.http.javadsl.model.HttpResponse.create()
.withStatus(StatusCodes.OK)
.withEntity("""{'s': 'text'}""")
Mockito.`when`(http.singleRequest(any()))
.then {
CompletableFuture.completedFuture<akka.http.javadsl.model.HttpResponse>(response)
}
but I don;t know how to make toStrict() expire.

As I understand from your question you can create mock object for ResponseEntity and create own implementation for toStrict() method that will have a delay. Something like in example below from here -> Can I delay a stubbed method response with Mockito?.
when(mock.load("a")).thenAnswer(new Answer<String>() {
#Override
public String answer(InvocationOnMock invocation){
Thread.sleep(5000);
return "ABCD1234";
}
});
Than you can set it in your response object.
val response = akka.http.javadsl.model.HttpResponse.create()
.withStatus(StatusCodes.OK)
.withEntity(mockedEntity)

Related

CompletableFuture<T> freezes UI

I am currently working on an api for my client application which needs to process http requests (using unirest) asynchronously as of now. I am new to CompletableFuture and haven't worked with anything similar up to this point. I was wondering whether the following structure makes sense:
// Request.kt (simplified)
class Request<T>(
// other variables relevant to the request such as body or path ...
private val responseType: Class<T>
) {
fun prepareRequest(action: (HttpRequest<*>) -> U): U {
// preprocesses the request, adds body if necessary and returns the request itself
}
fun executeAsync(action: (HttpResponse<T>) -> Unit) {
prepareRequest { req ->
action(req.asObjectAsync(responseType).get()) // Unirest call that (still) freezes the UI
}
}
// Builder logic ...
}
// ApiClient.kt (simplified)
abstract class ApiClient {
protected fun <T> executeAsync(req: Request<T>, action: (T) -> Unit) {
req.executeAsync { res ->
if (res.isSuccess){
action(res.body)
} else {
throw RuntimeException("res != 200")
}
}
}
}
// AuthClient.kt (simplified)
class AuthClient : ApiClient() {
fun signin(email: String, password: String, onSuccess: () -> Unit) {
executeAsync(
Request.builder(TokenModel::class.java)
.post("/signin")
.body(SignInModel(email, password))
.build()
) {
onSuccess() // this is going to refresh the UI, once the http request has been executed
}
}
}
As the call to get on CompletableFuture freezes the UI I thought of including an Executor or a Thread instead so that executeAsync in Request becomes the following:
fun executeAsync(action: (HttpResponse<T>) -> Unit) {
prepareRequest { req ->
Executors.newSingleThreadScheduledExecutor().execute {
action(it.asObjectAsync(responseType).get())
}
}
}
Is my structure overly complex or does it have to be like that? Do I need the Thread/Executor or can this be achieved in a different way?

How to handle Kotlin Jetpack Paging 3 exceptions?

I am new to kotlin and jetpack, I am requested to handle errors (exceptions) coming from the PagingData, I am not allowed to use Flow, I am only allowed to use LiveData.
This is the Repository:
class GitRepoRepository(private val service: GitRepoApi) {
fun getListData(): LiveData<PagingData<GitRepo>> {
return Pager(
// Configuring how data is loaded by adding additional properties to PagingConfig
config = PagingConfig(
pageSize = 20,
enablePlaceholders = false
),
pagingSourceFactory = {
// Here we are calling the load function of the paging source which is returning a LoadResult
GitRepoPagingSource(service)
}
).liveData
}
}
This is the ViewModel:
class GitRepoViewModel(private val repository: GitRepoRepository) : ViewModel() {
private val _gitReposList = MutableLiveData<PagingData<GitRepo>>()
suspend fun getAllGitRepos(): LiveData<PagingData<GitRepo>> {
val response = repository.getListData().cachedIn(viewModelScope)
_gitReposList.value = response.value
return response
}
}
In the Activity I am doing:
lifecycleScope.launch {
gitRepoViewModel.getAllGitRepos().observe(this#PagingActivity, {
recyclerViewAdapter.submitData(lifecycle, it)
})
}
And this is the Resource class which I created to handle exceptions (please provide me a better one if there is)
data class Resource<out T>(val status: Status, val data: T?, val message: String?) {
companion object {
fun <T> success(data: T?): Resource<T> {
return Resource(Status.SUCCESS, data, null)
}
fun <T> error(msg: String, data: T?): Resource<T> {
return Resource(Status.ERROR, data, msg)
}
fun <T> loading(data: T?): Resource<T> {
return Resource(Status.LOADING, data, null)
}
}
}
As you can see I am using Coroutines and LiveData. I want to be able to return the exception when it occurs from the Repository or the ViewModel to the Activity in order to display the exception or a message based on the exception in a TextView.
Your GitRepoPagingSource should catch retryable errors and pass them forward to Paging as a LoadResult.Error(exception).
class GitRepoPagingSource(..): PagingSource<..>() {
...
override suspend fun load(..): ... {
try {
... // Logic to load data
} catch (retryableError: IOException) {
return LoadResult.Error(retryableError)
}
}
}
This gets exposed to the presenter-side of Paging as LoadState, which can be reacted to via LoadStateAdapter, .addLoadStateListener, etc as well as .retry. All of the presenter APIs from Paging expose these methods, such as PagingDataAdapter: https://developer.android.com/reference/kotlin/androidx/paging/PagingDataAdapter
You gotta pass your error handler to the PagingSource
class MyPagingSource(
private val api: MyApi,
private val onError: (Throwable) -> Unit,
): PagingSource<Int, MyModel>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, YourModel> {
try {
...
} catch(e: Exception) {
onError(e) // <-- pass your error listener here
}
}
}

How to return an object from an api-call-function in Kotlin? [duplicate]

How can I return a value after a callback in kotlin, I tried using Thread.sleep but it doesn't work
fun searchColorFromAPI(): Colors {
val service: RetrofitService = ServiceGenerator.createService(RetrofitService::class.java)
val result: MutableList<String> = arrayListOf()
val call: Call<Colors?>? = service.unityConverter(result)
call?.enqueue(object : Callback<Colors?> {
override fun onResponse(call: Call<Colors?>?, response: Response<Colors?>) {
//switchProgressVisibility()
if (response.isSuccessful) {
val serviceResponse: Colors? = response.body()
if (serviceResponse != null) {
mColors = serviceResponse
}
else {
//buildToast(getString(R.string.null_response))
}
}
else {
//buildToast(getString(R.string.response_unsuccessful))
val errorBody: ResponseBody = response.errorBody()
Log.e(TAG, errorBody.toString())
}
}
override fun onFailure(call: Call<Colors?>?, t: Throwable?) {
/* buildToast(getString(R.string.error_calling_service))
Log.e(TAG, t?.message)*/
}
})
return mColors
}
Always, the mColors is returned before the onFailure or onResponse because they're asynchronous. Before this code was in MainActivity but I was advised to take off, but now when I try get mColors I get the empty value before and after the onResponse is executed, please I'm still learning Kotlin and Android.
Your problem stems from the fact that Retrofit call is asynchronous, so as soon as you call searchColorFromAPI it returns you mColors but the API call may not have been made yet, so you get the mColors value before API call.
To solve this issue, you can do
Use callback, this will require little modification in your current setup, but the 2nd option is preferable over this. Using callback your function should look like this.
/* Now instead of returning a value, your function takes a function (named callback)
as parameter. when your api call finishes, you can call the callback function and
pass the api response.
*/
fun searchColorFromAPI(callback: (Colors?) -> Unit) {
val service: RetrofitService = ServiceGenerator.createService(RetrofitService::class.java)
val result: MutableList<String> = arrayListOf()
val call: Call<Colors?>? = service.unityConverter(result)
call?.enqueue(object : Callback<Colors?> {
override fun onResponse(call: Call<Colors?>?, response: Response<Colors?>) {
//switchProgressVisibility()
if (response.isSuccessful) {
val serviceResponse: Colors? = response.body()
/** pass API response to callback */
callback(serviceResponse)
}
else {
val errorBody: ResponseBody = response.errorBody()
Log.e(TAG, errorBody.toString())
callback(null)
}
}
override fun onFailure(call: Call<Colors?>?, t: Throwable?) {
callback(null)
}
})
}
And in your activity declare a function as follows.
// This function will be called when your api call finishes
// and it will give you the api response
fun apiCallback(colors: Colors?){
if(colors == null){
// API Call failed
}
else{
// use colors as returned by API
}
}
And now call to searchColorFromApi should look like this
searchColorFromApi(apiCallback)
Use Live Data, declare following field in your viewmodel, if you are not using viewmodel then declare it in the class which has searchColorFromApi function.
var colors: MutableLiveData<Colors> = MutableLiveData()
and modify your searchColorFromAPI function as follows
fun searchColorFromAPI() {
val service: RetrofitService = ServiceGenerator.createService(RetrofitService::class.java)
val result: MutableList<String> = arrayListOf()
val call: Call<Colors?>? = service.unityConverter(result)
call?.enqueue(object : Callback<Colors?> {
override fun onResponse(call: Call<Colors?>?, response: Response<Colors?>) {
//switchProgressVisibility()
if (response.isSuccessful) {
val serviceResponse: Colors? = response.body()
if (serviceResponse != null) {
colors.postValue(response.body)
}
}
else {
colors.postValue(null)
val errorBody: ResponseBody = response.errorBody()
Log.e(TAG, errorBody.toString())
}
}
override fun onFailure(call: Call<Colors?>?, t: Throwable?) {
colors.postValue(null)
}
})
}
and in your activity do following
fun setupObservers(){
yourApiCallingClass.colors.observe(this, Observer {
// this code is called when ever value of color field changes
})
}
You can use live data ,that gets updated once the callback receives ,the same live data is observed by the caller fragment/activity
You can use coroutines to return a value from function which has asyn calls in it.
You can use interface callbacks to activity/ fragment to trigger the updates received from retrofit calls.

Kotlin coroutines, how to async alist of calls and return the result as a map

var responseMap = mutableMapOf<VendorType, ChargeResponse>()
requests.forEach {
val response = when (it.vendorType) {
VendorType.Type1 -> service.chargeForType1()
VendorType.Type2 -> service.chargeForType2()
else -> {
throw NotImplementedError("${it.vendorType} does not support yet")
}
}
responseMap[it.vendorType] = response
}
responseMap
So I want all the service.charge function run in separate thread. Return the map when all is done
Hope to solve your problem:
Assume your service and request like this:
interface Service {
suspend fun chargeForType1(): ChargeResponse
suspend fun chargeForType2(): ChargeResponse
}
data class Request(val vendorType: VendorType)
suspend fun requestAll(requests: List<Request>): Map<VendorType, ChargeResponse> {
return coroutineScope {
requests
.map { request ->
async {
request.vendorType to when (request.vendorType) {
VendorType.Type1 -> service.chargeForType1()
VendorType.Type2 -> service.chargeForType2()
else -> throw NotImplementedError("${request.vendorType} does not support yet")
}
}
}
.awaitAll()
.toMap()
}
}

Vertx plus Kotlin coroutines hangs forever

I am rewriting some Java Vertx asynch code using Kotlin coroutines for learning purposes. However, when I try to test a simple HTTP call, the coroutine based test hangs forever and I really don't understand where is the issue. Here a reproducer:
#RunWith(VertxUnitRunner::class)
class HelloWorldTest {
private val vertx: Vertx = Vertx.vertx()
#Before
fun setUp(context: TestContext) {
// HelloWorldVerticle is a simple http server that replies "Hello, World!" to whatever call
vertx.deployVerticle(HelloWorldVerticle::class.java!!.getName(), context.asyncAssertSuccess())
}
// ORIGINAL ASYNC TEST HERE. IT WORKS AS EXPECTED
#Test
fun testAsync(context: TestContext) {
val atc = context.async()
vertx.createHttpClient().getNow(8080, "localhost", "/") { response ->
response.handler { body ->
context.assertTrue(body.toString().equals("Hello, World!"))
atc.complete()
}
}
}
// First attempt, it hangs forever, the response is never called
#Test
fun testSync1(context: TestContext) = runBlocking<Unit> {
val atc = context.async()
val body = await<HttpClientResponse> {
vertx.createHttpClient().getNow(8080, "localhost", "/", { response -> response.handler {it}} )
}
context.assertTrue(body.toString().equals("Hello, World!"))
atc.complete()
}
// Second attempt, it hangs forever, the response is never called
#Test
fun testSync2(context: TestContext) = runBlocking<Unit> {
val atc = context.async()
val response = await<HttpClientResponse> {
vertx.createHttpClient().getNow(8080, "localhost", "/", it )
}
response.handler { body ->
context.assertTrue(body.toString().equals("Hello, World!"))
atc.complete()
}
}
suspend fun <T> await(callback: (Handler<T>) -> Unit) =
suspendCoroutine<T> { cont ->
callback(Handler { result: T ->
cont.resume(result)
})
}
}
Is everyone able to figure out the issue?
It seems to me that your code have several problems:
you may running the test before the http-server got deployed
I believe that since you execute your code inside runBlocking you are blocking the event loop from completing the request.
Finally, I will advise you to use the HttpClienctResponse::bodyHandler method instead of HttpClientResponse::handler as the handler may receive partial data.
Here is an alternative solution that works fine:
import io.vertx.core.AbstractVerticle
import io.vertx.core.Future
import io.vertx.core.Handler
import io.vertx.core.Vertx
import io.vertx.core.buffer.Buffer
import io.vertx.core.http.HttpClientResponse
import kotlin.coroutines.experimental.Continuation
import kotlin.coroutines.experimental.EmptyCoroutineContext
import kotlin.coroutines.experimental.startCoroutine
import kotlin.coroutines.experimental.suspendCoroutine
inline suspend fun <T> await(crossinline callback: (Handler<T>) -> Unit) =
suspendCoroutine<T> { cont ->
callback(Handler { result: T ->
cont.resume(result)
})
}
fun <T : Any> async(code: suspend () -> T) = Future.future<T>().apply {
code.startCoroutine(object : Continuation<T> {
override val context = EmptyCoroutineContext
override fun resume(value: T) = complete()
override fun resumeWithException(exception: Throwable) = fail(exception)
})
}
fun main(args: Array<String>) {
async {
val vertx: Vertx = Vertx.vertx()
//0. take the current context
val ctx = vertx.getOrCreateContext()
//1. deploy the http server
await<Unit> { cont ->
vertx.deployVerticle(object : AbstractVerticle() {
override fun start() {
vertx.createHttpServer()
.requestHandler { it.response().end("Hello World") }
.listen(7777) { ctx.runOnContext { cont.handle(Unit) } }
//note that it is important tp complete the handler in the correct context
}
})
}
//2. send request
val response: HttpClientResponse = await { vertx.createHttpClient().getNow(7777, "localhost", "/", it) }
//3. await response
val body = await<Buffer> { response.bodyHandler(it) }
println("received $body")
}
}