Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jackson kun som test-avhengighet 🧪 #1

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions azure-token-client/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
val jacksonVersion = "2.16.1"
val mockkVersion = "1.13.9"
val orgJsonVersion = "20231013"

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")
testImplementation("org.json:json:$orgJsonVersion")
}
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,10 @@ 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 jackson = requireNotNull(Jackson.azureTokenResponseOrNull(json, utstedtTidspunkt))
val orgJson = requireNotNull(OrgJson.azureTokenResponseOrNull(json, utstedtTidspunkt))
assertEquals(utstedtTidspunkt.plusSeconds(3599), jackson.expirationTime)
assertEquals(jackson, orgJson)
}

@Test
Expand All @@ -43,6 +38,9 @@ class AzureTokenResponseTest {
"trace_id": "255d1aef-8c98-452f-ac51-23d051240864",
"correlation_id": "fb3d2015-bc17-4bb9-bb85-30c5cf1aaaa7"
}"""
assertDoesNotThrow { objectMapper.readValue<AzureErrorResponse>(json) }
val jackson = requireNotNull(Jackson.azureErrorResponseOrNull(json))
val orgJson = requireNotNull(OrgJson.azureErrorResponseOrNull(json))
assertEquals("invalid_scope", jackson.error)
assertEquals(jackson, orgJson)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
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
import org.json.JSONObject

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)
}

internal object OrgJson: JsonSerde {
override fun deserialize(content: String): Map<String, Any?> = JSONObject(content).toMap()
override fun serialize(content: Map<String, Any?>) = JSONObject(content).toString()
}