Browse Source

implement memory / disk cache - fix #4

main
James Fenn 7 months ago
parent
commit
3a2baebfd5
30 changed files with 343 additions and 77 deletions
  1. +10
    -4
      example-android/src/main/java/me/jfenn/gitrest/example/android/MainActivity.kt
  2. +5
    -12
      example-jvm/src/main/java/me/jfenn/gitrest/example/jvm/Main.kt
  3. +9
    -10
      example-kotlinbrowser/src/main/kotlin/Main.kt
  4. +0
    -1
      gitrest/src/androidMain/kotlin/Stub.kt
  5. +3
    -0
      gitrest/src/androidMain/kotlin/me/jfenn/gitrest/model/PlatformConfig.kt
  6. +69
    -0
      gitrest/src/androidMain/kotlin/me/jfenn/gitrest/service/DiskCache.kt
  7. +49
    -0
      gitrest/src/androidMain/kotlin/me/jfenn/gitrest/service/MemoryCache.kt
  8. +17
    -11
      gitrest/src/commonMain/kotlin/me/jfenn/gitrest/GitrestClient.kt
  9. +4
    -3
      gitrest/src/commonMain/kotlin/me/jfenn/gitrest/base/ServiceBuilder.kt
  10. +37
    -0
      gitrest/src/commonMain/kotlin/me/jfenn/gitrest/model/GitrestConfig.kt
  11. +6
    -5
      gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitea/GiteaProvider.kt
  12. +1
    -1
      gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitea/model/GiteaLicense.kt
  13. +1
    -1
      gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitea/model/GiteaRepo.kt
  14. +1
    -1
      gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitea/model/GiteaUser.kt
  15. +8
    -6
      gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/github/GithubProvider.kt
  16. +1
    -1
      gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/github/model/GithubLicense.kt
  17. +1
    -1
      gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/github/model/GithubRepo.kt
  18. +1
    -1
      gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/github/model/GithubUser.kt
  19. +7
    -6
      gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitlab/GitlabProvider.kt
  20. +1
    -1
      gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitlab/model/GitlabLicense.kt
  21. +1
    -1
      gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitlab/model/GitlabRepo.kt
  22. +1
    -1
      gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitlab/model/GitlabUser.kt
  23. +9
    -0
      gitrest/src/commonMain/kotlin/me/jfenn/gitrest/service/Cache.kt
  24. +3
    -0
      gitrest/src/commonMain/kotlin/me/jfenn/gitrest/service/MemoryCache.kt
  25. +8
    -0
      gitrest/src/commonMain/kotlin/me/jfenn/gitrest/service/NoCache.kt
  26. +12
    -10
      gitrest/src/jsMain/kotlin/RequestProvider.kt
  27. +3
    -0
      gitrest/src/jsMain/kotlin/me/jfenn/gitrest/model/PlatformConfig.kt
  28. +23
    -0
      gitrest/src/jsMain/kotlin/me/jfenn/gitrest/service/MemoryCache.kt
  29. +3
    -0
      gitrest/src/jvmMain/kotlin/me/jfenn/gitrest/model/PlatformConfig.kt
  30. +49
    -0
      gitrest/src/jvmMain/kotlin/me/jfenn/gitrest/service/MemoryCache.kt

+ 10
- 4
example-android/src/main/java/me/jfenn/gitrest/example/android/MainActivity.kt View File

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


+ 5
- 12
example-jvm/src/main/java/me/jfenn/gitrest/example/jvm/Main.kt View File

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


+ 9
- 10
example-kotlinbrowser/src/main/kotlin/Main.kt View File

@@ -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 {


+ 0
- 1
gitrest/src/androidMain/kotlin/Stub.kt View File

@@ -1 +0,0 @@
// empty file to ensure that gradle compiles the target

+ 3
- 0
gitrest/src/androidMain/kotlin/me/jfenn/gitrest/model/PlatformConfig.kt View File

@@ -0,0 +1,3 @@
package me.jfenn.gitrest.model

actual class PlatformConfig {}

+ 69
- 0
gitrest/src/androidMain/kotlin/me/jfenn/gitrest/service/DiskCache.kt View File

@@ -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<Any>? {
return if (value is List<*>) {
return (value.firstOrNull()?.let { it::class.serializerOrNull() } ?: String.serializer()).list as KSerializer<Any>
} else {
return value::class.serializerOrNull() as KSerializer<Any>
}
}

@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 <T> 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
}
}

}

+ 49
- 0
gitrest/src/androidMain/kotlin/me/jfenn/gitrest/service/MemoryCache.kt View File

@@ -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<Any>) : WeakReference<Any>(value, referenceQueue)

private val referenceQueue = ReferenceQueue<Any>()
private val map = HashMap<String, WeakEntry>()

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 <T> get(key: String): T? {
val weakEntry = map[key]
weakEntry?.get()?.let {
return it as? T
}

underlyingCache.get<T>(key)?.let {
map[key] = WeakEntry(key, it as Any, referenceQueue)
return it
}

return null
}

}

gitrest/src/commonMain/kotlin/me/jfenn/gitrest/RequestProviderDelegate.kt → gitrest/src/commonMain/kotlin/me/jfenn/gitrest/GitrestClient.kt View File

@@ -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<ServiceBuilder<*>> = 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<String, RequestProvider> = HashMap()
@@ -18,9 +17,14 @@ class RequestProviderDelegate(
private suspend fun <T> get(str: String, block: suspend (RequestProvider, String) -> T?) : T? {
val providerStr = ProviderString(str)

config.cache.get<T>(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<DelegateResource>)?.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
}
}

