https://kotlinlang.org logo
#ktor
Title
# ktor
a

Ayodele

06/30/2020, 10:19 AM
Hii guys so I'm working on a microservice I'm using httpclient to plug into the UberApi and I'm getting results in the desired route in the localhost Now i need to plug it to my ktorservice so I can use a Post request from the Android end and get desired results on mobile client
h

Hamza

06/30/2020, 10:20 AM
you are not asking a specific question
😁 1
a

Ayodele

06/30/2020, 10:22 AM
Okay i need help on how i can make the data i got available to the android end. My idea of how this works is that you need a ktor client to plug into the UberApi which i have done and i need to plug it to my ktor server to make it available Making it available is what i dont know how to do @Hamza
r

Rémy

06/30/2020, 10:24 AM
Make an endpoint : • call Uber API • transform data received to your own • respond to client Android with your data ?
a

Ayodele

06/30/2020, 10:27 AM
Yeah I have called the UberApi Transforming data can be done in what ways please Its my first time doing backend @Rémy
r

Rémy

06/30/2020, 10:32 AM
No problem, I’m not an expert too (Android developer first 🙂). You made like a middleware, I did that too, between a third api and you client. I did things in the endpoint method (my previous message). Do you have small example code to show ?
a

Ayodele

06/30/2020, 10:43 AM
Copy code
val client = HttpClient()
val data = <http://client.post|client.post><String>(requestURL) {
    header("Authorization", Auth.authKey)
    body =
        """{
            |"pickup_stop": {"lat": ${start.lat}, "lng": ${start.lng}}, 
            |"destination_stops": [{"lat": ${end.lat}, "lng": ${end.lng}}], 
            |"payment_method_id": "${paymentMethod}", 
            |"payment_method_type": "default", 
            |"timezone": "${timeZone}", 
            |"preliminary": "true"
        |}""".trimMargin()
}
client.close()
return data
Copy code
get("/BoltRides") {
    call.respondText(getBoltRides(location(51.5073509, -0.1277583), location(51.5073509, -0.1277583)))
}
@Rémy
This is how i'm getting for bolt
the route is in application.Kt
r

Rémy

06/30/2020, 10:47 AM
Do you have something in return of your getBoltRides first ?
a

Ayodele

06/30/2020, 10:49 AM
I can get data in my postman, returns a largeset of data altogether
@Rémy
r

Rémy

06/30/2020, 10:51 AM
Ok, I think your mapping, or something else is messed up. Are you using the native Ktor client (I ask because I use a different one to consume others APIs, a habit from Android => OkHttp) ?
a

Ayodele

06/30/2020, 10:52 AM
yes
Copy code
import io.ktor.client.HttpClient
@Rémy
r

Rémy

06/30/2020, 10:53 AM
Okay I’m looking documentation.
a

arjun

06/30/2020, 10:53 AM
Copy code
<http://httpClient.post|httpClient.post><ClassName>
can u replace the className with a data class ?
r

Rémy

06/30/2020, 10:56 AM
It was my first expectation, but did you log you api call to handle error first ?
Maybe, your authentication isn’t well implemented.
And yes, a (data) class as dto is a better practice.
a

Ayodele

06/30/2020, 10:57 AM
@arjun will that help me if i change it to a data class?
r

Rémy

06/30/2020, 10:58 AM
You need the call logs first. To debug properly your problem.
a

Ayodele

06/30/2020, 10:59 AM
okay, first time hearing about that. you mean log like android?
a

arjun

06/30/2020, 11:00 AM
1. Cast the response to DTO, 2. have a try catch to see if u are getting any exception 3. better to create a request DTO and assign the instance to body rather than having a JSON there. 4. Set content-type header if its not picking JSON by default
r

Rémy

06/30/2020, 11:03 AM
a

Ayodele

06/30/2020, 11:05 AM
okay thanks does that mean I dont really need a repository pattern or Interface? @arjun @Rémy
r

Rémy

