Why I can't read text from URL with KTOR client? - kotlin

I want to read a text from a URL with KTOR client.
suspend fun fetch() : String {
val client = HttpClient(CIO)
return client.get(ENDPOINT)
}
but it gives me a Type mismatch. Required:String. Found:HttpResponse.
I did the same thing with:
suspend fun fetch() : String {
return URL(ENDPOINT).readText()
}
how can do it with KTOR client?

See the doc on receiving responses.
Since Ktor 2, client.get(...) returns an HttpResponse instance, and then you can read the body explicitly using .body():
suspend fun fetch() : String {
val client = HttpClient(CIO)
return client.get(ENDPOINT).body()
}
The body() method is generic and the return type depends on the expected expression type. You can make it explicit by using .body<String>(), especially in contexts where the compiler cannot guess.
Note that you shouldn't create a new client on the fly every time you make a request. Clients need resources like thread pools, and it's best if they are reused.

Related

How to get the value for reified in Kotlin?

I am getting started with kotlin and stuck with an issue.
I have a request function as below :
fun dumRequest(): MyRequest {
val token = JwtTokenGenerator.createToken(user)
return MyRequest(token.serialize())
}
And this is being passed as an argument to a function as :
val response: ResponseEntity<MyRequest> = callMyRequest(dumRequest())
And callMyRequest() is of generic type as :
private inline fun <reified T, reified Y> callMyRequest(request: Y): ResponseEntity<T> {
val headers = HttpHeaders()
headers.add(CONTENT_TYPE, "application/json")
headers.add(AUTHORIZATION, request.token) // I want something like this here
// other business logic
}
I want to get the token from the request object which is being pass to callMyRequest() and set it in the AUTH header. But since this is a generic type, not sure how I can get the token field out of this ?
Any help would be highly appreciated.
TIA
I don't really think you need to make Y reified here. It would suffice to restrict it to an interface containing the token, and then letting MyRequest implement that interface. Like <Y : AuthenticatedRequest>.
Actually, you don't really need the Y type parameter at all, because you could just take the interface-type as a parameter directly.
Something like this:
interface AuthenticatedRequest {
val token: String
}
data class MyRequest(override val token: String) : AuthenticatedRequest
private inline fun <reified T> callMyRequest(request: AuthenticatedRequest): ResponseEntity<T> {
val headers = HttpHeaders()
headers.add(CONTENT_TYPE, "application/json")
headers.add(AUTHORIZATION, request.token) // I want something like this here
// other business logic
}
It's when you want to deserialize your result to T that the value of reified comes in handy. It might make you able to do something like response.readEntity<T>(), without having to deal with the class-objects directly.

How to use #Cacheable with Kotlin suspend funcion

I am working in a Kotlin and Spring Boot project and I am trying to use Caffeine for caching. I have a service with a suspending function that makes an http call. Here is my config:
#Bean
open fun caffeineConfig(): #NonNull Caffeine<Any, Any> {
return Caffeine.newBuilder().expireAfterWrite(60, TimeUnit.SECONDS)
}
#Bean
open fun cacheManager(caffeine: Caffeine<Any, Any>): CacheManager {
val caffeineCacheManager = CaffeineCacheManager()
caffeineCacheManager.getCache("test")
caffeineCacheManager.setCaffeine(caffeine)
return caffeineCacheManager
}
And here is the function that I want to cache:
#Cacheable(value = ["test"])
open suspend fun getString(id: String): String {
return client.getString(id)
}
But it seems that the caching is not working since I can see from logs that the client gets called every time the service-function gets called. Does #Cacheable not work for suspending functions? Or am I missing something else?
The documentation of #Cacheable says:
Each time an advised method is invoked, caching behavior will be applied, checking whether the method has been already invoked for the given arguments. A sensible default simply uses the method parameters to compute the key, but a SpEL expression can be provided via the key() attribute, or a custom KeyGenerator implementation can replace the default one (see keyGenerator()).
The suspend modifier inserts an Continuation<String> parameter in the generated code which accepts input from the caller. This presumably means each invocation gets its own continuation and the cache detects this as a unique call.
However since the return value also gets changed depending on the continuation you cannot have the cache ignore the continuation parameter. A better approach is to not use suspend functions and instead returning a Deferred which consumers can share:
#Cacheable(value = ["test"])
open fun getString(id: String): Deferred<String> {
return someScope.async {
client.getString(id)
}
}
// Consumer side
getString(id).await()
This should work with the standard caching mechanism since Deferred is a normal object and no special parameters are required.