+ 4
- 3
gitrest/src/commonMain/kotlin/me/jfenn/gitrest/base/ServiceBuilder.kt View File

@@ -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<T: RequestProvider> {

val key : String
val tokens : MutableMap<String, String>

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

}

+ 37
- 0
gitrest/src/commonMain/kotlin/me/jfenn/gitrest/model/GitrestConfig.kt View File

@@ -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<ServiceBuilder<*>> = 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()

gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/gitea/GiteaProvider.kt → gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitea/GiteaProvider.kt View File

@@ -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<String, String> = 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

gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/gitea/model/GiteaLicense.kt → gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitea/model/GiteaLicense.kt View File

@@ -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

gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/gitea/model/GiteaRepo.kt → gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitea/model/GiteaRepo.kt View File

@@ -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

gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/gitea/model/GiteaUser.kt → gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitea/model/GiteaUser.kt View File

@@ -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

gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/github/GithubProvider.kt → gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/github/GithubProvider.kt View File

@@ -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<String, String> = 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

gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/github/model/GithubLicense.kt → gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/github/model/GithubLicense.kt View File

@@ -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

gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/github/model/GithubRepo.kt → gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/github/model/GithubRepo.kt View File

@@ -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

gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/github/model/GithubUser.kt → gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/github/model/GithubUser.kt View File

@@ -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

gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/gitlab/GitlabProvider.kt → gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitlab/GitlabProvider.kt View File

@@ -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<String, String> = 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

gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/gitlab/model/GitlabLicense.kt → gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitlab/model/GitlabLicense.kt View File

@@ -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

gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/gitlab/model/GitlabRepo.kt → gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitlab/model/GitlabRepo.kt View File

@@ -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

gitrest/src/commonMain/kotlin/me/jfenn/gitrest/impl/gitlab/model/GitlabUser.kt → gitrest/src/commonMain/kotlin/me/jfenn/gitrest/provider/gitlab/model/GitlabUser.kt View File

@@ -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

+ 9
- 0
gitrest/src/commonMain/kotlin/me/jfenn/gitrest/service/Cache.kt View File

@@ -0,0 +1,9 @@
package me.jfenn.gitrest.service

interface Cache {

fun set(key: String, value: Any)

fun <T> get(id: String) : T?

}

+ 3
- 0
gitrest/src/commonMain/kotlin/me/jfenn/gitrest/service/MemoryCache.kt View File

@@ -0,0 +1,3 @@
package me.jfenn.gitrest.service

expect class MemoryCache(underlyingCache : Cache = NoCache) : Cache

+ 8
- 0
gitrest/src/commonMain/kotlin/me/jfenn/gitrest/service/NoCache.kt View File

@@ -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 <T> get(id: String): T? = null
}

+ 12
- 10
gitrest/src/jsMain/kotlin/RequestProvider.kt View File

@@ -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<ServiceBuilder<*>> = arrayOf(
services: Array<ServiceBuilder<*>> = arrayOf(
GithubProvider,
GiteaProvider,
GitlabProvider
)
) {

val delegate = RequestProviderDelegate(providers)
val client = gitrest {
this.services = services
}

@JsName("getUser")
fun getUser(str: String): Promise<User?> = GlobalScope.promise {
delegate.getUser(str)?.let { User(it) }
client.getUser(str)?.let { User(it) }
}

@JsName("getRepo")
fun getRepo(str: String): Promise<Repo?> = GlobalScope.promise {
delegate.getRepo(str)?.let { Repo(it) }
client.getRepo(str)?.let { Repo(it) }
}

@JsName("getRepoContributors")
fun getRepoContributors(str: String): Promise<Array<User>?> = GlobalScope.promise {
delegate.getRepoContributors(str)?.map { User(it) }?.toTypedArray()
client.getRepoContributors(str)?.map { User(it) }?.toTypedArray()
}

@JsName("getLicense")
fun getLicense(str: String): Promise<License?> = GlobalScope.promise {
delegate.getLicense(str)?.let { License(it) }
client.getLicense(str)?.let { License(it) }
}

}

+ 3
- 0
gitrest/src/jsMain/kotlin/me/jfenn/gitrest/model/PlatformConfig.kt View File

@@ -0,0 +1,3 @@
package me.jfenn.gitrest.model

actual class PlatformConfig {}

+ 23
- 0
gitrest/src/jsMain/kotlin/me/jfenn/gitrest/service/MemoryCache.kt View File

@@ -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<String, Any>()

override fun set(key: String, value: Any) {
map[key] = value
underlyingCache.set(key, value)
}

override fun <T> get(key: String): T? {
return (map[key] ?: underlyingCache.get<T>(key)) as? T?
}

}

+ 3
- 0
gitrest/src/jvmMain/kotlin/me/jfenn/gitrest/model/PlatformConfig.kt View File

@@ -0,0 +1,3 @@
package me.jfenn.gitrest.model

actual class PlatformConfig {}

+ 49
- 0
gitrest/src/jvmMain/kotlin/me/jfenn/gitrest/service/MemoryCache.kt View File

@@ -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<Any>) : WeakReference<Any>(value, referenceQueue)

private val referenceQueue = ReferenceQueue<Any>()
private val map = HashMap<String, WeakEntry>()

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 <T> get(key: String): T? {
val weakEntry = map[key]
weakEntry?.get()?.let {
return it as? T
}

underlyingCache.get<T>(key)?.let {
set(key, it as Any)
return it
}

return null
}

}

Loading…
Cancel
Save