06/30/2020, 11:07 AM
It means you have to found the root cause first 🙂 dto instead of a String is a good practice. But, to debug properly, you need the error log 😉 Step by step 😄
Let me now if you found the error.
a

Ayodele

06/30/2020, 11:09 AM
mmm, i'm not getting any errors i'm getting my desired results are you saying there might be potential errors
?
@Rémy
r

Rémy

06/30/2020, 11:11 AM
Alright you received yours data isn’t it ?
a

Ayodele

06/30/2020, 11:12 AM
yes
r

Rémy

06/30/2020, 11:13 AM
So, what’s the problem ? Your client doesn’t get the response ?
a

Ayodele

06/30/2020, 11:14 AM
or I guess i'm not mean to use GET in the application.kt? i only used that to see if i could get the result the data is a verylarge one and I dont need most of the info its sending, is there a way i can streamline it so I can get it in my client?
r

Rémy

06/30/2020, 11:26 AM
If you want just some informations, you have to make a data mapping on a smaller object who fit your needs.
An interface / class / data class map the API response, then, you can make an other object with just some properties. Then, you send it to your client.
a

arjun

06/30/2020, 11:28 AM
@Ayodele how are u getting the data now? what solved the problem ?
1
r

Rémy

06/30/2020, 11:28 AM
Make the mapping on the backend side. Client doesn’t need too much infos.
As I understand, problem wasn’t retrieving api informations but informations mapping, isn’t it @Ayodele?
a

Ayodele

06/30/2020, 11:31 AM
@arjun @Rémy i've been getting the data what is confusing me is how to expose this to the client because this backend service will be hosted what i want basically is to be able to call myapp.herokuapp.com/boltride through a POST request from an adnroid app and get my data the way I'm getting it now
@arjun
with less data of course
r

Rémy

06/30/2020, 11:35 AM
Like @arjun said, your data response needs to be a class (data), then use it’s properties / data to make your own object to send to Android client.
Copy code
val data = <http://client.post|client.post><UberDataDto>(requestURL) { //...
https://ktor.io/clients/http-client/quick-start/responses.html You have to deserialized the response into an object. https://ktor.io/clients/http-client/features/json-feature.html <-
a

Ayodele

06/30/2020, 11:42 AM
whew, Okay thanks would have been nice if someone had a public repo or something
r

Rémy

06/30/2020, 11:53 AM
I think but I don’t have link. Do you have the Uber endpoint definition, to be able to create the DTO ?
a

Ayodele

06/30/2020, 11:59 AM
i'm sorry definition?
@Rémy
r

Rémy

06/30/2020, 12:01 PM
Each endpoint has a response definitions, normally it’s in the Uber API document. Like /getUberDriver => 200 : { “id”: string, “name”: string, age: number, ... }
a

arjun

06/30/2020, 12:04 PM
Copy code
fun getRides(request: PickupRequest): PickupResponse {
        return try {
            val response = <http://httpClient.post|httpClient.post><PickupResponse> {
                header("Content-Type", ContentType.Application.Json.toString())
                url(requestUrl)
                body = request
            }
            response
        } catch(e: Exception) {
            logger.error(e.message, e)
            null
        }
    }
👏 1
a

Ayodele

06/30/2020, 12:12 PM
hmm, this would help
thanks @Rémy @arjun
🙂 1
hi @Rémy @arjun will this work
Copy code
data class BoltResponse (
     val message : String,
    val city : String,
    val searchCategories : List<Rides>
):Serializable
message. city and searchCategories is what i'm getting searchCategories is a list
r

Rémy

06/30/2020, 1:30 PM
I don’t know what the service send. I suppose it’s alright 🙂
a

Ayodele

06/30/2020, 1:32 PM
Yeah
🙂 1
hi @Rémy @arjun I'm getting this error
ERROR Application - Unhandled: GET - /BoltRides
java.lang.IllegalStateException: Request cannot be executed; I/O reactor status: STOPPED
I used a logger
Copy code
get("/BoltRides") {
    call.respond(getBoltRides(location(51.5073509, -0.1277583), location(51.5073509, -0.1277583)))
}
this is how i'm calling it after I started using a DTO i coukdnt use respond text any longer
r

