Skip to content

Commit

Permalink
Specify http cache per platform (#1072)
Browse files Browse the repository at this point in the history
* Specify http cache per platform

* Match top level naming

* Add missing trailing comma before closing bracket

* Include okio dependency

* Fix dependency target

---------

Co-authored-by: Ashley Davies <[email protected]>
  • Loading branch information
ashdavies and ashdavies authored Jul 17, 2024
1 parent 834a9f6 commit 06cc41f
Show file tree
Hide file tree
Showing 22 changed files with 154 additions and 107 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package io.ashdavies.events

import io.ashdavies.http.UnaryCallable
import io.ashdavies.http.defaultHttpClient
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.plugins.ClientRequestException
Expand All @@ -23,7 +22,7 @@ internal data class GetEventsRequest(
internal typealias GetEventsResponse = List<ApiEvent>

internal class GetEventsCallable(
httpClient: HttpClient = defaultHttpClient(),
httpClient: HttpClient,
) : UnaryCallable<GetEventsRequest, GetEventsResponse> {

private val httpClient = httpClient.config {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ private val HttpRequestData.path: String
private val Headers.contentLength: String
get() = requireNotNull(get(HttpHeaders.ContentLength))

internal fun inMemoryHttpClientEngine(initialValue: List<String>): HttpClientEngine {
internal fun inMemoryHttpClientEngine(initialValue: List<String> = emptyList()): HttpClientEngine {
val values = initialValue.toMutableList()

return MockEngine { request ->
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package io.ashdavies.gallery

import io.ashdavies.http.defaultHttpClient
import io.ashdavies.http.DefaultHttpConfiguration
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.request.get
Expand Down Expand Up @@ -28,7 +28,7 @@ internal enum class SyncState {
}

internal fun SyncManager(
client: HttpClient = defaultHttpClient(inMemoryHttpClientEngine(emptyList())),
client: HttpClient = HttpClient(inMemoryHttpClientEngine(), DefaultHttpConfiguration),
reader: File.() -> ByteReadChannel = File::readChannel,
): SyncManager = object : SyncManager {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package io.ashdavies.gallery

import app.cash.turbine.test
import io.ashdavies.http.defaultHttpClient
import io.ashdavies.http.DefaultHttpConfiguration
import io.ashdavies.util.randomUuid
import io.ktor.client.HttpClient
import io.ktor.utils.io.ByteReadChannel
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
Expand All @@ -15,7 +16,10 @@ internal class SyncManagerTest {
@Test
fun `should request initial value`() = runTest {
val manager = SyncManager(
client = defaultHttpClient(inMemoryHttpClientEngine(listOf(RandomImage))),
client = HttpClient(
engine = inMemoryHttpClientEngine(listOf(RandomImage)),
block = DefaultHttpConfiguration,
),
reader = { ByteReadChannel.Empty },
)

Expand All @@ -27,7 +31,10 @@ internal class SyncManagerTest {
@Test
fun `should sync image on invocation`() = runTest {
val manager = SyncManager(
client = defaultHttpClient(inMemoryHttpClientEngine(emptyList())),
client = HttpClient(
engine = inMemoryHttpClientEngine(emptyList()),
block = DefaultHttpConfiguration,
),
reader = { ByteReadChannel.Empty },
)

Expand All @@ -44,7 +51,10 @@ internal class SyncManagerTest {
@Test
fun `should put synced image without content`() = runTest {
val manager = SyncManager(
client = defaultHttpClient(inMemoryHttpClientEngine(listOf(RandomImage))),
client = HttpClient(
engine = inMemoryHttpClientEngine(listOf(RandomImage)),
block = DefaultHttpConfiguration,
),
reader = { ByteReadChannel.Empty },
)

Expand All @@ -61,7 +71,10 @@ internal class SyncManagerTest {
@Test
fun `should include content length header`() = runTest {
val manager = SyncManager(
client = defaultHttpClient(inMemoryHttpClientEngine(emptyList())),
client = HttpClient(
engine = inMemoryHttpClientEngine(emptyList()),
block = DefaultHttpConfiguration,
),
reader = { ByteReadChannel.Empty },
)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package io.ashdavies.check

import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.produceState
Expand All @@ -12,7 +11,7 @@ import com.google.firebase.appcheck.playintegrity.PlayIntegrityAppCheckProviderF
import dev.gitlive.firebase.Firebase
import dev.gitlive.firebase.app
import io.ashdavies.http.AppCheckToken
import io.ashdavies.http.LocalHttpClient
import io.ashdavies.http.ProvideHttpClient
import io.ktor.client.HttpClient
import io.ktor.client.plugins.DefaultRequest
import io.ktor.client.request.header
Expand All @@ -32,8 +31,8 @@ public actual fun ProvideAppCheckToken(client: HttpClient, content: @Composable
appCheck.installAppCheckProviderFactory(factory)
}

CompositionLocalProvider(
LocalHttpClient provides client.config {
ProvideHttpClient(
config = {
install(DefaultRequest) {
header(HttpHeaders.AppCheckToken, token?.token)
}
Expand Down
1 change: 1 addition & 0 deletions app-launcher/android/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ kotlin {
implementation(libs.ktor.client.core)
implementation(libs.slack.circuit.foundation)
implementation(libs.slack.circuit.overlay)
implementation(libs.squareup.okio)
}

val androidInstrumentedTest by getting {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import com.slack.circuit.foundation.CircuitCompositionLocals
Expand All @@ -17,22 +16,29 @@ import com.slack.circuit.foundation.rememberCircuitNavigator
import com.slack.circuit.overlay.ContentWithOverlays
import io.ashdavies.content.enableStrictMode
import io.ashdavies.content.isDebuggable
import io.ashdavies.http.LocalHttpClient
import io.ashdavies.http.ProvideHttpClient
import io.ashdavies.http.publicStorage
import io.ashdavies.io.resolveCacheDir
import io.ktor.client.plugins.DefaultRequest
import io.ktor.client.plugins.cache.HttpCache
import io.ktor.client.request.header
import java.security.MessageDigest
import java.util.Locale

@Composable
private fun LauncherApp(context: Context = LocalContext.current, extra: (String) -> String?) {
CompositionLocalProvider(
LocalHttpClient provides LocalHttpClient.current.config {
ProvideHttpClient(
config = {
install(DefaultRequest) {
header("X-Android-Cert", getSignature(context.packageManager, context.packageName))
header("X-Android-Package", context.packageName)
header("X-API-Key", BuildConfig.ANDROID_API_KEY)
header("User-Agent", Build.PRODUCT)
}

install(HttpCache) {
publicStorage(context.resolveCacheDir())
}
},
) {
CircuitCompositionLocals(remember { Circuit(context) }) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ import io.ashdavies.check.ProvideAppCheckToken
import io.ashdavies.common.PlaygroundDatabase
import io.ashdavies.content.PlatformContext
import io.ashdavies.dominion.Card
import io.ashdavies.io.resolveCacheDir
import io.ashdavies.material.dynamicColorScheme
import io.ashdavies.sql.ProvideTransacter
import io.ashdavies.sql.rememberTransacter
import okio.Path

private const val IMAGE_CACHE_PATH = "image_cache"

Expand Down Expand Up @@ -61,7 +61,3 @@ public fun LauncherContent(context: PlatformContext, content: @Composable () ->
}
}
}

internal expect fun PlatformContext.resolveCacheDir(
relative: String,
): Path
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package io.ashdavies.playground

import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
Expand All @@ -13,7 +12,7 @@ import com.slack.circuit.foundation.CircuitCompositionLocals
import com.slack.circuit.foundation.NavigableCircuitContent
import com.slack.circuit.foundation.rememberCircuitNavigator
import io.ashdavies.content.PlatformContext
import io.ashdavies.http.LocalHttpClient
import io.ashdavies.http.ProvideHttpClient
import io.ktor.client.plugins.DefaultRequest
import io.ktor.client.request.header

Expand All @@ -30,8 +29,8 @@ private class LauncherCommand : CliktCommand() {
val circuit = remember { Circuit(PlatformContext.Default) }

CircuitCompositionLocals(circuit) {
CompositionLocalProvider(
LocalHttpClient provides LocalHttpClient.current.config {
ProvideHttpClient(
config = {
install(DefaultRequest) {
header("User-Agent", System.getProperty("os.name"))
header("X-API-Key", BuildConfig.BROWSER_API_KEY)
Expand Down
2 changes: 2 additions & 0 deletions cloud-run/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ kotlin {
implementation(projects.eventsAggregator)
implementation(projects.httpClient)
implementation(projects.httpCommon)
implementation(projects.platformSupport)

implementation(libs.google.firebase.admin)
implementation(libs.kotlinx.datetime)
Expand All @@ -82,6 +83,7 @@ kotlin {
implementation(libs.ktor.server.core)
implementation(libs.ktor.server.default.headers)
implementation(libs.ktor.server.host.common)
implementation(libs.squareup.okio)
}

commonTest.dependencies {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package io.ashdavies.cloud

import kotlinx.serialization.Serializable

@Serializable
internal data class GoogleApiException(
val error: Error,
) : Exception(error.message) {

@Serializable
data class Error(
val code: Int,
val message: String,
val status: String,
)
}
24 changes: 22 additions & 2 deletions cloud-run/src/commonMain/kotlin/io/ashdavies/cloud/Main.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
package io.ashdavies.cloud

import io.ashdavies.http.defaultHttpClient
import io.ashdavies.content.PlatformContext
import io.ashdavies.http.DefaultHttpConfiguration
import io.ashdavies.http.publicStorage
import io.ashdavies.http.throwClientRequestExceptionAs
import io.ashdavies.io.resolveCacheDir
import io.ktor.client.HttpClient
import io.ktor.client.plugins.HttpCallValidator
import io.ktor.client.plugins.cache.HttpCache
import io.ktor.http.HttpHeaders
import io.ktor.serialization.Configuration
import io.ktor.serialization.kotlinx.json.json
Expand All @@ -28,7 +34,21 @@ public fun main() {
server.start(wait = true)
}

internal fun Application.main(client: HttpClient = defaultHttpClient { installCallValidator() }) {
internal fun Application.main() {
val client = HttpClient {
DefaultHttpConfiguration()

install(HttpCache) {
publicStorage(PlatformContext.Default.resolveCacheDir())
}

install(HttpCallValidator) {
throwClientRequestExceptionAs<GoogleApiException>()
}

expectSuccess = true
}

install(DefaultHeaders, DefaultHeadersConfig::headers)
install(Compression, CompressionConfig::default)
install(ContentNegotiation, Configuration::json)
Expand Down
30 changes: 0 additions & 30 deletions cloud-run/src/commonMain/kotlin/io/ashdavies/cloud/Validator.kt

This file was deleted.

1 change: 1 addition & 0 deletions http-client/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ plugins {
kotlin {
sourceSets.commonMain.dependencies {
implementation(projects.httpCommon)
implementation(projects.platformSupport)

implementation(compose.runtime)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.ashdavies.http

import io.ktor.client.call.body
import io.ktor.client.plugins.ClientRequestException
import io.ktor.client.plugins.HttpCallValidator

public inline fun <reified T : Exception> HttpCallValidator.Config.throwClientRequestExceptionAs() {
handleResponseExceptionWithRequest { exception, _ ->
if (exception is ClientRequestException) throw exception.response.body<T>()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.ashdavies.http

import io.ktor.client.plugins.cache.HttpCache
import io.ktor.client.plugins.cache.storage.FileStorage
import okio.Path

public fun HttpCache.Config.publicStorage(path: Path) {
publicStorage(FileStorage(path.toFile()))
}
Loading

0 comments on commit 06cc41f

Please sign in to comment.