From 3a2baebfd5885c8a099a92311f59d6f6bfd5052d Mon Sep 17 00:00:00 2001 From: James Fenn Date: Thu, 25 Jun 2020 19:39:53 -0400 Subject: [PATCH] implement memory / disk cache - fix #4 --- .../gitrest/example/android/MainActivity.kt | 14 ++-- .../java/me/jfenn/gitrest/example/jvm/Main.kt | 17 ++--- example-kotlinbrowser/src/main/kotlin/Main.kt | 19 +++-- gitrest/src/androidMain/kotlin/Stub.kt | 1 - .../me/jfenn/gitrest/model/PlatformConfig.kt | 3 + .../me/jfenn/gitrest/service/DiskCache.kt | 69 +++++++++++++++++++ .../me/jfenn/gitrest/service/MemoryCache.kt | 49 +++++++++++++ ...stProviderDelegate.kt => GitrestClient.kt} | 28 +++++--- .../me/jfenn/gitrest/base/ServiceBuilder.kt | 7 +- .../me/jfenn/gitrest/model/GitrestConfig.kt | 37 ++++++++++ .../{impl => provider}/gitea/GiteaProvider.kt | 11 +-- .../gitea/model/GiteaLicense.kt | 2 +- .../gitea/model/GiteaRepo.kt | 2 +- .../gitea/model/GiteaUser.kt | 2 +- .../github/GithubProvider.kt | 14 ++-- .../github/model/GithubLicense.kt | 2 +- .../github/model/GithubRepo.kt | 2 +- .../github/model/GithubUser.kt | 2 +- .../gitlab/GitlabProvider.kt | 13 ++-- .../gitlab/model/GitlabLicense.kt | 2 +- .../gitlab/model/GitlabRepo.kt | 2 +- .../gitlab/model/GitlabUser.kt | 2 +- .../kotlin/me/jfenn/gitrest/service/Cache.kt | 9 +++ .../me/jfenn/gitrest/service/MemoryCache.kt | 3 + .../me/jfenn/gitrest/service/NoCache.kt | 8 +++ gitrest/src/jsMain/kotlin/RequestProvider.kt | 22 +++--- .../me/jfenn/gitrest/model/PlatformConfig.kt | 3 + .../me/jfenn/gitrest/service/MemoryCache.kt | 23 +++++++ .../me/jfenn/gitrest/model/PlatformConfig.kt | 3 + .../me/jfenn/gitrest/service/MemoryCache.kt | 49 +++++++++++++ 30 files changed, 343 insertions(+), 77 deletions(-) delete mode 100644 gitrest/src/androidMain/kotlin/Stub.kt create mode 100644 gitrest/src/androidMain/kotlin/me/jfenn/gitrest/model/PlatformConfig.kt create mode 100644 gitrest/src/androidMain/kotlin/me/jfenn/gitrest/service/DiskCache.kt create mode 100644 gitrest/src/androidMain/kotlin/me/jfenn/gitrest/service/MemoryCache.kt rename gitrest/src/commonMain/kotlin/me/jfenn/gitrest/{RequestProviderDelegate.kt => GitrestClient.kt} (68%) create mode 100644 gitrest/src/commonMain/kotlin/me/jfenn/gitrest/model/GitrestConfig.kt rename gitrest/src/commonMain/kotlin/me/jfenn/gitrest/{impl => provider}/gitea/GiteaProvider.kt (80%) rename gitrest/src/commonMain/kotlin/me/jfenn/gitrest/{impl => provider}/gitea/model/GiteaLicense.kt (64%) rename gitrest/src/commonMain/kotlin/me/jfenn/gitrest/{impl => provider}/gitea/model/GiteaRepo.kt (94%) rename gitrest/src/commonMain/kotlin/me/jfenn/gitrest/{impl => provider}/gitea/model/GiteaUser.kt (94%) rename gitrest/src/commonMain/kotlin/me/jfenn/gitrest/{impl => provider}/github/GithubProvider.kt (75%) rename gitrest/src/commonMain/kotlin/me/jfenn/gitrest/{impl => provider}/github/model/GithubLicense.kt (94%) rename gitrest/src/commonMain/kotlin/me/jfenn/gitrest/{impl => provider}/github/model/GithubRepo.kt (94%) rename gitrest/src/commonMain/kotlin/me/jfenn/gitrest/{impl => provider}/github/model/GithubUser.kt (95%) rename gitrest/src/commonMain/kotlin/me/jfenn/gitrest/{impl => provider}/gitlab/GitlabProvider.kt (79%) rename gitrest/src/commonMain/kotlin/me/jfenn/gitrest/{impl => provider}/gitlab/model/GitlabLicense.kt (94%) rename gitrest/src/commonMain/kotlin/me/jfenn/gitrest/{impl => provider}/gitlab/model/GitlabRepo.kt (93%) rename gitrest/src/commonMain/kotlin/me/jfenn/gitrest/{impl => provider}/gitlab/model/GitlabUser.kt (95%) create mode 100644 gitrest/src/commonMain/kotlin/me/jfenn/gitrest/service/Cache.kt create mode 100644 gitrest/src/commonMain/kotlin/me/jfenn/gitrest/service/MemoryCache.kt create mode 100644 gitrest/src/commonMain/kotlin/me/jfenn/gitrest/service/NoCache.kt create mode 100644 gitrest/src/jsMain/kotlin/me/jfenn/gitrest/model/PlatformConfig.kt create mode 100644 gitrest/src/jsMain/kotlin/me/jfenn/gitrest/service/MemoryCache.kt create mode 100644 gitrest/src/jvmMain/kotlin/me/jfenn/gitrest/model/PlatformConfig.kt create mode 100644 gitrest/src/jvmMain/kotlin/me/jfenn/gitrest/service/MemoryCache.kt diff --git a/example-android/src/main/java/me/jfenn/gitrest/example/android/MainActivity.kt b/example-android/src/main/java/me/jfenn/gitrest/example/android/MainActivity.kt index cfa5b8f..d3145fb 100644 --- a/example-android/src/main/java/me/jfenn/gitrest/example/android/MainActivity.kt +++ b/example-android/src/main/java/me/jfenn/gitrest/example/android/MainActivity.kt @@ -7,18 +7,24 @@ import androidx.recyclerview.widget.RecyclerView import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import me.jfenn.androidutils.bind -import me.jfenn.gitrest.RequestProviderDelegate +import me.jfenn.gitrest.GitrestClient +import me.jfenn.gitrest.gitrest +import me.jfenn.gitrest.service.DiskCache +import me.jfenn.gitrest.service.MemoryCache class MainActivity : AppCompatActivity() { val recycler: RecyclerView? by bind(R.id.recycler) - val delegate = RequestProviderDelegate() - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) + val client = gitrest { + cache = MemoryCache(DiskCache(this, cacheDir)) + logDebug = { println(it) } + } + recycler?.layoutManager = LinearLayoutManager(this) GlobalScope.launch { val repos = listOf( @@ -26,7 +32,7 @@ class MainActivity : AppCompatActivity() { "gitlab@salsa.debian.org:reproducible-builds/reproducible-website", "gitea@code.horrific.dev:james/git-rest-wrapper", "gitlab@code.horrific.dev:james/git-rest-wrapper" - ).map { delegate.getRepo(it) } + ).map { client.getRepo(it) } recycler?.post { recycler?.adapter = RepoAdapter(repos) diff --git a/example-jvm/src/main/java/me/jfenn/gitrest/example/jvm/Main.kt b/example-jvm/src/main/java/me/jfenn/gitrest/example/jvm/Main.kt index 4aca247..8286c09 100644 --- a/example-jvm/src/main/java/me/jfenn/gitrest/example/jvm/Main.kt +++ b/example-jvm/src/main/java/me/jfenn/gitrest/example/jvm/Main.kt @@ -1,20 +1,13 @@ package me.jfenn.gitrest.example.jvm -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import me.jfenn.gitrest.RequestProviderDelegate -import me.jfenn.gitrest.impl.gitea.GiteaProvider -import me.jfenn.gitrest.impl.github.GithubProvider -import me.jfenn.gitrest.impl.gitlab.GitlabProvider +import me.jfenn.gitrest.gitrest -val delegate = RequestProviderDelegate(arrayOf( - GithubProvider, - GitlabProvider, - GiteaProvider -)) +val client = gitrest { + logDebug = { println(it) } +} suspend fun fetchRepository(repoId: String) { - delegate.getRepo(repoId)?.let { + client.getRepo(repoId)?.let { println(""" ${it.id}: ${it.description} diff --git a/example-kotlinbrowser/src/main/kotlin/Main.kt b/example-kotlinbrowser/src/main/kotlin/Main.kt index 2afab11..850eadc 100644 --- a/example-kotlinbrowser/src/main/kotlin/Main.kt +++ b/example-kotlinbrowser/src/main/kotlin/Main.kt @@ -1,21 +1,20 @@ -import me.jfenn.gitrest.RequestProviderDelegate -import me.jfenn.gitrest.impl.gitea.GiteaProvider -import me.jfenn.gitrest.impl.github.GithubProvider -import me.jfenn.gitrest.impl.gitlab.GitlabProvider +import me.jfenn.gitrest.GitrestClient +import me.jfenn.gitrest.provider.gitea.GiteaProvider +import me.jfenn.gitrest.provider.github.GithubProvider +import me.jfenn.gitrest.provider.gitlab.GitlabProvider import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlinx.html.InputType import kotlinx.html.button import kotlinx.html.dom.append import kotlinx.html.js.* +import me.jfenn.gitrest.gitrest import org.w3c.dom.HTMLInputElement import kotlin.browser.document -val delegate = RequestProviderDelegate(arrayOf( - GithubProvider, - GitlabProvider, - GiteaProvider -)) +val client = gitrest { + logDebug = { console.log(it) } +} fun main() { document.body!!.append { @@ -41,7 +40,7 @@ fun main() { } } -suspend fun fetchRepo(id: String) = delegate.getRepo(id)?.let { repo -> +suspend fun fetchRepo(id: String) = client.getRepo(id)?.let { repo -> document.body!!.append { div { h3 { diff --git a/gitrest/src/androidMain/kotlin/Stub.kt b/gitrest/src/androidMain/kotlin/Stub.kt deleted file mode 100644 index 5cd75a2..0000000 --- a/gitrest/src/androidMain/kotlin/Stub.kt +++ /dev/null @@ -1 +0,0 @@ -// empty file to ensure that gradle compiles the target diff --git a/gitrest/src/androidMain/kotlin/me/jfenn/gitrest/model/PlatformConfig.kt b/gitrest/src/androidMain/kotlin/me/jfenn/gitrest/model/PlatformConfig.kt new file mode 100644 index 0000000..d38772c --- /dev/null +++ b/gitrest/src/androidMain/kotlin/me/jfenn/gitrest/model/PlatformConfig.kt @@ -0,0 +1,3 @@ +package me.jfenn.gitrest.model + +actual class PlatformConfig {} diff --git a/gitrest/src/androidMain/kotlin/me/jfenn/gitrest/service/DiskCache.kt b/gitrest/src/androidMain/kotlin/me/jfenn/gitrest/service/DiskCache.kt new file mode 100644 index 0000000..a446c1d --- /dev/null +++ b/gitrest/src/androidMain/kotlin/me/jfenn/gitrest/service/DiskCache.kt @@ -0,0 +1,69 @@ +package me.jfenn.gitrest.service + +import kotlinx.serialization.* +import kotlinx.serialization.builtins.ListSerializer +import kotlinx.serialization.builtins.list +import kotlinx.serialization.builtins.serializer +import kotlinx.serialization.modules.SerializersModule +import me.jfenn.gitrest.model.GitrestConfig +import me.jfenn.gitrest.provider.gitea.model.GiteaUser +import java.io.File +import java.io.IOException +import java.io.PrintWriter + +/** + * Naive caching implementation for the JVM that uses WeakReference + * as a cache "buffer" that can be cleaned up by the GC when necessary. + */ +class DiskCache( + val config: GitrestConfig, + appCacheDir: File, + val cacheDuration: Long = 864000000 // cache for ~10 days by default +) : Cache { + + val cacheDir = File(appCacheDir, "http") + + fun String.cacheFile() = File(cacheDir, "${this.replace(File.separator, "_")}.json") + + @ImplicitReflectionSerializer + fun getSerializerOf(value: Any) : KSerializer? { + return if (value is List<*>) { + return (value.firstOrNull()?.let { it::class.serializerOrNull() } ?: String.serializer()).list as KSerializer + } else { + return value::class.serializerOrNull() as KSerializer + } + } + + @ImplicitReflectionSerializer + override fun set(key: String, value: Any) { + val serializer = getSerializerOf(value) ?: return + val string = value.javaClass.name + "#" + System.currentTimeMillis() + "#" + config.jsonSerializer.stringify(serializer, value) + + try { + cacheDir.mkdirs() + key.cacheFile().writeText(string) + } catch (e: IOException) { + config.logError("GIT-REST: ${e::class.simpleName} - ${e.message}") + } + } + + @ImplicitReflectionSerializer + override fun get(key: String): T? { + return try { + val fileContents = key.cacheFile().readText().split("#", limit = 3) + if (fileContents.size != 3) return null + val (className, lastModified, json) = fileContents + + if (System.currentTimeMillis() - lastModified.toLong() < cacheDuration) { + config.jsonSerializer.parse( + Class.forName(className).kotlin.serializer(), + json + ) as? T + } else null + } catch (e : IOException) { + config.logError("GIT-REST: ${e::class.simpleName} - ${e.message}") + null + } + } + +} diff --git a/gitrest/src/androidMain/kotlin/me/jfenn/gitrest/service/MemoryCache.kt b/gitrest/src/androidMain/kotlin/me/jfenn/gitrest/service/MemoryCache.kt new file mode 100644 index 0000000..dcf02a6 --- /dev/null +++ b/gitrest/src/androidMain/kotlin/me/jfenn/gitrest/service/MemoryCache.kt @@ -0,0 +1,49 @@ +package me.jfenn.gitrest.service + +import me.jfenn.gitrest.model.DelegateResource +import java.lang.ref.ReferenceQueue +import java.lang.ref.WeakReference + +/** + * Naive caching implementation for the JVM that uses WeakReference + * as a cache "buffer" that can be cleaned up by the GC when necessary. + */ +actual class MemoryCache actual constructor( + val underlyingCache: Cache +) : Cache { + + private class WeakEntry internal constructor( + internal val key: String, + value: Any, + referenceQueue: ReferenceQueue) : WeakReference(value, referenceQueue) + + private val referenceQueue = ReferenceQueue() + private val map = HashMap() + + override fun set(key: String, value: Any) { + // remove any invalid references + var weakEntry = referenceQueue.poll() as WeakEntry? + while (weakEntry != null) { + map.remove(weakEntry.key) + weakEntry = referenceQueue.poll() as WeakEntry? + } + + map[key] = WeakEntry(key, value, referenceQueue) + underlyingCache.set(key, value) + } + + override fun get(key: String): T? { + val weakEntry = map[key] + weakEntry?.get()?.let { + return it as? T + } + + underlyingCache.get(key)?.let { + map[key] = WeakEntry(key, it as Any, referenceQueue) + return it + } + + return null + } + +} diff --git a/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/RequestProviderDelegate.kt b/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/GitrestClient.kt similarity index 68% rename from gitrest/src/commonMain/kotlin/me/jfenn/gitrest/RequestProviderDelegate.kt rename to gitrest/src/commonMain/kotlin/me/jfenn/gitrest/GitrestClient.kt index 02ab6e5..cec0163 100644 --- a/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/RequestProviderDelegate.kt +++ b/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/GitrestClient.kt @@ -1,16 +1,15 @@ package me.jfenn.gitrest import me.jfenn.gitrest.base.RequestProvider -import me.jfenn.gitrest.base.ServiceBuilder -import me.jfenn.gitrest.impl.gitea.GiteaProvider -import me.jfenn.gitrest.impl.github.GithubProvider -import me.jfenn.gitrest.impl.gitlab.GitlabProvider import me.jfenn.gitrest.model.* -class RequestProviderDelegate( - private val services: Array> = arrayOf(GithubProvider, GitlabProvider, GiteaProvider), - private val strictMode: Boolean = false, - private val logError: (String) -> Unit = { println(it) } // TODO: implement a more robust log handler + check BuildConfig.DEBUG before logging +fun gitrest(block: GitrestConfig.() -> Unit) : GitrestClient { + val config = GitrestConfig().apply { block() } + return GitrestClient(config) +} + +class GitrestClient( + private val config : GitrestConfig ) : RequestProvider { private val providers: MutableMap = HashMap() @@ -18,9 +17,14 @@ class RequestProviderDelegate( private suspend fun get(str: String, block: suspend (RequestProvider, String) -> T?) : T? { val providerStr = ProviderString(str) + config.cache.get(providerStr.toString())?.let { + config.logDebug("GIT-REST: Returning cached entry for $providerStr") + return it + } + val providerId = "${providerStr.provider}@${providerStr.hostname}" val provider = providers[providerId] ?: run { - services.firstOrNull { it.key == providerStr.provider }?.create(providerStr.hostname)?.also { it -> + config.services.firstOrNull { it.key == providerStr.provider }?.create(config, providerStr.hostname)?.also { it -> providers[providerId] = it } ?: throw RuntimeException("Provider not found: $providerId") // TODO: query random endpoints to determine provider @@ -34,10 +38,12 @@ class RequestProviderDelegate( (resource as? DelegateResource)?.setId(providerStr) ?: (resource as? List)?.forEach { it.setId(providerStr) } + }?.also { + config.cache.set(providerStr.toString(), it as Any) } } catch (e: Exception) { - if (strictMode) throw e - else logError("GIT-REST: Error fetching ${str}: ${e::class.simpleName} - ${e.message}") + if (config.strictMode) throw e + else config.logError("GIT-REST: Error fetching ${str}: ${e::class.simpleName} - ${e.message}") null } } diff --git a/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/base/ServiceBuilder.kt b/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/base/ServiceBuilder.kt index abe3059..b063064 100644 --- a/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/base/ServiceBuilder.kt +++ b/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/base/ServiceBuilder.kt @@ -6,22 +6,23 @@ import io.ktor.client.features.json.JsonFeature import io.ktor.client.features.json.serializer.KotlinxSerializer import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonConfiguration +import me.jfenn.gitrest.model.GitrestConfig interface ServiceBuilder { val key : String val tokens : MutableMap - fun ktor(block: HttpClientConfig<*>.() -> Unit) : HttpClient { + fun GitrestConfig.ktor(block: HttpClientConfig<*>.() -> Unit) : HttpClient { return HttpClient { install(JsonFeature) { - serializer = KotlinxSerializer(Json(JsonConfiguration.Stable.copy(ignoreUnknownKeys = true))) + serializer = KotlinxSerializer(jsonSerializer) } block() } } - fun create(hostname: String): T + fun create(config: GitrestConfig, hostname: String): T } diff --git a/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/model/GitrestConfig.kt b/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/model/GitrestConfig.kt new file mode 100644 index 0000000..87e53dc --- /dev/null +++ b/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/model/GitrestConfig.kt @@ -0,0 +1,37 @@ +package me.jfenn.gitrest.model + +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonConfiguration +import me.jfenn.gitrest.base.ServiceBuilder +import me.jfenn.gitrest.provider.gitea.GiteaProvider +import me.jfenn.gitrest.provider.github.GithubProvider +import me.jfenn.gitrest.provider.gitlab.GitlabProvider +import me.jfenn.gitrest.service.Cache +import me.jfenn.gitrest.service.MemoryCache + +class GitrestConfig { + + var services: Array> = arrayOf( + GithubProvider, + GitlabProvider, + GiteaProvider + ) + + var strictMode: Boolean = false + + var logDebug: (String) -> Unit = {} // TODO: implement a more robust log handler + check BuildConfig.DEBUG before logging + var logError: (String) -> Unit = { println(it) } + + var jsonSerializer = Json(JsonConfiguration.Stable.copy(ignoreUnknownKeys = true)) + + var cache : Cache = MemoryCache() + + var platformConfig : PlatformConfig = PlatformConfig() + + fun platform(block: PlatformConfig.() -> Unit) { + platformConfig.apply { block() } + } + +} + +expect class PlatformConfig() \ No newline at end of file diff --git a/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/gitea/GiteaProvider.kt b/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitea/GiteaProvider.kt similarity index 80% rename from gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/gitea/GiteaProvider.kt rename to gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitea/GiteaProvider.kt index 1963060..22c9a9a 100644 --- a/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/gitea/GiteaProvider.kt +++ b/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitea/GiteaProvider.kt @@ -1,4 +1,4 @@ -package me.jfenn.gitrest.impl.gitea +package me.jfenn.gitrest.provider.gitea import io.ktor.client.HttpClient import io.ktor.client.features.defaultRequest @@ -7,8 +7,9 @@ import io.ktor.client.request.header import io.ktor.http.URLProtocol import me.jfenn.gitrest.base.RequestProvider import me.jfenn.gitrest.base.ServiceBuilder -import me.jfenn.gitrest.impl.gitea.model.GiteaRepo -import me.jfenn.gitrest.impl.gitea.model.GiteaUser +import me.jfenn.gitrest.model.GitrestConfig +import me.jfenn.gitrest.provider.gitea.model.GiteaRepo +import me.jfenn.gitrest.provider.gitea.model.GiteaUser import me.jfenn.gitrest.model.License class GiteaProvider( @@ -27,8 +28,8 @@ class GiteaProvider( override val key = "gitea" override val tokens: MutableMap = HashMap() - override fun create(hostname: String): GiteaProvider { - val client = ktor { + override fun create(config: GitrestConfig, hostname: String): GiteaProvider { + val client = config.ktor { defaultRequest { url { protocol = URLProtocol.HTTPS diff --git a/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/gitea/model/GiteaLicense.kt b/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitea/model/GiteaLicense.kt similarity index 64% rename from gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/gitea/model/GiteaLicense.kt rename to gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitea/model/GiteaLicense.kt index 52dc466..6a5a2e2 100644 --- a/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/gitea/model/GiteaLicense.kt +++ b/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitea/model/GiteaLicense.kt @@ -1,3 +1,3 @@ -package me.jfenn.gitrest.impl.gitea.model +package me.jfenn.gitrest.provider.gitea.model // TODO: placeholder file (not compiled) - Gitea has no "license" implementation diff --git a/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/gitea/model/GiteaRepo.kt b/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitea/model/GiteaRepo.kt similarity index 94% rename from gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/gitea/model/GiteaRepo.kt rename to gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitea/model/GiteaRepo.kt index dc73c69..611c326 100644 --- a/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/gitea/model/GiteaRepo.kt +++ b/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitea/model/GiteaRepo.kt @@ -1,4 +1,4 @@ -package me.jfenn.gitrest.impl.gitea.model +package me.jfenn.gitrest.provider.gitea.model import me.jfenn.gitrest.model.Repo import kotlinx.serialization.SerialName diff --git a/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/gitea/model/GiteaUser.kt b/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitea/model/GiteaUser.kt similarity index 94% rename from gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/gitea/model/GiteaUser.kt rename to gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitea/model/GiteaUser.kt index cd411d1..346eba3 100644 --- a/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/gitea/model/GiteaUser.kt +++ b/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitea/model/GiteaUser.kt @@ -1,4 +1,4 @@ -package me.jfenn.gitrest.impl.gitea.model +package me.jfenn.gitrest.provider.gitea.model import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable diff --git a/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/github/GithubProvider.kt b/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/github/GithubProvider.kt similarity index 75% rename from gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/github/GithubProvider.kt rename to gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/github/GithubProvider.kt index 69523b9..f63eca4 100644 --- a/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/github/GithubProvider.kt +++ b/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/github/GithubProvider.kt @@ -1,4 +1,4 @@ -package me.jfenn.gitrest.impl.github +package me.jfenn.gitrest.provider.github import io.ktor.client.HttpClient import io.ktor.client.features.defaultRequest @@ -7,9 +7,11 @@ import io.ktor.client.request.header import io.ktor.http.URLProtocol import me.jfenn.gitrest.base.RequestProvider import me.jfenn.gitrest.base.ServiceBuilder -import me.jfenn.gitrest.impl.github.model.GithubLicense -import me.jfenn.gitrest.impl.github.model.GithubRepo -import me.jfenn.gitrest.impl.github.model.GithubUser +import me.jfenn.gitrest.model.GitrestConfig +import me.jfenn.gitrest.provider.gitea.model.GiteaUser +import me.jfenn.gitrest.provider.github.model.GithubLicense +import me.jfenn.gitrest.provider.github.model.GithubRepo +import me.jfenn.gitrest.provider.github.model.GithubUser class GithubProvider( val client: HttpClient = HttpClient() @@ -27,8 +29,8 @@ class GithubProvider( override val key = "github" override val tokens: MutableMap = HashMap() - override fun create(hostname: String): GithubProvider { - val client = ktor { + override fun create(config: GitrestConfig, hostname: String): GithubProvider { + val client = config.ktor { defaultRequest { url { protocol = URLProtocol.HTTPS diff --git a/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/github/model/GithubLicense.kt b/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/github/model/GithubLicense.kt similarity index 94% rename from gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/github/model/GithubLicense.kt rename to gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/github/model/GithubLicense.kt index cbbd851..9a0bd51 100644 --- a/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/github/model/GithubLicense.kt +++ b/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/github/model/GithubLicense.kt @@ -1,4 +1,4 @@ -package me.jfenn.gitrest.impl.github.model +package me.jfenn.gitrest.provider.github.model import me.jfenn.gitrest.model.License import kotlinx.serialization.SerialName diff --git a/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/github/model/GithubRepo.kt b/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/github/model/GithubRepo.kt similarity index 94% rename from gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/github/model/GithubRepo.kt rename to gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/github/model/GithubRepo.kt index bd5a603..40318c3 100644 --- a/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/github/model/GithubRepo.kt +++ b/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/github/model/GithubRepo.kt @@ -1,4 +1,4 @@ -package me.jfenn.gitrest.impl.github.model +package me.jfenn.gitrest.provider.github.model import me.jfenn.gitrest.model.Repo import kotlinx.serialization.SerialName diff --git a/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/github/model/GithubUser.kt b/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/github/model/GithubUser.kt similarity index 95% rename from gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/github/model/GithubUser.kt rename to gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/github/model/GithubUser.kt index ce86f85..13cf42b 100644 --- a/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/github/model/GithubUser.kt +++ b/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/github/model/GithubUser.kt @@ -1,4 +1,4 @@ -package me.jfenn.gitrest.impl.github.model +package me.jfenn.gitrest.provider.github.model import me.jfenn.gitrest.model.User import kotlinx.serialization.SerialName diff --git a/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/gitlab/GitlabProvider.kt b/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitlab/GitlabProvider.kt similarity index 79% rename from gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/gitlab/GitlabProvider.kt rename to gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitlab/GitlabProvider.kt index c835c89..2122fa5 100644 --- a/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/gitlab/GitlabProvider.kt +++ b/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitlab/GitlabProvider.kt @@ -1,4 +1,4 @@ -package me.jfenn.gitrest.impl.gitlab +package me.jfenn.gitrest.provider.gitlab import io.ktor.client.HttpClient import io.ktor.client.features.defaultRequest @@ -7,9 +7,10 @@ import io.ktor.client.request.header import io.ktor.http.URLProtocol import me.jfenn.gitrest.base.RequestProvider import me.jfenn.gitrest.base.ServiceBuilder -import me.jfenn.gitrest.impl.gitlab.model.GitlabLicense -import me.jfenn.gitrest.impl.gitlab.model.GitlabRepo -import me.jfenn.gitrest.impl.gitlab.model.GitlabUser +import me.jfenn.gitrest.model.GitrestConfig +import me.jfenn.gitrest.provider.gitlab.model.GitlabLicense +import me.jfenn.gitrest.provider.gitlab.model.GitlabRepo +import me.jfenn.gitrest.provider.gitlab.model.GitlabUser class GitlabProvider( val client: HttpClient @@ -31,8 +32,8 @@ class GitlabProvider( override val key = "gitlab" override val tokens: MutableMap = HashMap() - override fun create(hostname: String): GitlabProvider { - val client = ktor { + override fun create(config: GitrestConfig, hostname: String): GitlabProvider { + val client = config.ktor { defaultRequest { url { protocol = URLProtocol.HTTPS diff --git a/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/gitlab/model/GitlabLicense.kt b/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitlab/model/GitlabLicense.kt similarity index 94% rename from gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/gitlab/model/GitlabLicense.kt rename to gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitlab/model/GitlabLicense.kt index 338f6d2..f167da6 100644 --- a/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/gitlab/model/GitlabLicense.kt +++ b/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitlab/model/GitlabLicense.kt @@ -1,4 +1,4 @@ -package me.jfenn.gitrest.impl.gitlab.model +package me.jfenn.gitrest.provider.gitlab.model import me.jfenn.gitrest.model.License import kotlinx.serialization.SerialName diff --git a/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/gitlab/model/GitlabRepo.kt b/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitlab/model/GitlabRepo.kt similarity index 93% rename from gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/gitlab/model/GitlabRepo.kt rename to gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitlab/model/GitlabRepo.kt index 97814c8..204b26c 100644 --- a/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/gitlab/model/GitlabRepo.kt +++ b/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitlab/model/GitlabRepo.kt @@ -1,4 +1,4 @@ -package me.jfenn.gitrest.impl.gitlab.model +package me.jfenn.gitrest.provider.gitlab.model import me.jfenn.gitrest.model.Repo import kotlinx.serialization.SerialName diff --git a/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/gitlab/model/GitlabUser.kt b/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitlab/model/GitlabUser.kt similarity index 95% rename from gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/gitlab/model/GitlabUser.kt rename to gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitlab/model/GitlabUser.kt index 51e1ee9..991d46a 100644 --- a/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/gitlab/model/GitlabUser.kt +++ b/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitlab/model/GitlabUser.kt @@ -1,4 +1,4 @@ -package me.jfenn.gitrest.impl.gitlab.model +package me.jfenn.gitrest.provider.gitlab.model import me.jfenn.gitrest.model.User import kotlinx.serialization.SerialName diff --git a/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/service/Cache.kt b/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/service/Cache.kt new file mode 100644 index 0000000..5ddecdd --- /dev/null +++ b/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/service/Cache.kt @@ -0,0 +1,9 @@ +package me.jfenn.gitrest.service + +interface Cache { + + fun set(key: String, value: Any) + + fun get(id: String) : T? + +} \ No newline at end of file diff --git a/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/service/MemoryCache.kt b/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/service/MemoryCache.kt new file mode 100644 index 0000000..cf35234 --- /dev/null +++ b/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/service/MemoryCache.kt @@ -0,0 +1,3 @@ +package me.jfenn.gitrest.service + +expect class MemoryCache(underlyingCache : Cache = NoCache) : Cache \ No newline at end of file diff --git a/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/service/NoCache.kt b/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/service/NoCache.kt new file mode 100644 index 0000000..e512d61 --- /dev/null +++ b/gitrest/src/commonMain/kotlin/me/jfenn/gitrest/service/NoCache.kt @@ -0,0 +1,8 @@ +package me.jfenn.gitrest.service + +import me.jfenn.gitrest.model.DelegateResource + +object NoCache : Cache { + override fun set(key: String, value: Any) {} + override fun get(id: String): T? = null +} \ No newline at end of file diff --git a/gitrest/src/jsMain/kotlin/RequestProvider.kt b/gitrest/src/jsMain/kotlin/RequestProvider.kt index d699710..938ff23 100644 --- a/gitrest/src/jsMain/kotlin/RequestProvider.kt +++ b/gitrest/src/jsMain/kotlin/RequestProvider.kt @@ -1,46 +1,48 @@ -import me.jfenn.gitrest.RequestProviderDelegate import me.jfenn.gitrest.base.ServiceBuilder -import me.jfenn.gitrest.impl.gitea.GiteaProvider -import me.jfenn.gitrest.impl.github.GithubProvider -import me.jfenn.gitrest.impl.gitlab.GitlabProvider +import me.jfenn.gitrest.provider.gitea.GiteaProvider +import me.jfenn.gitrest.provider.github.GithubProvider +import me.jfenn.gitrest.provider.gitlab.GitlabProvider import gitrest.js.License import gitrest.js.Repo import gitrest.js.User import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.promise +import me.jfenn.gitrest.gitrest import kotlin.js.Promise /** * NodeJS-usable wrapper class to convert functions/arguments into their JS-compatible versions */ class RequestProvider( - providers: Array> = arrayOf( + services: Array> = arrayOf( GithubProvider, GiteaProvider, GitlabProvider ) ) { - val delegate = RequestProviderDelegate(providers) + val client = gitrest { + this.services = services + } @JsName("getUser") fun getUser(str: String): Promise = GlobalScope.promise { - delegate.getUser(str)?.let { User(it) } + client.getUser(str)?.let { User(it) } } @JsName("getRepo") fun getRepo(str: String): Promise = GlobalScope.promise { - delegate.getRepo(str)?.let { Repo(it) } + client.getRepo(str)?.let { Repo(it) } } @JsName("getRepoContributors") fun getRepoContributors(str: String): Promise?> = GlobalScope.promise { - delegate.getRepoContributors(str)?.map { User(it) }?.toTypedArray() + client.getRepoContributors(str)?.map { User(it) }?.toTypedArray() } @JsName("getLicense") fun getLicense(str: String): Promise = GlobalScope.promise { - delegate.getLicense(str)?.let { License(it) } + client.getLicense(str)?.let { License(it) } } } \ No newline at end of file diff --git a/gitrest/src/jsMain/kotlin/me/jfenn/gitrest/model/PlatformConfig.kt b/gitrest/src/jsMain/kotlin/me/jfenn/gitrest/model/PlatformConfig.kt new file mode 100644 index 0000000..d38772c --- /dev/null +++ b/gitrest/src/jsMain/kotlin/me/jfenn/gitrest/model/PlatformConfig.kt @@ -0,0 +1,3 @@ +package me.jfenn.gitrest.model + +actual class PlatformConfig {} diff --git a/gitrest/src/jsMain/kotlin/me/jfenn/gitrest/service/MemoryCache.kt b/gitrest/src/jsMain/kotlin/me/jfenn/gitrest/service/MemoryCache.kt new file mode 100644 index 0000000..32e2a10 --- /dev/null +++ b/gitrest/src/jsMain/kotlin/me/jfenn/gitrest/service/MemoryCache.kt @@ -0,0 +1,23 @@ +package me.jfenn.gitrest.service + +import me.jfenn.gitrest.model.DelegateResource + +/** + * Naive caching implementation for JS. + */ +actual class MemoryCache actual constructor( + val underlyingCache: Cache +) : Cache { + + private val map = HashMap() + + override fun set(key: String, value: Any) { + map[key] = value + underlyingCache.set(key, value) + } + + override fun get(key: String): T? { + return (map[key] ?: underlyingCache.get(key)) as? T? + } + +} \ No newline at end of file diff --git a/gitrest/src/jvmMain/kotlin/me/jfenn/gitrest/model/PlatformConfig.kt b/gitrest/src/jvmMain/kotlin/me/jfenn/gitrest/model/PlatformConfig.kt new file mode 100644 index 0000000..d38772c --- /dev/null +++ b/gitrest/src/jvmMain/kotlin/me/jfenn/gitrest/model/PlatformConfig.kt @@ -0,0 +1,3 @@ +package me.jfenn.gitrest.model + +actual class PlatformConfig {} diff --git a/gitrest/src/jvmMain/kotlin/me/jfenn/gitrest/service/MemoryCache.kt b/gitrest/src/jvmMain/kotlin/me/jfenn/gitrest/service/MemoryCache.kt new file mode 100644 index 0000000..e04eb1b --- /dev/null +++ b/gitrest/src/jvmMain/kotlin/me/jfenn/gitrest/service/MemoryCache.kt @@ -0,0 +1,49 @@ +package me.jfenn.gitrest.service + +import me.jfenn.gitrest.model.DelegateResource +import java.lang.ref.ReferenceQueue +import java.lang.ref.WeakReference + +/** + * Naive caching implementation for the JVM that uses WeakReference + * as a cache "buffer" that can be cleaned up by the GC when necessary. + */ +actual class MemoryCache actual constructor( + val underlyingCache: Cache +) : Cache { + + private class WeakEntry internal constructor( + internal val key: String, + value: Any, + referenceQueue: ReferenceQueue) : WeakReference(value, referenceQueue) + + private val referenceQueue = ReferenceQueue() + private val map = HashMap() + + override fun set(key: String, value: Any) { + // remove any invalid references + var weakEntry = referenceQueue.poll() as WeakEntry? + while (weakEntry != null) { + map.remove(weakEntry.key) + weakEntry = referenceQueue.poll() as WeakEntry? + } + + map[key] = WeakEntry(key, value, referenceQueue) + underlyingCache.set(key, value) + } + + override fun get(key: String): T? { + val weakEntry = map[key] + weakEntry?.get()?.let { + return it as? T + } + + underlyingCache.get(key)?.let { + set(key, it as Any) + return it + } + + return null + } + +} -- GitLab