Rémy

07/01/2020, 6:51 AM
If you extract the method from the response and put result data in an object. What’s do you reveived ? What’s the return data type ?
You have to serialized you object 🙂
a

Ayodele

07/01/2020, 6:51 AM
how do I do that
my function has a return type of the DTO class
r

Rémy

07/01/2020, 6:52 AM
With Gson, or Jackson ?
a

Ayodele

07/01/2020, 6:52 AM
just like arjun wrote yesterday
Gson
r

Rémy

07/01/2020, 6:54 AM
Hum, normally Ktor can handle serialization on the fly, but I need to look the documentation.
You have to configure Gson for server side response. And if respond with a Class, Ktor automatically serialized it to Json.
a

Ayodele

07/01/2020, 6:59 AM
I
Copy code
install(ContentNegotiation) {
    gson {
    }
}
I have that in my App.kt
r

Rémy

07/01/2020, 6:59 AM
Yeap, and it didn’t work ?
a

Ayodele

07/01/2020, 7:02 AM
nah must I call client.close() after finishing request?
r

Rémy

07/01/2020, 7:03 AM
Try to remove this.
Is it mentioned in the documentation ?
a

Ayodele

07/01/2020, 7:04 AM
if i remove it thers's another error
ERROR Application - Unhandled: GET - /BoltRides
io.ktor.client.call.NoTransformationFoundException: No transformation found: class <http://kotlinx.coroutines.io|kotlinx.coroutines.io>.ByteBufferChannel
r

Rémy

07/01/2020, 7:06 AM
@Ayodele I will make a small example of what you want to do into a small github repo 🙂 The basics of consume a third api and deserialized / Serialized data to use.
a

Ayodele

07/01/2020, 7:10 AM
Thanks, that will be really appreciated
Thanks a lot
r

Rémy

07/01/2020, 7:12 AM
🙂
Just give me ~ 30 min
a

Ayodele

07/01/2020, 7:17 AM
Alright
r

Rémy

07/01/2020, 8:00 AM
Okay I push it to Github.
a

Ayodele

07/01/2020, 8:01 AM
url?
r

Rémy

07/01/2020, 8:04 AM
2 secs
Really simple case.
a

Ayodele

07/01/2020, 8:09 AM
Thanks alot
hey I figured how to deserialize it Thanks
its working now
👏 1
r

Rémy

07/01/2020, 8:28 AM
You understood the concept, and it’s the main goal 🙂
👍 1
In my exampel I forget to close de client 😉
If you execute the call inside a lambda, it’s handle by default.
a

Ayodele

07/01/2020, 1:19 PM
yeah, I did that
@Rémy please if i wanna write unit test for a function like this
Copy code
fun getRides(request: PickupRequest): PickupResponse {
        return try {
            val response = <http://httpClient.post|httpClient.post><PickupResponse> {
                header("Content-Type", ContentType.Application.Json.toString())
                url(requestUrl)
                body = request
            }
            response
        } catch(e: Exception) {
            logger.error(e.message, e)
            null
        }
    }
how do I go about it? I have never written unitTests
r

Rémy

07/01/2020, 1:23 PM
Don’t write unit test related to webservice. I think it’s more reliable to write unit test for your business logic (e.g. dto -> your own object, etc...). So, you can mock / fake a webservice response with a local json, and test what you make with data. I’m not an expert for test related web services.
a

Ayodele

07/01/2020, 1:25 PM
more like when a create a repository?
r

Rémy

07/01/2020, 1:35 PM
Test how you manage your data inside your application, and what you send to the client.
a

Ayodele

07/01/2020, 1:36 PM
Alright
Thanks
😉 1
a

arjun

07/01/2020, 2:31 PM
for DB it makes sense to write integration tests. for a client, u just need to mock the response and test any custom logic (like u wanted to create a custom DTO from a big object)
👍 1
a