Kotlin Decorators/Annotations for Function to reduce Boilerplate

I am writing a library to interact with a third party RESTful API. I'm using Retrofit to do that. Each API endpoint has a defined HTTP code that indicates success (some are 200, some are 201, some are 204, etc.).
Rather than having the base that Retrofit generates from be:
UsersService {
#GET("some/path")
fun getPath(): Call<SomeObject>
}
and then in another class
fun getPath(): SomeObject {
val result = myUsersService.getPath().execute()
if(result.code()!=201) throw SomeException("")
return result.body()!!
}
I would really like to do something in my Retrofit "definition" file like
UsersService {
#SuccessCode(201)
#GET("some/path")
fun getPath(): Call<T>
}
which would generate whatever the Retrofit code generates, but then effectively wrap it in a function like
fun someGeneratedFunction(fn: ()->Call<T>): T {
val result = fn.execute()
if(result.code() != 201) throw SomeException("")
return result
}
In Python these are function decorators. Reading over annotations, they appear to be something for classes, not functions. Is there a way to do this with Kotlin?
This reduces boilerplate, which could be done in other ways I know, but I like that it makes the UsersService file more self-documenting than it already is. I have looked over the Retrofit guide, but I can't find anything suggesting they've already got an annotation like this.
For retrofit the functionality to do this already exists, take a look at this retrofit example.
For the behavior you want:
Use retrofit.(nextRequestBodyConverter|nextResponseBodyConverter) to retrieve the actual converter for the method type.
Check if the annotation is present in annotations
If present, wrap the converter and check the response code, throwing an error on failure.
If absent, just return the original converter and don't change its behavior.

RxJava How do I convert Single<T> in Rxjava to Call<T> in Retrofit or opposite convert

I have an API service class with a method that returns an Call provided by Retrofit.
Recently, Rx2Java introduced the Single so I want to change Call to Single but I don't want to change logic.
For example :
class interface testAPI{
#GET
Call<Object> get()
}
And now I'd like to change like below
#GET
Single<Object> get()
Is there anyway to convert Single to Call likes :
Single.fromCall
or
Call.fromSingle
Update
#Moinkhan : Thank you for the reply!
I'd like to describe my problem in detail.
Previous version app is working fine with Rest API and Retrofit
#GET
Call<Response> get()
Now, we're planing to use GraphQL + Apollo client Android.
Apollo client is compatible with Rxjava. The API will be look like below :
class apolloApi {
fun get(): Single<Response> {
// Run query code with Apollo Query and Rx2Java
}
}
If we do that way, we have to change all of previous logic. It takes our effort. ( Definitely, we must do in the future ! )
But now, if we can convert Single to Call, it will be fine !
For example :
class apolloApi {
private fun get(): Single<Response> {
// Run query code with Apollo Query and Rx2Java
}
fun convertCall(): Call<Response> {
return get().toCall() // Something like that
}
}
Yes you can..
You just need to add below dependency..
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
In your retrofit client..
retrofit = Retrofit.Builder()
.client(client)
.baseUrl(baseUrl)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create()) // this line is important
.addConverterFactory(GsonConverterFactory.create())
.build()
for conversion from Call > Single or Signle > Call you can create extension function in kotlin.
from Call to Single
fun <T> Call<T>.toSingle(): Single<Response<T>> {
val single = SingleSubject.create<Response<T>>()
this.enqueue(object : Callback<T> {
override fun onFailure(call: Call<T>, t: Throwable) {
single.onError(t)
}
override fun onResponse(call: Call<T>, response: Response<T>) {
single.onSuccess(response)
}
})
return single
}
In the Apollo Android docs, there's an example that does not use RxJava, and one that does use it. The one that does not has an API very similar to that of Retrofit Call. It has an enqueue method with a callback with onSuccess and onFailure.
I think a good compromise in your situation would be to start using.the Apollo client without RxJava so that your client code (i.e. the code that currently depends on and calls the Call interface) does not have to be changed.
Later, when you have the resources for the migration, you can migrate it gradually to the RxJava version, using the Rx2Apollo wrapper.

