Skip to content

Commit 3ce509f

Browse files
committed
task: implement iam router with new test
1 parent 867e521 commit 3ce509f

File tree

13 files changed

+284
-25
lines changed

13 files changed

+284
-25
lines changed

build.gradle.kts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ version = "1.0-SNAPSHOT"
99

1010
repositories {
1111
mavenCentral()
12+
13+
maven {
14+
url = uri("https://maven.scijava.org/content/repositories/public/")
15+
}
1216
}
1317

1418
dependencies {
@@ -18,6 +22,7 @@ dependencies {
1822
implementation(libs.ktor.serialization.kotlinx.json)
1923
implementation(libs.ktor.client.content.negotiation)
2024
implementation(libs.jetbrains.kotlinx.serialization.json)
25+
implementation(libs.jsonapi.converter)
2126

2227
testImplementation(kotlin("test"))
2328
testImplementation(libs.mockk)

gradle/libs.versions.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ kotlin = "2.0.21"
33
ktor = "3.0.3"
44
kotlinSerialization = "1.7.3"
55
mockk = "1.9.3"
6+
jsonapi = "0.14"
67

78
[libraries]
89
ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
@@ -12,4 +13,5 @@ ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx
1213
ktor-client-mock = { module = "io.ktor:ktor-client-mock", version.ref = "ktor" }
1314
kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" }
1415
jetbrains-kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinSerialization" }
15-
mockk = { module = "io.mockk:mockk", version.ref = "mockk" }
16+
mockk = { module = "io.mockk:mockk", version.ref = "mockk" }
17+
jsonapi-converter = { module = "com.github.jasminb:jsonapi-converter", version.ref = "jsonapi" }

src/main/kotlin/com/ctrlhub/core/Api.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ package com.ctrlhub.core
22

33
import com.ctrlhub.core.api.KtorApiClient
44
import com.ctrlhub.core.auth.AuthRouter
5+
import com.ctrlhub.core.iam.IamRouter
56

67
class Api private constructor(apiClient: KtorApiClient) {
78
val auth: AuthRouter = AuthRouter(apiClient)
9+
val iam: IamRouter = IamRouter(apiClient)
810

911
companion object {
1012
fun create(): Api {

src/main/kotlin/com/ctrlhub/core/api/ApiException.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package com.ctrlhub.core.api
22

3-
import io.ktor.client.statement.HttpResponse
3+
import io.ktor.client.statement.*
44

55
open class ApiException(message: String, e: Throwable) : Exception(message, e)
66

src/main/kotlin/com/ctrlhub/core/api/KtorApiClient.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ import io.ktor.client.plugins.*
66
import io.ktor.client.plugins.contentnegotiation.*
77
import io.ktor.client.request.*
88
import io.ktor.client.statement.*
9-
import io.ktor.http.HttpHeaders
9+
import io.ktor.http.*
1010
import io.ktor.serialization.kotlinx.json.*
11-
import io.ktor.util.appendIfNameAbsent
11+
import io.ktor.util.*
1212
import kotlinx.serialization.encodeToString
1313
import kotlinx.serialization.json.Json
1414

src/main/kotlin/com/ctrlhub/core/auth/AuthRouter.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ import com.ctrlhub.core.auth.payload.LoginPayload
88
import com.ctrlhub.core.auth.response.AuthFlowResponse
99
import com.ctrlhub.core.auth.response.CompleteResponse
1010
import com.ctrlhub.core.router.Router
11-
import io.ktor.client.call.body
12-
import io.ktor.client.plugins.ClientRequestException
13-
import io.ktor.http.HttpStatusCode
11+
import io.ktor.client.call.*
12+
import io.ktor.client.plugins.*
13+
import io.ktor.http.*
1414

1515
class AuthRouter(apiClient: KtorApiClient) : Router(apiClient = apiClient) {
1616
suspend fun initiate(): AuthFlowResponse {
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.ctrlhub.core.iam
2+
3+
import com.ctrlhub.core.api.ApiClientException
4+
import com.ctrlhub.core.api.ApiException
5+
import com.ctrlhub.core.api.KtorApiClient
6+
import com.ctrlhub.core.iam.response.User
7+
import com.ctrlhub.core.router.Router
8+
import com.github.jasminb.jsonapi.ResourceConverter
9+
import io.ktor.client.call.*
10+
import io.ktor.client.plugins.*
11+
12+
class IamRouter(apiClient: KtorApiClient) : Router(apiClient) {
13+
14+
suspend fun whoami(sessionToken: String): User {
15+
return try {
16+
val rawResponse = apiClient.get("/v3/iam/whoami", mapOf("X-Session-Token" to sessionToken))
17+
val resourceConverter = ResourceConverter(User::class.java)
18+
val jsonApiResponse = resourceConverter.readDocument<User>(rawResponse.body<ByteArray>(), User::class.java)
19+
20+
jsonApiResponse.get()!!
21+
} catch (e: ClientRequestException) {
22+
throw ApiClientException("Whoami request failed", e.response, e)
23+
} catch (e: Exception) {
24+
throw ApiException("Whoami request failed", e)
25+
}
26+
}
27+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package com.ctrlhub.core.iam.response
2+
3+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
4+
import com.fasterxml.jackson.annotation.JsonProperty
5+
import com.github.jasminb.jsonapi.StringIdHandler
6+
import com.github.jasminb.jsonapi.annotations.Id
7+
import com.github.jasminb.jsonapi.annotations.Type
8+
9+
@Type("users")
10+
class User {
11+
@Id(StringIdHandler::class)
12+
lateinit var id: String
13+
lateinit var email: String
14+
lateinit var profile: Profile
15+
lateinit var identities: List<Identity>
16+
}
17+
18+
class Identity {
19+
val platform: String? = null
20+
val id: String? = null
21+
val meta: IdentityMeta? = null
22+
}
23+
24+
class IdentityMeta {
25+
@JsonProperty("organisation_id") val organisationId: String? = null
26+
}
27+
28+
class Profile {
29+
lateinit var address: ProfileAddress
30+
lateinit var contact: ProfileContact
31+
lateinit var personal: ProfilePersonal
32+
lateinit var settings: ProfileSettings
33+
lateinit var work: ProfileWork
34+
}
35+
36+
class ProfileAddress {
37+
lateinit var area: String
38+
@JsonProperty("country_code") lateinit var countryCode: String
39+
lateinit var county: String
40+
lateinit var name: String
41+
lateinit var number: String
42+
lateinit var postcode: String
43+
lateinit var street: String
44+
lateinit var town: String
45+
lateinit var what3words: String
46+
}
47+
48+
class ProfileContact {
49+
lateinit var landline: String
50+
lateinit var mobile: String
51+
}
52+
53+
@JsonIgnoreProperties(ignoreUnknown = true)
54+
class ProfilePersonal {
55+
lateinit var dob: String
56+
@JsonProperty("first_name") lateinit var firstName: String
57+
@JsonProperty("last_name") lateinit var lastName: String
58+
}
59+
60+
class ProfileSettings {
61+
@JsonProperty("preferred_language") lateinit var preferredLanguage: String
62+
lateinit var timezone: String
63+
}
64+
65+
class ProfileWork {
66+
lateinit var cscs: String
67+
lateinit var eusr: String
68+
lateinit var occupation: String
69+
@JsonProperty("start_date") lateinit var startDate: String
70+
}
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.ctrlhub.core.router
22

3-
import com.ctrlhub.core.Config
43
import com.ctrlhub.core.api.KtorApiClient
54

65
abstract class Router(val apiClient: KtorApiClient)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.ctrlhub.core.serializer
2+
3+
import kotlinx.serialization.ExperimentalSerializationApi
4+
import kotlinx.serialization.KSerializer
5+
import kotlinx.serialization.Serializer
6+
import kotlinx.serialization.encoding.Decoder
7+
import kotlinx.serialization.encoding.Encoder
8+
import java.time.LocalDate
9+
import java.time.format.DateTimeFormatter
10+
11+
@Serializer(forClass = LocalDate::class)
12+
class LocalDateSerializer : KSerializer<LocalDate?> {
13+
private val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
14+
15+
@OptIn(ExperimentalSerializationApi::class)
16+
override fun serialize(encoder: Encoder, value: LocalDate?) {
17+
if (value == null) {
18+
encoder.encodeNull()
19+
} else {
20+
encoder.encodeString(value.format(formatter))
21+
}
22+
}
23+
24+
override fun deserialize(decoder: Decoder): LocalDate? {
25+
val stringValue = decoder.decodeString()
26+
return if (stringValue.isEmpty()) {
27+
null
28+
} else {
29+
LocalDate.parse(stringValue, formatter)
30+
}
31+
}
32+
}

0 commit comments

Comments
 (0)