Skip to content

Commit 22922b7

Browse files
committed
Use Retrofit instead of ktor client
- too many problems caused by ktor client because it needs the latest coroutines package. - Intellij does not allow plugins to pack it's own version of coroutines, which means in most cases ktor will throw NoClassDef error - retrofit has a fluent language, it has support for async operations (though not really fluent with coroutines) and most importantly it does not rely on coroutines.
1 parent 8759b81 commit 22922b7

File tree

6 files changed

+70
-62
lines changed

6 files changed

+70
-62
lines changed

build.gradle.kts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,12 @@ version = properties("pluginVersion")
1919

2020
val ktorVersion = properties("ktorVersion")
2121
dependencies {
22-
implementation("io.ktor:ktor-client-core:$ktorVersion")
23-
implementation("io.ktor:ktor-client-cio:$ktorVersion")
24-
implementation("io.ktor:ktor-client-logging:$ktorVersion")
25-
implementation("io.ktor:ktor-client-encoding:$ktorVersion")
26-
implementation("io.ktor:ktor-client-content-negotiation:$ktorVersion")
27-
// implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion")
28-
implementation("io.ktor:ktor-serialization-gson:$ktorVersion")
29-
implementation("org.apache.logging.log4j:log4j-slf4j-impl:2.17.2")
30-
22+
implementation("com.squareup.retrofit2:retrofit:2.9.0")
23+
// define a BOM and its version
24+
implementation(platform("com.squareup.okhttp3:okhttp-bom:4.9.3"))
25+
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
26+
implementation("com.squareup.okhttp3:okhttp")
27+
implementation("com.squareup.okhttp3:logging-interceptor")
3128
}
3229

3330
// Configure project's dependencies
@@ -54,6 +51,13 @@ changelog {
5451
}
5552

5653
tasks {
54+
buildPlugin {
55+
exclude { "coroutines" in it.name }
56+
}
57+
prepareSandbox {
58+
exclude { "coroutines" in it.name }
59+
}
60+
5761
// Set the JVM compatibility versions
5862
properties("javaVersion").let {
5963
withType<JavaCompile> {

gradle.properties

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,4 @@ gradleVersion=7.4
2424
# Opt-out flag for bundling Kotlin standard library.
2525
# See https://plugins.jetbrains.com/docs/intellij/kotlin.html#kotlin-standard-library for details.
2626
# suppress inspection "UnusedProperty"
27-
kotlin.stdlib.default.dependency=false
28-
29-
30-
ktorVersion=2.0.1
27+
kotlin.stdlib.default.dependency=false
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.coder.gateway.sdk
2+
3+
import retrofit2.Call
4+
import retrofit2.http.Body
5+
import retrofit2.http.POST
6+
7+
/**
8+
* Coder does basic authentication on http, and once a session token is
9+
* received, everything else will happen on https.
10+
*/
11+
interface CoderAuthenticatonRestService {
12+
@POST("auth/basic/login")
13+
fun authenticate(@Body loginRequest: LoginRequest): Call<LoginResponse>
14+
}
Lines changed: 33 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,42 @@
11
package com.coder.gateway.sdk
22

3+
import com.coder.gateway.sdk.ex.AuthenticationException
4+
import com.intellij.openapi.Disposable
35
import com.intellij.openapi.components.Service
4-
import io.ktor.client.HttpClient
5-
import io.ktor.client.engine.cio.CIO
6-
import io.ktor.client.plugins.compression.ContentEncoding
7-
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
8-
import io.ktor.client.plugins.logging.DEFAULT
9-
import io.ktor.client.plugins.logging.LogLevel
10-
import io.ktor.client.plugins.logging.Logger
11-
import io.ktor.client.plugins.logging.Logging
12-
import io.ktor.client.request.post
13-
import io.ktor.client.request.setBody
14-
import io.ktor.client.statement.bodyAsText
15-
import io.ktor.http.ContentType
16-
import io.ktor.http.contentType
17-
import io.ktor.serialization.gson.gson
18-
import kotlinx.coroutines.runBlocking
19-
20-
@Service
21-
class CoderClientService {
22-
private val httpClient = HttpClient(CIO) {
23-
install(Logging) {
24-
logger = Logger.DEFAULT
25-
level = LogLevel.ALL
26-
}
27-
install(ContentEncoding)
28-
install(ContentNegotiation) {
29-
gson() {
30-
setPrettyPrinting()
31-
}
6+
import com.intellij.openapi.diagnostic.Logger
7+
import retrofit2.Retrofit
8+
import retrofit2.converter.gson.GsonConverterFactory
9+
10+
@Service(Service.Level.APP)
11+
class CoderClientService : Disposable {
12+
private lateinit var retroRestClient: CoderRestService
13+
14+
lateinit var sessionToken: String
15+
16+
/**
17+
* This must be called before anything else. It will authenticate with coder and retrieve a session token
18+
* @throws [AuthenticationException] if authentication failed
19+
*/
20+
fun initClientSession(host: String, port: Int, email: String, password: String) {
21+
val hostPath = host.trimEnd('/')
22+
val sessionTokenResponse = Retrofit.Builder()
23+
.baseUrl("http://$hostPath:$port")
24+
.addConverterFactory(GsonConverterFactory.create())
25+
.build()
26+
.create(CoderAuthenticatonRestService::class.java).authenticate(LoginRequest(email, password)).execute()
27+
28+
if (!sessionTokenResponse.isSuccessful) {
29+
throw AuthenticationException("Authentication failed with code:${sessionTokenResponse.code()}, reason: ${sessionTokenResponse.errorBody().toString()}")
3230
}
31+
sessionToken = sessionTokenResponse.body()!!.sessionToken
32+
retroRestClient = Retrofit.Builder()
33+
.baseUrl("https://$hostPath:$port")
34+
.addConverterFactory(GsonConverterFactory.create())
35+
.build()
36+
.create(CoderRestService::class.java)
3337
}
3438

35-
suspend fun authenthicateWithPassword(url: String, email: String, password: String) {
36-
val urlPath = url.trimEnd('/')
37-
val response = httpClient.post("$urlPath/auth/basic/login") {
38-
contentType(ContentType.Application.Json)
39-
setBody(LoginRequest(email, password))
40-
}
41-
42-
println(">>> ${response.bodyAsText()}")
43-
}
39+
override fun dispose() {
4440

45-
fun dispose() {
46-
httpClient.close()
4741
}
48-
}
49-
50-
fun main() {
51-
val coderClient = CoderClientService()
52-
53-
runBlocking {
54-
coderClient.authenthicateWithPassword("http://localhost:7080", "example@email.com", "password example")
55-
}
56-
57-
coderClient.dispose()
5842
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package com.coder.gateway.sdk
2+
3+
4+
interface CoderRestService
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.coder.gateway.sdk.ex
2+
3+
import java.io.IOException
4+
5+
class AuthenticationException(val reason: String) : IOException(reason)

0 commit comments

Comments
 (0)