What's the recommended way to delay Kotlin's buildSequence?

I'm trying to poll a paginated API and provide new items to the user as they appear.
fun connect(): Sequence<T> = buildSequence {
while (true) {
// result is a List<T>
val result = dataSource.getFirstPage()
yieldAll(/* the new data in `result` */)
// Block the thread for a little bit
}
}
Here's the sample usage:
for (item in connect()) {
// do something as each item is made available
}
My first thought was to use the delay function, but I get this message:
Restricted suspended functions can only invoke member or extension suspending functions on their restricted coroutine scope
This is the signature for buildSequence:
public fun <T> buildSequence(builderAction: suspend SequenceBuilder<T>.() -> Unit): Sequence<T>
I think this message means that I can only use the suspend functions in SequenceBuilder: yield and yieldAll and that using arbitrary suspend function calls aren't allowed.
Right now I'm using this to block the sequence building by one second after every time the API is polled:
val resumeTime = System.nanoTime() + TimeUnit.SECONDS.toNanos(1)
while (resumeTime > System.nanoTime()) {
// do nothing
}
This works, but it really doesn't seem like a good solution. Has anybody encountered this issue before?
Why does it not work? Some research
When we look at buildSequence, we can see that it takes an builderAction: suspend SequenceBuilder<T>.() -> Unit as its argument. As a client of that method, you'll be able to hand on a suspend lambda that has SequenceBuilder as its receiver (read about lambda with receiver here).
The SequenceBuilder itself is annotated with RestrictSuspension:
#RestrictsSuspension
#SinceKotlin("1.1")
public abstract class SequenceBuilder<in T> ...
The annotation is defined and commented like this:
/**
* Classes and interfaces marked with this annotation are restricted
* when used as receivers for extension `suspend` functions.
* These `suspend` extensions can only invoke other member or extension
* `suspend` functions on this particular receiver only
* and are restricted from calling arbitrary suspension functions.
*/
#SinceKotlin("1.1") #Target(AnnotationTarget.CLASS) #Retention(AnnotationRetention.BINARY)
public annotation class RestrictsSuspension
As the RestrictSuspension documentation tells, in the case of buildSequence, you can pass a lambda with SequenceBuilder as its receiver but with restricted possibilities since you'll only be able to call "other member or extension suspend functions on this particular receiver". That means, the block passed to buildSequence may call any method defined on SequenceBuilder (like yield, yieldAll). Since, on the other hand, the block is "restricted from calling arbitrary suspension functions", using delay does not work. The resulting compiler error verifies it:
Restricted suspended functions can only invoke member or extension suspending functions on their restricted coroutine scope.
Ultimately, you need to be aware that the buildSequence creates a coroutine that is an example of a synchronous coroutine. In your example, the sequence code will be executed in the same thread that consumes the sequence by calling connect().
How to delay the sequence?
As we learned, The buildSequence creates a synchronous sequence. It's fine to use regular Thread blocking here:
fun connect(): Sequence<T> = buildSequence {
while (true) {
val result = dataSource.getFirstPage()
yieldAll(result)
Thread.sleep(1000)
}
}
But, do you really want an entire thread to be blocked? Alternatively, you can implement asynchronous sequences as described here. As a result, using delay and other suspending functions will be valid.
Just for an alternate solution...
If what you're really trying to do is asynchronously produce elements, you can use Flows which are basically asynchronous sequences.
Here is a quick table:
Sync
Async
Single
Normal valuefun example(): String
suspendingsuspend fun example(): Stringorfun example(): Deferred<String>
Many
Sequencefun example(): Sequence<String>
Flowfun example(): Flow<String>
You can convert your Sequence<T> to a Flow<T> by replacing the sequence { ... } builder with the flow { ... } builder and then replace yield/yieldAll with emit/emitAll:
fun example(): Flow<String> = flow {
(1..5).forEach { getString().let { emit(it) } }
}
suspend fun getString(): String = { ... }
So, for your example:
fun connect(): Flow<T> = flow {
while (true) {
// Call suspend function to get data from dataSource
val result: List<T> = dataSource.getFirstPage()
emitAll(result)
// _Suspend_ for a little bit
delay(1000)
}
}