Kotlin: inline function and shared type - kotlin

I want to call multiple API calls at once using coroutines. So far I used coroutines only to call single API call which returned result in form off success or error. Its using inline function. But I need to understand how to use this inline function parameter for data class.
Class JsonResponse is open class which is result for every single API call. But if I use T inside my data class, its red. If I use there type JsonResponse, it is returning JsonResponse type, but not that exact type what apiBlock suppose to return.
Example: apiBlock can contain task, which will return LoginResponse. LoginResponse is extending JsonResponse. But if I use JsonResponse instead T inside my data class, I will have generic JsonResponse inside my onSuccess callback.
My goal here is to initialize multiple tasks, run them inside coroutine as async, then wait for all tasks to finish and if some of them will fail, return failed ones as array so they can be called again.
Here is code what I want to achieve:
data class ApiTask(
val apiBlock: suspend CoroutineScope.() -> T,
val onSuccess: suspend (T)->Unit)
protected fun<T: JsonResponse> apiCallChained(
apiBlocks: List<ApiTask>,
onError: ((List<ApiTask>)->Unit),
onSuccess: ()->Unit){
val failedTasks = mutableListOf<ApiTask>()
apiBlocks.forEach { apiBlock->
launch(Dispatchers.Main){
val (r, err) = withContext(Dispatchers.IO){
try {
apiBlock.apiBlock(this) to null
} catch (e: ApiCallError) {
null to e
}
}
when {
r != null -> {
apiBlock.onSuccess(r)
}
err != null -> failedTasks.add(apiBlock)
}
}
}
if (failedTasks.isEmpty()) onSuccess.invoke() else onError.invoke(failedTasks)
}
Here is my working example for single API call:
protected fun<T: JsonResponse> apiCall(apiBlock: suspend CoroutineScope.() -> T,
onError: ((ApiCallError)->Unit)? = null,
onDone: (()->Unit)? = null,
onSuccess: suspend (T)->Unit): Job {
return launch(Dispatchers.Main){
val (r, err) = withContext(Dispatchers.IO){
try {
apiBlock() to null
} catch (e: ApiCallError) {
null to e
}
}
onDone?.invoke()
when {
r != null -> onSuccess(r)
err != null -> {
onError?.invoke(err)
}
}
}
}
Example of ApiBlock parameter value:
class ApiLogin(js: JSONObject): JsonResponse(js) {
companion object {
#Throws(ApiCallError::class)
operator fun invoke(api: AppApi, email: String, pass: String): ApiLogin{
return ApiLogin(api.apiLoginUser(email, pass))
}
}
class LoginServerResponse(js: JSONObject): JsonResponse(js){
val httpCode by JsInt("httpCode")
val session by JsString("session")
}
val r = LoginServerResponse(js)
}
This class is then used as
apiBlock = { ApiLogin(app.api, email, pass) }
UPDATE:
Fixed it like this, its working.
data class ApiTask<out T: JsonResponse>(
val apiBlock: suspend CoroutineScope.() -> T,
val onSuccess: suspend (#UnsafeVariance T)->Unit)

Your ApiTask also needs a generic parameter:
data class ApiTask<T : JsonResponse>(
val apiBlock: suspend CoroutineScope.() -> T,
val onSuccess: suspend (T) -> Unit
)
Then you have to add this generic parameter in the apiCallChained function too:
protected fun <T : JsonResponse> apiCallChained(
apiBlocks: List<ApiTask<T>>,
onError: ((List<ApiTask<T>>) -> Unit),
onSuccess: () -> Unit
) {
val failedTasks = mutableListOf<ApiTask<T>>()
apiBlocks.forEach { apiBlock ->
launch(Dispatchers.Main) {
val (r, err) = withContext(Dispatchers.IO) {
try {
apiBlock.apiBlock(this) to null
} catch (e: ApiCallError) {
null to e
}
}
when {
r != null -> {
apiBlock.onSuccess(r)
}
err != null -> failedTasks.add(apiBlock)
}
}
}
if (failedTasks.isEmpty()) onSuccess.invoke() else onError.invoke(failedTasks)
}

Related

realTime List using callbackFlow from firestore

i'm facing hard times updating list of Orders in real time from firestore using stateflow !!
class RepositoryImp : Repository {
private fun Query.snapshotFlow(): Flow<QuerySnapshot> = callbackFlow {
val snapshott = addSnapshotListener { value, error ->
if (error != null) {
close()
return#addSnapshotListener
}
if (value != null)
trySend(value)
}
awaitClose {
snapshott.remove()
}
}
override fun getAllOrders() = flow<State<List<OrderModel>>> {
emit(State.loading())
val snapshot = ORDER_COLLECTION_REF.snapshotFlow()
.mapNotNull { it.toObjects(OrderModel::class.java) }
emit(State.success(snapshot)) // **HERE** !!!!!!
}.catch {
emit(State.failed(it.message.toString()))
}.flowOn(Dispatchers.IO)
}
i'm receiving the error from // emit(State.success(snapshot)) that says :
Type mismatch: inferred type is Flow<(Mutable)List<OrderModel!>> but List< OrderModel> was expected
sealed class State <T> {
class Loading <T> : State<T>()
data class Success <T> (val data: T) : State <T>()
data class Failed <T> (val message: String) : State <T>()
companion object {
fun <T> loading() = Loading <T>()
fun <T> success(data: T) = Success(data)
fun <T> failed(message: String) = Failed<T>(message)
}
}
My fun to LoadOrders :
private suspend fun loadOrders() {
viewModel.getAllOrders().collect { state ->
when (state) {
is State.Loading -> {
showToast("Loading")
}
is State.Success -> {
adapter.submitList(state.data)
}
is State.Failed -> showToast("Failed! ${state.message}")
}
}
}
Your snapshot variable is a Flow of lists, not a single List. If you want to just fetch the current list, you shouldn't use a flow for that. Instead use get().await().
override fun getAllOrders() = flow<State<List<OrderModel>>> {
emit(State.loading())
val snapshot = ORDER_COLLECTION_REF.get().await()
.let { it.toObjects(OrderModel::class.java) }
emit(State.success(snapshot))
}.catch {
emit(State.failed(it.message.toString()))
}.flowOn(Dispatchers.IO)
The flowOn call is actually unnecessary because we aren't doing anything blocking. await() is a suspend function.
Based on comments discussion below, supposing we want to show a loading state only before the first item, then show a series of success states, and we want to show an error and stop emitting once there's an error, we could do:
override fun getAllOrders() = flow<State<List<OrderModel>>> {
emit(State.loading())
val snapshots = ORDER_COLLECTION_REF.snapshotFlow()
.mapNotNull { State.success(it.toObjects(OrderModel::class.java)) }
emitAll(snapshots)
}.catch {
emit(State.failed(it.message.toString()))
}

How to ignore empty database result for the first time and wait for server result in application?

My app using room as a database and retrofit as a network calling api.
i am observing database only as a single source of truth. every thing is working fine. But i am not finding solution of one scenario.
Like for the first time when user open app it do following operations
fetch data from db
fetch data from server
because currently database is empty so it sends empty result to observer which hide progress bar . i want to discard that event and send result to observer when server dump data to database. even server result is empty. so progress bar should always hide once their is confirmation no data exists.
in other words application should always rely on database but if it empty then it should wait until server response and then notify observer.
this is my code
observer
viewModel.characters.observe(viewLifecycleOwner, Observer {
Log.e("status is ", "${it.message} at ${System.currentTimeMillis()}")
when (it.status) {
Resource.Status.SUCCESS -> {
binding.progressBar.visibility = View.GONE
if (!it.data.isNullOrEmpty()) adapter.setItems(ArrayList(it.data))
}
Resource.Status.ERROR -> {
Toast.makeText(requireContext(), it.message, Toast.LENGTH_SHORT).show()
binding.progressBar.visibility = View.GONE
}
Resource.Status.LOADING ->
binding.progressBar.visibility = View.VISIBLE
}
})
ViewModel
#HiltViewModel
class CharactersViewModel #Inject constructor(
private val repository: CharacterRepository
) : ViewModel() {
val characters = repository.getCharacters()
}
Repository
class CharacterRepository #Inject constructor(
private val remoteDataSource: CharacterRemoteDataSource,
private val localDataSource: CharacterDao
) {
fun getCharacters() : LiveData<Resource<List<Character>>> {
return performGetOperation(
databaseQuery = { localDataSource.getAllCharacters() },
networkCall = { remoteDataSource.getCharacters() },
saveCallResult = { localDataSource.insertAll(it.results) }
)
}
}
Utility function for all api and database handling
fun <T, A> performGetOperation(databaseQuery: () -> LiveData<T>,
countQuery: () -> Int,
networkCall: suspend () -> Resource<A>,
saveCallResult: suspend (A) -> Unit): LiveData<Resource<T>> =
liveData(Dispatchers.IO) {
emit(Resource.loading())
val source = databaseQuery().map { Resource.success(it,"database") }.distinctUntilChanged()
emitSource(source)
val responseStatus = networkCall()
if (responseStatus.status == SUCCESS) {
saveCallResult(responseStatus.data!!)
} else if (responseStatus.status == ERROR) {
emit(Resource.error(responseStatus.message!!))
}
}
LocalDataSource
#Dao
interface CharacterDao {
#Query("SELECT * FROM characters")
fun getAllCharacters() : LiveData<List<Character>>
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertAll(characters: List<Character>)
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(character: Character)
}
DataSource
class CharacterRemoteDataSource #Inject constructor(
private val characterService: CharacterService
): BaseDataSource() {
suspend fun getCharacters() = getResult { characterService.getAllCharacters() }}
}
Base Data Source
abstract class BaseDataSource {
protected suspend fun <T> getResult(call: suspend () -> Response<T>): Resource<T> {
try {
Log.e("status is", "started")
val response = call()
if (response.isSuccessful) {
val body = response.body()
if (body != null) return Resource.success(body,"server")
}
return error(" ${response.code()} ${response.message()}")
} catch (e: Exception) {
return error(e.message ?: e.toString())
}
}
private fun <T> error(message: String): Resource<T> {
Timber.d(message)
return Resource.error("Network call has failed for a following reason: $message")
}
}
Character Service
interface CharacterService {
#GET("character")
suspend fun getAllCharacters() : Response<CharacterList>
}
Resource
data class Resource<out T>(val status: Status, val data: T?, val message: String?) {
enum class Status {
SUCCESS,
ERROR,
LOADING
}
companion object {
fun <T> success(data: T,message : String): Resource<T> {
return Resource(Status.SUCCESS, data, message)
}
fun <T> error(message: String, data: T? = null): Resource<T> {
return Resource(Status.ERROR, data, message)
}
fun <T> loading(data: T? = null): Resource<T> {
return Resource(Status.LOADING, data, "loading")
}
}
}
CharacterList
data class CharacterList(
val info: Info,
val results: List<Character>
)
What is the best way by that i ignore database if it is empty and wait for server response and then notify observer

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()
}
}

