Skip to content

Commit

Permalink
Jackson kun som test-avhengighet 🧪
Browse files Browse the repository at this point in the history
  • Loading branch information
fraadsbrandth committed Jan 9, 2024
1 parent 864ddaf commit 49279b6
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 63 deletions.
5 changes: 2 additions & 3 deletions azure-token-client/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ val jacksonVersion = "2.16.1"
val mockkVersion = "1.13.9"

dependencies {
api("com.fasterxml.jackson.module:jackson-module-kotlin:$jacksonVersion")
api("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jacksonVersion")

testImplementation("com.fasterxml.jackson.module:jackson-module-kotlin:$jacksonVersion")
testImplementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jacksonVersion")
testImplementation("io.mockk:mockk:$mockkVersion")
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
package com.github.navikt.tbd_libs.azure

import com.fasterxml.jackson.annotation.JsonIgnoreProperties
import com.fasterxml.jackson.annotation.JsonProperty
import com.github.navikt.tbd_libs.azure.JsonSerde.Companion.deserializeOrNull
import com.github.navikt.tbd_libs.azure.JsonSerde.Companion.stringOrNull

@JsonIgnoreProperties(ignoreUnknown = true)
internal data class AzureErrorResponse(
@JsonProperty("error")
val error: String,
@JsonProperty("error_description")
val description: String
)
) {
internal companion object {
internal fun JsonSerde.azureErrorResponseOrNull(body: String): AzureErrorResponse? {
val json = deserializeOrNull(body) ?: return null
val error = json.stringOrNull("error") ?: return null
val errorDescription = json.stringOrNull("error_description") ?: return null
return AzureErrorResponse(error, errorDescription)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
package com.github.navikt.tbd_libs.azure

import com.fasterxml.jackson.databind.InjectableValues
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.SerializationFeature
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import com.github.navikt.tbd_libs.azure.AzureErrorResponse.Companion.azureErrorResponseOrNull
import com.github.navikt.tbd_libs.azure.AzureTokenResponse.Companion.azureTokenResponseOrNull
import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
Expand All @@ -18,9 +14,7 @@ class AzureTokenClient(
private val clientId: String,
private val authMethod: AzureAuthMethod,
private val client: HttpClient = HttpClient.newHttpClient(),
private val objectMapper: ObjectMapper = jacksonObjectMapper()
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.registerModule(JavaTimeModule())
private val jsonSerde: JsonSerde
) : AzureTokenProvider {

override fun onBehalfOfToken(scope: String, token: String) =
Expand All @@ -29,35 +23,16 @@ class AzureTokenClient(
håndterTokenRespons(requestBearerToken(scope))

private fun håndterTokenRespons(body: String): AzureToken {
val tokenResponse = deserializeToken(body) ?: kastExceptionVedFeil(body)
val tokenResponse = jsonSerde.azureTokenResponseOrNull(body, LocalDateTime.now()) ?: kastExceptionVedFeil(body)
return AzureToken(tokenResponse.token, tokenResponse.expirationTime)
}

private fun kastExceptionVedFeil(body: String): Nothing {
val error = deserializeErrorResponse(body)
val error = jsonSerde.azureErrorResponseOrNull(body)
?: throw AzureClientException("Ukjent feil ved henting av token. Kan ikke tolke responsen: $body")
throw AzureClientException("Feil fra azure: ${error.error}: ${error.description}")
}

private fun deserializeErrorResponse(body: String): AzureErrorResponse? {
return try {
objectMapper.readValue<AzureErrorResponse>(body)
} catch (_: Exception) {
null
}
}

private fun deserializeToken(body: String): AzureTokenResponse? {
val reader = objectMapper.reader(InjectableValues.Std()
.addValue(LocalDateTime::class.java, LocalDateTime.now())
).forType(AzureTokenResponse::class.java)
return try {
reader.readValue<AzureTokenResponse>(body)
} catch (_: Exception) {
null
}
}

private fun requestBearerToken(scope: String) = requestToken(buildTokenRequestBody(scope))
private fun requestOnBehalfOfToken(scope: String, token: String) = requestToken(buildOnBehalfOfRequestBody(scope, token))

Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
package com.github.navikt.tbd_libs.azure

import com.fasterxml.jackson.annotation.JacksonInject
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
import com.fasterxml.jackson.annotation.JsonProperty
import com.github.navikt.tbd_libs.azure.JsonSerde.Companion.deserializeOrNull
import com.github.navikt.tbd_libs.azure.JsonSerde.Companion.longOrNull
import com.github.navikt.tbd_libs.azure.JsonSerde.Companion.stringOrNull
import java.time.LocalDateTime

@JsonIgnoreProperties(ignoreUnknown = true)
internal data class AzureTokenResponse(
@JsonProperty("token_type")
val tokenType: String,
@JsonProperty("access_token")
val token: String,
@JsonProperty("expires_in")
val expiresIn: Long,
@JacksonInject
private val utstedtTidspunkt: LocalDateTime
) {
val expirationTime: LocalDateTime = utstedtTidspunkt.plusSeconds(expiresIn)
Expand All @@ -23,4 +18,14 @@ internal data class AzureTokenResponse(
"Forventer kun token av typen Bearer. Fikk $tokenType"
}
}

internal companion object {
internal fun JsonSerde.azureTokenResponseOrNull(body: String, utstedtTidspunkt: LocalDateTime): AzureTokenResponse? {
val json = deserializeOrNull(body) ?: return null
val tokenType = json.stringOrNull("token_type") ?: return null
val accessToken = json.stringOrNull("access_token") ?: return null
val expiresIn = json.longOrNull("expires_in") ?: return null
return AzureTokenResponse(tokenType, accessToken, expiresIn, utstedtTidspunkt)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.github.navikt.tbd_libs.azure

import java.lang.Exception

interface JsonSerde {
fun deserialize(content: String): Map<String, Any?>
fun serialize(content: Map<String, Any?>): String

companion object {
internal fun Map<String, Any?>.stringOrNull(key: String): String? = get(key)?.takeIf { it is String }?.let { it as String }
internal fun Map<String, Any?>.longOrNull(key: String): Long? = get(key)?.takeIf { it is Number }?.let { it as Number }?.toLong()
internal fun JsonSerde.deserializeOrNull(content: String) = try { deserialize(content) } catch (_: Exception) { null }
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
package com.github.navikt.tbd_libs.azure

import com.github.navikt.tbd_libs.azure.MockHttpClient.Companion.mockOkResponse
import com.github.navikt.tbd_libs.azure.MockHttpClient.Companion.verifiserJwtRequestBody
import com.github.navikt.tbd_libs.azure.MockHttpClient.Companion.verifiserClientSecretRequestBody
import com.github.navikt.tbd_libs.azure.MockHttpClient.Companion.verifiserJwtRequestBody
import com.github.navikt.tbd_libs.azure.MockHttpClient.Companion.verifiserOBOClientSecretRequestBody
import com.github.navikt.tbd_libs.azure.MockHttpClient.Companion.verifiserOBOJwtRequestBody
import com.github.navikt.tbd_libs.azure.MockHttpClient.Companion.verifiserPOST
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import java.net.URI
import java.net.http.HttpClient
import java.time.LocalDateTime

class AzureTokenClientTest {
private companion object {
Expand Down Expand Up @@ -59,13 +58,13 @@ class AzureTokenClientTest {

private fun requestToken(authMethod: AzureAuthMethod): Pair<HttpClient, AzureToken> {
val httpClient = mockOkResponse()
val client = AzureTokenClient(tokenEndpoint, CLIENT_ID, authMethod, httpClient)
val client = AzureTokenClient(tokenEndpoint, CLIENT_ID, authMethod, httpClient, Jackson)
return httpClient to client.bearerToken(SCOPE)
}

private fun requestOboToken(authMethod: AzureAuthMethod): Pair<HttpClient, AzureToken> {
val httpClient = mockOkResponse()
val client = AzureTokenClient(tokenEndpoint, CLIENT_ID, authMethod, httpClient)
val client = AzureTokenClient(tokenEndpoint, CLIENT_ID, authMethod, httpClient, Jackson)
return httpClient to client.onBehalfOfToken(SCOPE, OTHER_TOKEN)
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
package com.github.navikt.tbd_libs.azure

import com.fasterxml.jackson.databind.InjectableValues
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import com.github.navikt.tbd_libs.azure.AzureErrorResponse.Companion.azureErrorResponseOrNull
import com.github.navikt.tbd_libs.azure.AzureTokenResponse.Companion.azureTokenResponseOrNull
import org.intellij.lang.annotations.Language
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertDoesNotThrow
import java.time.LocalDateTime

class AzureTokenResponseTest {
private val objectMapper = jacksonObjectMapper()

@Test
fun deserializeTokenResponse() {
Expand All @@ -22,12 +19,8 @@ class AzureTokenResponseTest {
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik1uQ19WWmNBVGZNNXBP..."
}"""
val utstedtTidspunkt = LocalDateTime.of(2018, 1, 1, 13, 37, 0, 123)
val reader = objectMapper.reader(InjectableValues.Std()
.addValue(LocalDateTime::class.java, utstedtTidspunkt)
).forType(AzureTokenResponse::class.java)

val token = assertDoesNotThrow { reader.readValue<AzureTokenResponse>(json) }
assertEquals(utstedtTidspunkt.plusSeconds(3599), token.expirationTime)
val deserialized = requireNotNull(Jackson.azureTokenResponseOrNull(json, utstedtTidspunkt))
assertEquals(utstedtTidspunkt.plusSeconds(3599), deserialized.expirationTime)
}

@Test
Expand All @@ -43,6 +36,7 @@ class AzureTokenResponseTest {
"trace_id": "255d1aef-8c98-452f-ac51-23d051240864",
"correlation_id": "fb3d2015-bc17-4bb9-bb85-30c5cf1aaaa7"
}"""
assertDoesNotThrow { objectMapper.readValue<AzureErrorResponse>(json) }
val deserialized = requireNotNull(Jackson.azureErrorResponseOrNull(json))
assertEquals("invalid_scope", deserialized.error)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.github.navikt.tbd_libs.azure

import com.fasterxml.jackson.databind.SerializationFeature
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue

internal object Jackson: JsonSerde {
private val objectMapper = jacksonObjectMapper()
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.registerModule(JavaTimeModule())
override fun deserialize(content: String): Map<String, Any?> = objectMapper.readValue(content)
override fun serialize(content: Map<String, Any?>): String = objectMapper.writeValueAsString(content)
}

0 comments on commit 49279b6

Please sign in to comment.