Skip to content

Commit 6003e47

Browse files
committed
Implement client support to retrieve workspaces
- created by logged user - new models, i.e User, RebuildMessage, Workspace & WorskpaceStat - gson serializers for java.time.Instant - merged the two Coder Client API's, no need to have one only for https scheme
1 parent b945bf1 commit 6003e47

File tree

13 files changed

+232
-40
lines changed

13 files changed

+232
-40
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@
99
* welcome screen
1010
* basic connector view triggered by the Coder's welcome view. It asks the user a Coder hostname, port, email and password.
1111
* back button to return to the main welcome view
12-
* basic Coder http client which authenticates and retrieves a session token
12+
* basic Coder http client which authenticates, retrieves a session token and uses it to retrieve the Workspaces created by the
13+
user that is logged.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
package com.coder.gateway.models
22

3-
internal data class LoginModel(var host: String = "localhost", var port: Int = 7080, var email: String = "example@email.com", var password: String? = "")
3+
internal data class LoginModel(var uriScheme: UriScheme = UriScheme.HTTP, var host: String = "localhost", var port: Int = 7080, var email: String = "example@email.com", var password: String? = "")
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.coder.gateway.models
2+
3+
import com.google.gson.annotations.SerializedName
4+
import java.time.Duration
5+
6+
data class RebuildMessage(
7+
@SerializedName("text") val text: String,
8+
@SerializedName("required") val required: Boolean,
9+
@SerializedName("auto_off_threshold") val auto_off_threshold: Duration
10+
)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.coder.gateway.models
2+
3+
enum class UriScheme(val scheme: String) {
4+
HTTP("http"), HTTPS("https")
5+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.coder.gateway.models
2+
3+
import com.google.gson.annotations.SerializedName
4+
import java.time.Instant
5+
6+
7+
data class User(
8+
@SerializedName("id") val id: String,
9+
@SerializedName("email") val email: String,
10+
@SerializedName("username") val username: String,
11+
@SerializedName("name") val name: String,
12+
@SerializedName("roles") val roles: Set<String>,
13+
@SerializedName("temporary_password") val temporaryPassword: Boolean,
14+
@SerializedName("login_type") val loginType: Boolean,
15+
@SerializedName("key_regenerated_at") val keyRegeneratedAt: Boolean,
16+
@SerializedName("created_at") val createdAt: Instant,
17+
@SerializedName("updated_at") val updatedAt: Instant,
18+
)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.coder.gateway.models
2+
3+
import com.google.gson.annotations.SerializedName
4+
import java.time.Instant
5+
6+
data class Workspace(
7+
@SerializedName("id") val id: String,
8+
@SerializedName("name") val name: String,
9+
@SerializedName("image_id") val imageId: String,
10+
@SerializedName("image_tag") val imageTag: String,
11+
@SerializedName("organization_id") val organizationId: String,
12+
@SerializedName("user_id") val userId: String,
13+
@SerializedName("last_built_at") val lastBuiltAt: Instant,
14+
@SerializedName("cpu_cores") val cpuCores: Float,
15+
@SerializedName("memory_gb") val memoryGB: Float,
16+
@SerializedName("disk_gb") val disk_gb: Int,
17+
@SerializedName("gpus") val gpus: Int,
18+
@SerializedName("updating") val updating: Boolean,
19+
@SerializedName("latest_stat") val latestStat: WorkspaceStat,
20+
@SerializedName("rebuild_messages") val rebuildMessages: List<RebuildMessage>,
21+
@SerializedName("created_at") val createdAt: Instant,
22+
@SerializedName("updated_at") val updatedAt: Instant,
23+
@SerializedName("last_opened_at") val lastOpenedAt: Instant,
24+
@SerializedName("last_connection_at") val lastConnectionAt: Instant,
25+
@SerializedName("auto_off_threshold") val autoOffThreshold: Long,
26+
@SerializedName("use_container_vm") val useContainerVM: Boolean,
27+
@SerializedName("resource_pool_id") val resourcePoolId: String,
28+
29+
)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.coder.gateway.models
2+
3+
import com.google.gson.annotations.SerializedName
4+
import java.time.Instant
5+
6+
data class WorkspaceStat(
7+
@SerializedName("time") val time: Instant,
8+
@SerializedName("last_online") val last_online: Instant,
9+
@SerializedName("container_status") val container_status: String,
10+
@SerializedName("stat_error") val stat_error: String,
11+
@SerializedName("cpu_usage") val cpu_usage: Float,
12+
@SerializedName("memory_total") val memory_total: Long,
13+
@SerializedName("memory_usage") val memory_usage: Float,
14+
@SerializedName("disk_total") val disk_total: Long,
15+
@SerializedName("disk_used") val disk_used: Long,
16+
)

src/main/kotlin/com/coder/gateway/sdk/CoderAuthenticatonRestService.kt

Lines changed: 0 additions & 14 deletions
This file was deleted.
Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,71 @@
11
package com.coder.gateway.sdk
22

3+
import com.coder.gateway.models.UriScheme
4+
import com.coder.gateway.models.User
5+
import com.coder.gateway.models.Workspace
36
import com.coder.gateway.sdk.ex.AuthenticationException
4-
import com.intellij.openapi.Disposable
7+
import com.google.gson.Gson
8+
import com.google.gson.GsonBuilder
59
import com.intellij.openapi.components.Service
6-
import com.intellij.openapi.diagnostic.Logger
10+
import com.jetbrains.gateway.sdk.convertors.InstantConverter
11+
import okhttp3.OkHttpClient
12+
import okhttp3.logging.HttpLoggingInterceptor
713
import retrofit2.Retrofit
814
import retrofit2.converter.gson.GsonConverterFactory
15+
import java.time.Instant
916

1017
@Service(Service.Level.APP)
11-
class CoderClientService : Disposable {
18+
class CoderClientService {
1219
private lateinit var retroRestClient: CoderRestService
1320

1421
lateinit var sessionToken: String
22+
lateinit var me: User
1523

1624
/**
1725
* This must be called before anything else. It will authenticate with coder and retrieve a session token
1826
* @throws [AuthenticationException] if authentication failed
1927
*/
20-
fun initClientSession(host: String, port: Int, email: String, password: String) {
28+
fun initClientSession(uriScheme: UriScheme, host: String, port: Int, email: String, password: String) {
2129
val hostPath = host.trimEnd('/')
22-
val sessionTokenResponse = Retrofit.Builder()
23-
.baseUrl("http://$hostPath:$port")
24-
.addConverterFactory(GsonConverterFactory.create())
30+
31+
val gson: Gson = GsonBuilder()
32+
.registerTypeAdapter(Instant::class.java, InstantConverter())
33+
.setPrettyPrinting()
34+
.create()
35+
36+
val interceptor = HttpLoggingInterceptor()
37+
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY)
38+
39+
retroRestClient = Retrofit.Builder()
40+
.baseUrl("${uriScheme.scheme}://$hostPath:$port")
41+
.client(OkHttpClient.Builder().addInterceptor(interceptor).build())
42+
.addConverterFactory(GsonConverterFactory.create(gson))
2543
.build()
26-
.create(CoderAuthenticatonRestService::class.java).authenticate(LoginRequest(email, password)).execute()
44+
.create(CoderRestService::class.java)
45+
46+
val sessionTokenResponse = retroRestClient.authenticate(LoginRequest(email, password)).execute()
2747

2848
if (!sessionTokenResponse.isSuccessful) {
29-
throw AuthenticationException("Authentication failed with code:${sessionTokenResponse.code()}, reason: ${sessionTokenResponse.errorBody().toString()}")
49+
throw AuthenticationException("Authentication failed with code:${sessionTokenResponse.code()}, reason: ${sessionTokenResponse.message()}")
3050
}
3151
sessionToken = sessionTokenResponse.body()!!.sessionToken
32-
retroRestClient = Retrofit.Builder()
33-
.baseUrl("https://$hostPath:$port")
34-
.addConverterFactory(GsonConverterFactory.create())
35-
.build()
36-
.create(CoderRestService::class.java)
52+
53+
val userResponse = retroRestClient.me(sessionToken).execute()
54+
55+
if (!userResponse.isSuccessful) {
56+
throw IllegalStateException("Could not retrieve information about logged use:${userResponse.code()}, reason: ${userResponse.message()}")
57+
}
58+
59+
me = userResponse.body()!!
3760
}
3861

39-
override fun dispose() {
62+
fun workspaces(): List<Workspace> {
63+
val workspacesResponse = retroRestClient.workspaces(sessionToken, me.id).execute()
64+
if (!workspacesResponse.isSuccessful) {
65+
throw IllegalStateException("Could not retrieve Coder Workspaces:${workspacesResponse.code()}, reason: ${workspacesResponse.message()}")
66+
}
67+
68+
return workspacesResponse.body()!!
4069

4170
}
4271
}
Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,23 @@
11
package com.coder.gateway.sdk
22

3+
import com.coder.gateway.models.User
4+
import com.coder.gateway.models.Workspace
5+
import retrofit2.Call
6+
import retrofit2.http.Body
7+
import retrofit2.http.GET
8+
import retrofit2.http.Header
9+
import retrofit2.http.POST
10+
import retrofit2.http.Query
311

4-
interface CoderRestService
12+
13+
interface CoderRestService {
14+
15+
@POST("auth/basic/login")
16+
fun authenticate(@Body loginRequest: LoginRequest): Call<LoginResponse>
17+
18+
@GET("api/v0/users/me")
19+
fun me(@Header("Session-Token") sessionToken: String): Call<User>
20+
21+
@GET("api/v0/workspaces")
22+
fun workspaces(@Header("Session-Token") sessionToken: String, @Query("users") users: String): Call<List<Workspace>>
23+
}

0 commit comments

Comments
 (0)