Ayodele

07/03/2020, 9:30 AM
Hi @Rémy please how to I log in ktor like in android LOG.d()
r

Rémy

07/03/2020, 9:32 AM
At this time I never used it, but I place often some println(). You can look at this. Maybe, @arjun had better option / opinion ?
a

Ayodele

07/03/2020, 10:17 AM
Alright
a

arjun

07/03/2020, 10:17 AM
Copy code
install(CallLogging) {
    level = <http://Level.INFO|Level.INFO>
}
👍 1
a

Ayodele

07/03/2020, 10:18 AM
Thanks @arjun
a

arjun

07/03/2020, 10:18 AM
a

Ayodele

07/03/2020, 12:27 PM
@arjun @Rémy dplease help i'm getting this error
java.io.IOException: Broken delimiter occurred
Hi guys, please i just noticed that my server doesnt receive json requests from android client it only accepts form parameters
r

Rémy

07/03/2020, 2:09 PM
What’s your endpoint ?
a

Ayodele

07/03/2020, 2:11 PM
could this be this issue?
Copy code
val signUpParameters = call.receive<parameters>()
what do people use normally?
call.receive<DTO>()
?
I think you want to receive an object. Not a form.
a

Ayodele

07/03/2020, 2:24 PM
yeah
Thanks
r

Rémy

07/03/2020, 2:27 PM
😉
a

Ayodele

07/06/2020, 1:36 PM
hi @Rémy please I have a list, bascially an array from an external Api and I want to map the properties i to my own properties in another list so it returns something like List<MyDTO>
please help
r

Rémy

07/06/2020, 1:49 PM
Create a function to map your dto list to your own data.
a

Ayodele

07/06/2020, 1:55 PM
did that it was showing empty array in my response and I'm receiving response from the external Api
Copy code
override suspend fun getResponse(start: Location, end: Location): List<MyDTOResponse>? {
    val myResponse = listOf< MyDTOResponse>()
    val apiRes = getApiRes(start, end)!!.data.rideCategories

}
i did somthing like this
and I tried to equate the properties of myResponse to my apiRes MyDTOResponse returrned an empty array
r

Rémy

07/06/2020, 1:59 PM
I don’t understand correctly your flow. You retrieve some data from API (a list), and you want to make a new list with it ?
a

Ayodele

07/06/2020, 1:59 PM
yeah, exactly
r

Rémy

07/06/2020, 2:03 PM
Just after you retrieve your api list, then map it to your own objects.
apiRes.forEach { category -> newList.add(MyObject.fromDto(category)) } ? Something like that ?
a

Ayodele

07/06/2020, 2:09 PM
whats MyObject.fromDTO?
r

Rémy

07/06/2020, 2:30 PM
Your object which you want to use 🤷‍♂️
It’s not working ?
a

Ayodele

07/06/2020, 2:36 PM
i cant call MyObject, its also a list do i have to loop?
I know forEach is a loop
got it
i used yourmethod
thanks
r

Rémy

07/06/2020, 5:38 PM
😉
a

Ayodele

07/07/2020, 10:15 AM
Hi @arjun @Rémy Please have you used Jwt before? I'm trying to use postman for my requests and i noticed postman added a previous;y gotten response, and added it as the Bearer so it was giving me this error
Error: Invalid character in header content ["Authorization"]
do you know what the issue might be?
r

Rémy

07/07/2020, 10:17 AM
No idea 😕
a

arjun

07/07/2020, 10:32 AM
can u be more specific ? for authorization u need a Bearer token. usual format is
Bearer JwtToken
a

Ayodele

07/07/2020, 10:33 AM
yes
a

arjun

07/07/2020, 10:33 AM
do u need authorization or not ?
a

Ayodele

07/07/2020, 10:33 AM
mine looks like this
I need authorization
a

arjun

07/07/2020, 10:42 AM
do you know what is Authorization ? and what you are trying to achieve ?
and do you know what is JWT ?
1