How to check Kotlin BufferedReader use { return something } has called method close()?

My sample Kotlin about BufferedReader().use {}
I wonder if the close() is called when I return early in the use block
fun main() {
sendGet()
}
fun sendGet() {
val queryUrl = "http://www.google.com/search?q=kotlin&ie=utf-8&oe=utf-8"
val url = URL(queryUrl)
val conn = url.openConnection() as HttpURLConnection
conn.requestMethod = "GET"
conn.setRequestProperty("User-Agent", "Mozilla/5.0")
val responseCode = conn.responseCode
println("Response code: ${responseCode}")
when (responseCode) {
200 -> {
println("response: ${conn.getResponseText()}")
}
else -> println("Bad response code: ${responseCode}")
}
}
private fun HttpURLConnection.getResponseText(): String {
BufferedReader(InputStreamReader(inputStream)).use {
return it.readText()
}
}
You can see the source code for use in stdlib by navigating using "Go to implementation" (Cmd + B on a Mac):
public inline fun <T : Closeable?, R> T.use(block: (T) -> R): R {
var exception: Throwable? = null
try {
return block(this)
} catch (e: Throwable) {
exception = e
throw e
} finally {
when {
apiVersionIsAtLeast(1, 1, 0) -> this.closeFinally(exception)
this == null -> {}
exception == null -> close()
else ->
try {
close()
} catch (closeException: Throwable) {
// cause.addSuppressed(closeException) // ignored here
}
}
}
}
Because the call to close is inside the finally block, it will execute even on an early return.

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

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)