Compare commits

...

78 Commits

Author SHA1 Message Date
a3a48bbaac fixes in file key value repo 2022-07-02 02:28:43 +06:00
5e716fb9a8 start 0.11.12 2022-07-02 02:26:58 +06:00
11a36153cc Merge pull request #174 from InsanusMokrassar/0.11.11
0.11.11: improvements of cache repos
2022-06-30 14:03:29 +06:00
8bee354f04 improvements of cache repos 2022-06-30 13:59:32 +06:00
f7dd2b5ce7 Merge pull request #173 from InsanusMokrassar/0.11.11
0.11.11
2022-06-30 13:41:43 +06:00
8ca10c00bb update dokka workflow 2022-06-30 13:41:12 +06:00
905c7e8eda deprecate hmacSha256 2022-06-30 13:19:37 +06:00
d4c5e849bf deprecate BodyPair 2022-06-30 13:15:19 +06:00
8250a2a021 start 0.11.11 2022-06-30 13:14:06 +06:00
01b3df7b8c Update github_release.gradle 2022-06-30 10:12:54 +06:00
daa6e4aff5 Merge pull request #172 from InsanusMokrassar/0.11.10
0.11.10
2022-06-30 02:56:14 +06:00
23bcb26a58 complete improvements in caches 2022-06-30 02:44:44 +06:00
e55f60c30b rename unlimited kv cache 2022-06-30 00:22:07 +06:00
0d0c16e16d add full cache repos 2022-06-29 23:53:49 +06:00
540d5cce7c start add full repos caches 2022-06-29 19:43:58 +06:00
a548b00979 update repos cache 2022-06-29 19:31:57 +06:00
4b7ca6d565 start 0.11.10 2022-06-29 19:29:38 +06:00
0473fa238c Merge pull request #171 from InsanusMokrassar/0.11.9
0.11.9
2022-06-29 01:56:03 +06:00
cfc7119697 update dependencies 2022-06-28 23:06:09 +06:00
22a6520d3e start 0.11.9 2022-06-28 23:01:32 +06:00
fb25e91191 start 0.12.0 2022-06-28 22:57:21 +06:00
c116b270b6 Merge pull request #170 from InsanusMokrassar/0.11.8
0.11.8
2022-06-28 14:18:11 +06:00
aa2d598689 fixes in FileKeyValueRepo 2022-06-28 14:13:04 +06:00
5ef3bb746b start 0.11.8 2022-06-28 14:12:12 +06:00
037616e271 Merge pull request #169 from InsanusMokrassar/0.11.7
0.11.7
2022-06-28 03:07:54 +06:00
abbea906f1 fixes 2022-06-28 01:46:24 +06:00
9132e216c9 add several extensions for MapperRepo 2022-06-28 01:43:57 +06:00
12a7e3c4af fixes 2022-06-28 01:06:46 +06:00
b40c093917 SimpleMapper, SimpleSuspendableMapper, mappers for CRUDRepo 2022-06-28 00:56:51 +06:00
7ac12455c8 add ReadCRUDRepo mappers 2022-06-28 00:19:25 +06:00
5043eec7a2 start 0.11.7 2022-06-27 23:35:10 +06:00
cf31f53e01 Merge pull request #168 from InsanusMokrassar/0.11.6
0.11.6
2022-06-24 00:19:34 +06:00
cd22d76fa7 fsms fixes 2022-06-23 18:47:59 +06:00
c8759843f7 start 0.11.6 2022-06-23 17:19:07 +06:00
781bbcc012 Merge pull request #167 from InsanusMokrassar/0.11.5
0.11.5
2022-06-23 12:50:31 +06:00
6da29c0686 add note about 0.11.4 2022-06-23 12:49:09 +06:00
fcdb6fc45a start and finish 0.11.5 -.- 2022-06-23 12:48:24 +06:00
e785a99bd7 Merge pull request #166 from InsanusMokrassar/0.11.4
0.11.4
2022-06-22 22:37:30 +06:00
121e513fdd fixes in tempUpload of js part 2022-06-22 22:36:06 +06:00
9cf01ab54f improve compose list state creation 2022-06-22 16:56:51 +06:00
c655107681 renames in compose states creation 2022-06-22 16:52:19 +06:00
91a5af6a9a fix of changelog 2022-06-22 16:10:58 +06:00
86e74c0a6f add extension toMutableListState 2022-06-22 16:09:52 +06:00
d8f01f21a0 start 0.11.4 2022-06-22 16:09:35 +06:00
9fb8626d8c Rewrite crud.yml 2022-06-16 15:51:22 +06:00
1c52e04cdb Merge pull request #164 from InsanusMokrassar/0.11.3
0.11.3
2022-06-14 23:59:49 +06:00
798128256e fixes in websockets 2022-06-14 23:48:10 +06:00
72c2df47fd start 0.11.3 2022-06-14 19:35:17 +06:00
c9c6d4c0c1 Merge pull request #163 from InsanusMokrassar/0.11.2
0.11.2
2022-06-13 23:21:14 +06:00
2f4f9f3003 fixes 2022-06-13 23:20:25 +06:00
22e8f8e5d6 start 0.11.2 2022-06-13 22:19:33 +06:00
04cf8c3d9a Merge pull request #162 from InsanusMokrassar/0.11.1
0.11.1
2022-06-12 16:32:05 +06:00
52198be543 fixes in tempupload 2022-06-12 14:26:41 +06:00
80fd5a489b add pagination type info in crud read repo client 2022-06-11 22:37:32 +06:00
b187043ee1 fixes in kv and kvs of ktor repos 2022-06-10 22:25:16 +06:00
8965752055 start 0.11.1 2022-06-10 22:23:45 +06:00
b796620267 Merge pull request #161 from InsanusMokrassar/0.11.0
0.11.0
2022-06-05 02:54:05 +06:00
62df81bb4e update uuid and partially fill changelog 2022-06-05 00:34:45 +06:00
0a8e0f6178 complete swagger templates 2022-06-04 17:12:05 +06:00
f705020aaa fixes in packages 2022-06-04 16:55:23 +06:00
78bd3b9853 replaces of new API of ktor 2022-06-04 16:52:01 +06:00
992b283597 add kv swagger template 2022-06-04 16:47:22 +06:00
8fc1ff1d59 fixes and filling of cru.yml for swagger 2022-06-04 16:18:48 +06:00
3bbde61f39 renames in crud repos and some little refactoring 2022-06-04 15:15:17 +06:00
8d955c4b9d renames in kevalues repos 2022-06-04 15:00:23 +06:00
18593c530b renames in key value repos 2022-06-04 14:42:16 +06:00
78903cd4eb complete improvements in repos ktor parts 2022-06-04 00:22:36 +06:00
eaa143f7d7 kv rework, fixes in map keyvalue repo, tests 2022-06-03 22:33:26 +06:00
bcb0e42fa2 add new started clients for crud 2022-06-03 17:52:58 +06:00
8eed435302 start improvements in ktor parts 2022-06-03 12:22:51 +06:00
0e4a63057f start 0.11.0 2022-06-02 23:50:06 +06:00
1af5faa440 Merge pull request #159 from InsanusMokrassar/0.10.8
0.10.8
2022-05-29 20:06:14 +06:00
7412217b0c add Element.isOverflow 2022-05-29 10:22:57 +06:00
3e749d75b7 start 0.10.8 2022-05-29 10:19:39 +06:00
846b5c87c9 Merge pull request #158 from InsanusMokrassar/0.10.7
0.10.7
2022-05-28 23:59:54 +06:00
7b5e84f80b doForAll and getForAll not suspend 2022-05-28 22:47:06 +06:00
27822a8a66 start 0.10.7 2022-05-28 18:48:51 +06:00
36f4e7ec37 Merge pull request #157 from InsanusMokrassar/0.10.6
0.10.6
2022-05-28 09:47:10 +06:00
119 changed files with 4104 additions and 421 deletions

View File

@@ -15,7 +15,7 @@ jobs:
continue-on-error: true
run: cd /usr/local/lib/android/sdk/build-tools/32.0.0/ && mv d8 dx && cd lib && mv d8.jar dx.jar
- name: Build
run: ./gradlew dokkaHtml
run: ./gradlew build && ./gradlew dokkaHtml
- name: Publish KDocs
uses: peaceiris/actions-gh-pages@v3
with:

View File

@@ -1,5 +1,116 @@
# Changelog
## 0.11.12
* `Repos`:
* `Common`:
* `JVM`:
* Fixes in `ReadFileKeyValueRepo` methods (`values`/`keys`)
## 0.11.11
* `Crypto`:
* `hmacSha256` has been deprecated
* `Ktor`:
* `Client`:
* `BodyPair` has been deprecated
* `Repos`:
* `Cache`:
* New interface `CacheRepo`
* New interface `FullCacheRepo`
* `actualize*` methods inside of full cache repos now open for overriding
## 0.11.10
* `Repos`:
* `Cache`:
* `KVCache` has been replaced to the package `dev.inmo.micro_utils.repos.cache`
* `SimpleKVCache` has been replaced to the package `dev.inmo.micro_utils.repos.cache`
* New `KVCache` subtype - `FullKVCache`
* Add `Full*` variants of standard repos
* Add `cached`/`caching` (for write repos) extensions for all standard types of repos
## 0.11.9
* `Versions`
* `Coroutines`: `1.6.1` -> `1.6.3`
* `Ktor`: `2.0.2` -> `2.0.3`
* `Compose`: `1.2.0-alpha01-dev686` -> `1.2.0-alpha01-dev729`
## 0.11.8
* `Repos`:
* `Common`:
* Fixes in `FileKeyValueRepo`
## 0.11.7
* `Common`:
* New abstractions `SimpleMapper` and `SimpleSuspendableMapper`
* `Repos`:
* `Common`:
* Add mappers for `CRUDRepo`
## 0.11.6
* `FSM`:
* `Common`
* Several fixes related to the jobs handling
## 0.11.5
* `Coroutines`:
* `Compose`:
* Add extension `StateFlow#asMutableComposeListState` and `StateFlow#asComposeList`
* Add extension `StateFlow#asMutableComposeState`/`StateFlow#asComposeState`
## 0.11.4
**THIS VERSION HAS BEEN BROKEN, DO NOT USE IT**
## 0.11.3
* `Ktor`:
* Support of `WebSockets` has been improved
* `Client`:
* New extensions: `HttpClient#openBaseWebSocketFlow`, `HttpClient#openWebSocketFlow`, `HttpClient#openSecureWebSocketFlow`
## 0.11.2
* `Ktor`:
* Support of `WebSockets` has been improved and added fixes inside of clients
## 0.11.1
* `Repos`
* `Ktor`
* In `configureReadKeyValueRepoRoutes` and `configureReadKeyValuesRepoRoutes` configurators fixed requiring of `reversed` property
## 0.11.0
* `Versions`
* `UUID`: `0.4.0` -> `0.4.1`
* `Ktor`
* `Client`:
* New extension fun `HttpResponse#throwOnUnsuccess`
* All old functions, classes and extensions has been rewritten with new ktor-way with types info and keeping `ContentNegotiation` in mind
* `Server`:
* All old functions, classes and extensions has been rewritten with new ktor-way with types info and keeping `ContentNegotiation` in mind
* `Repos`
* `Ktor`:
* Fully rewritten work with all declared repositories
* All old functions, classes and extensions has been rewritten with new ktor-way with types info and keeping `ContentNegotiation` in mind
## 0.10.8
* `Common`
* Add `Element.isOverflow*` extension properties
## 0.10.7
* `Pagination`:
* Now it is possible to use `doForAll*` and `getForAll` functions in non suspend places
## 0.10.6
* `Versions`

View File

@@ -0,0 +1,53 @@
package dev.inmo.micro_utils.common
import kotlin.jvm.JvmName
interface SimpleMapper<T1, T2> {
fun convertToT1(from: T2): T1
fun convertToT2(from: T1): T2
}
@JvmName("convertFromT2")
fun <T1, T2> SimpleMapper<T1, T2>.convert(from: T2) = convertToT1(from)
@JvmName("convertFromT1")
fun <T1, T2> SimpleMapper<T1, T2>.convert(from: T1) = convertToT2(from)
class SimpleMapperImpl<T1, T2>(
private val t1: (T2) -> T1,
private val t2: (T1) -> T2,
) : SimpleMapper<T1, T2> {
override fun convertToT1(from: T2): T1 = t1.invoke(from)
override fun convertToT2(from: T1): T2 = t2.invoke(from)
}
@Suppress("NOTHING_TO_INLINE")
inline fun <T1, T2> simpleMapper(
noinline t1: (T2) -> T1,
noinline t2: (T1) -> T2,
) = SimpleMapperImpl(t1, t2)
interface SimpleSuspendableMapper<T1, T2> {
suspend fun convertToT1(from: T2): T1
suspend fun convertToT2(from: T1): T2
}
@JvmName("convertFromT2")
suspend fun <T1, T2> SimpleSuspendableMapper<T1, T2>.convert(from: T2) = convertToT1(from)
@JvmName("convertFromT1")
suspend fun <T1, T2> SimpleSuspendableMapper<T1, T2>.convert(from: T1) = convertToT2(from)
class SimpleSuspendableMapperImpl<T1, T2>(
private val t1: suspend (T2) -> T1,
private val t2: suspend (T1) -> T2,
) : SimpleSuspendableMapper<T1, T2> {
override suspend fun convertToT1(from: T2): T1 = t1.invoke(from)
override suspend fun convertToT2(from: T1): T2 = t2.invoke(from)
}
@Suppress("NOTHING_TO_INLINE")
inline fun <T1, T2> simpleSuspendableMapper(
noinline t1: suspend (T2) -> T1,
noinline t2: suspend (T1) -> T2,
) = SimpleSuspendableMapperImpl(t1, t2)

View File

@@ -41,6 +41,13 @@ data class Optional<T> internal constructor(
inline val <T> T.optional
get() = Optional.presented(this)
inline val <T : Any> T?.optionalOrAbsentIfNull
get() = if (this == null) {
Optional.absent<T>()
} else {
Optional.presented(this)
}
/**
* Will call [block] when data presented ([Optional.dataPresented] == true)
*/

View File

@@ -0,0 +1,12 @@
package dev.inmo.micro_utils.common
import org.w3c.dom.Element
inline val Element.isOverflowWidth
get() = scrollWidth > clientWidth
inline val Element.isOverflowHeight
get() = scrollHeight > clientHeight
inline val Element.isOverflow
get() = isOverflowHeight || isOverflowWidth

View File

@@ -0,0 +1,26 @@
package dev.inmo.micro_utils.coroutines.compose
import androidx.compose.runtime.*
import androidx.compose.runtime.snapshots.SnapshotStateList
import dev.inmo.micro_utils.common.applyDiff
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
@Suppress("NOTHING_TO_INLINE")
inline fun <reified T> Flow<List<T>>.asMutableComposeListState(
scope: CoroutineScope
): SnapshotStateList<T> {
val state = mutableStateListOf<T>()
subscribeSafelyWithoutExceptions(scope) {
state.applyDiff(it)
}
return state
}
@Suppress("NOTHING_TO_INLINE")
inline fun <reified T> Flow<List<T>>.asComposeList(
scope: CoroutineScope
): List<T> = asMutableComposeListState(scope)

View File

@@ -0,0 +1,35 @@
package dev.inmo.micro_utils.coroutines.compose
import androidx.compose.runtime.*
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
fun <T> Flow<T>.asMutableComposeState(
initial: T,
scope: CoroutineScope
): MutableState<T> {
val state = mutableStateOf(initial)
subscribeSafelyWithoutExceptions(scope) { state.value = it }
return state
}
@Suppress("NOTHING_TO_INLINE")
inline fun <T> StateFlow<T>.asMutableComposeState(
scope: CoroutineScope
): MutableState<T> = asMutableComposeState(value, scope)
fun <T> Flow<T>.asComposeState(
initial: T,
scope: CoroutineScope
): State<T> {
val state = asMutableComposeState(initial, scope)
return derivedStateOf { state.value }
}
@Suppress("NOTHING_TO_INLINE")
inline fun <T> StateFlow<T>.asComposeState(
scope: CoroutineScope
): State<T> = asComposeState(value, scope)

View File

@@ -1,3 +1,4 @@
package dev.inmo.micro_utils.crypto
@Deprecated("Deprecated due to incorrect of work sometimes and redundancy. Can be replaced by korlibs krypto")
expect fun SourceString.hmacSha256(key: String): String

View File

@@ -8,6 +8,7 @@ external interface CryptoJs {
@JsNonModule
external val CryptoJS: CryptoJs
@Deprecated("Deprecated due to incorrect of work sometimes and redundancy. Can be replaced by korlibs krypto")
actual fun SourceString.hmacSha256(key: String): String {
return CryptoJS.asDynamic().HmacSHA256(this, key).toString().unsafeCast<String>()
}

View File

@@ -3,6 +3,7 @@ package dev.inmo.micro_utils.crypto
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
@Deprecated("Deprecated due to incorrect of work sometimes and redundancy. Can be replaced by korlibs krypto")
actual fun SourceString.hmacSha256(key: String): String {
val mac = Mac.getInstance("HmacSHA256")

View File

@@ -6,6 +6,7 @@ import dev.inmo.micro_utils.coroutines.*
import dev.inmo.micro_utils.fsm.common.utils.StateHandlingErrorHandler
import dev.inmo.micro_utils.fsm.common.utils.defaultStateHandlingErrorHandler
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
@@ -85,10 +86,10 @@ open class DefaultStatesMachine <T: State>(
protected open suspend fun performUpdate(state: T) {
val newState = launchStateHandling(state, handlers)
if (newState != null) {
statesManager.update(state, newState)
} else {
if (newState == null) {
statesManager.endChain(state)
} else {
statesManager.update(state, newState)
}
}
@@ -118,7 +119,7 @@ open class DefaultStatesMachine <T: State>(
* [StatesManager.endChain].
*/
override fun start(scope: CoroutineScope): Job = scope.launchSafelyWithoutExceptions {
statesManager.onStartChain.subscribeSafelyWithoutExceptions(this) {
(statesManager.getActiveStates().asFlow() + statesManager.onStartChain).subscribeSafelyWithoutExceptions(this) {
launch { performStateUpdate(Optional.absent(), it, scope.LinkedSupervisorScope()) }
}
statesManager.onChainStateUpdated.subscribeSafelyWithoutExceptions(this) {
@@ -134,10 +135,6 @@ open class DefaultStatesMachine <T: State>(
}
}
}
statesManager.getActiveStates().forEach {
launch { performStateUpdate(Optional.absent(), it, scope.LinkedSupervisorScope()) }
}
}
/**

View File

@@ -52,6 +52,7 @@ open class DefaultUpdatableStatesMachine<T : State>(
statesJobs.remove(
jobsStates[job] ?: return@withLock
)
jobsStates.remove(job)
}
}
}

View File

@@ -21,7 +21,7 @@ if (new File(projectDir, "secret.gradle").exists()) {
owner "InsanusMokrassar"
repo "MicroUtils"
tagName "${project.version}"
tagName "v${project.version}"
releaseName "${project.version}"
targetCommitish "${project.version}"

View File

@@ -14,5 +14,5 @@ crypto_js_version=4.1.1
# Project data
group=dev.inmo
version=0.10.6
android_code_version=121
version=0.11.12
android_code_version=136

View File

@@ -2,18 +2,18 @@
kt = "1.6.21"
kt-serialization = "1.3.3"
kt-coroutines = "1.6.1"
kt-coroutines = "1.6.3"
jb-compose = "1.2.0-alpha01-dev686"
jb-compose = "1.2.0-alpha01-dev729"
jb-exposed = "0.38.2"
jb-dokka = "1.6.21"
klock = "2.7.0"
uuid = "0.4.0"
uuid = "0.4.1"
ktor = "2.0.2"
ktor = "2.0.3"
gh-release = "2.3.7"
gh-release = "2.4.1"
android-gradle = "7.0.4"
dexcount = "3.1.0"
@@ -37,17 +37,24 @@ kt-serialization-cbor = { module = "org.jetbrains.kotlinx:kotlinx-serialization-
kt-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kt-coroutines" }
kt-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kt-coroutines" }
kt-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kt-coroutines" }
ktor-io = { module = "io.ktor:ktor-io", version.ref = "ktor" }
ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" }
ktor-client = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktor" }
ktor-client-java = { module = "io.ktor:ktor-client-java", version.ref = "ktor" }
ktor-client-websockets = { module = "io.ktor:ktor-client-websockets", version.ref = "ktor" }
ktor-client-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktor" }
ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" }
ktor-server = { module = "io.ktor:ktor-server", version.ref = "ktor" }
ktor-server-cio = { module = "io.ktor:ktor-server-cio", version.ref = "ktor" }
ktor-server-host-common = { module = "io.ktor:ktor-server-host-common", version.ref = "ktor" }
ktor-websockets = { module = "io.ktor:ktor-websockets", version.ref = "ktor" }
ktor-server-websockets = { module = "io.ktor:ktor-server-websockets", version.ref = "ktor" }
ktor-server-statusPages = { module = "io.ktor:ktor-server-status-pages", version.ref = "ktor" }
ktor-server-content-negotiation = { module = "io.ktor:ktor-server-content-negotiation", version.ref = "ktor" }
klock = { module = "com.soywiz.korlibs.klock:klock", version.ref = "klock" }

View File

@@ -0,0 +1,15 @@
package dev.inmo.micro_utils.ktor.client
import io.ktor.client.plugins.ClientRequestException
import io.ktor.client.statement.HttpResponse
import io.ktor.http.isSuccess
inline fun HttpResponse.throwOnUnsuccess(
unsuccessMessage: () -> String
) {
if (status.isSuccess()) {
return
}
throw ClientRequestException(this, unsuccessMessage())
}

View File

@@ -0,0 +1,103 @@
package dev.inmo.micro_utils.ktor.client
import dev.inmo.micro_utils.common.Warning
import dev.inmo.micro_utils.coroutines.runCatchingSafely
import dev.inmo.micro_utils.coroutines.safely
import dev.inmo.micro_utils.ktor.common.*
import io.ktor.client.HttpClient
import io.ktor.client.plugins.pluginOrNull
import io.ktor.client.plugins.websocket.*
import io.ktor.client.request.HttpRequestBuilder
import io.ktor.http.URLProtocol
import kotlinx.coroutines.channels.SendChannel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.isActive
/**
* @param checkReconnection This lambda will be called when it is required to reconnect to websocket to establish
* connection. Must return true in case if must be reconnected. By default always reconnecting
*/
@Warning("This feature is internal and should not be used directly. It is can be changed without any notification and warranty on compile-time or other guaranties")
inline fun <reified T : Any> openBaseWebSocketFlow(
noinline checkReconnection: suspend (Throwable?) -> Boolean = { true },
noinline webSocketSessionRequest: suspend SendChannel<T>.() -> Unit
): Flow<T> {
return channelFlow {
do {
val reconnect = runCatchingSafely {
webSocketSessionRequest()
checkReconnection(null)
}.getOrElse { e ->
checkReconnection(e).also {
if (!it) {
close(e)
}
}
}
} while (reconnect && isActive)
if (isActive) {
safely {
close()
}
}
}
}
/**
* @param checkReconnection This lambda will be called when it is required to reconnect to websocket to establish
* connection. Must return true in case if must be reconnected. By default always reconnecting
*/
inline fun <reified T : Any> HttpClient.openWebSocketFlow(
url: String,
useSecureConnection: Boolean,
noinline checkReconnection: suspend (Throwable?) -> Boolean = { true },
noinline requestBuilder: HttpRequestBuilder.() -> Unit = {}
): Flow<T> {
pluginOrNull(WebSockets) ?: error("Plugin $WebSockets must be installed for using createStandardWebsocketFlow")
return openBaseWebSocketFlow<T>(checkReconnection) {
val block: suspend DefaultClientWebSocketSession.() -> Unit = {
while (isActive) {
send(receiveDeserialized<T>())
}
}
if (useSecureConnection) {
wss(url, requestBuilder, block)
} else {
ws(url, requestBuilder, block)
}
}
}
/**
* @param checkReconnection This lambda will be called when it is required to reconnect to websocket to establish
* connection. Must return true in case if must be reconnected. By default always reconnecting
*/
inline fun <reified T : Any> HttpClient.openWebSocketFlow(
url: String,
noinline checkReconnection: suspend (Throwable?) -> Boolean = { true },
noinline requestBuilder: HttpRequestBuilder.() -> Unit = {}
): Flow<T> = openWebSocketFlow(url, false, checkReconnection, requestBuilder)
/**
* @param checkReconnection This lambda will be called when it is required to reconnect to websocket to establish
* connection. Must return true in case if must be reconnected. By default always reconnecting
*/
inline fun <reified T : Any> HttpClient.openSecureWebSocketFlow(
url: String,
noinline checkReconnection: suspend (Throwable?) -> Boolean = { true },
noinline requestBuilder: HttpRequestBuilder.() -> Unit = {}
): Flow<T> = openWebSocketFlow(url, true, checkReconnection, requestBuilder)
/**
* @param checkReconnection This lambda will be called when it is required to reconnect to websocket to establish
* connection. Must return true in case if must be reconnected. By default always reconnecting
*/
inline fun <reified T : Any> HttpClient.createStandardWebsocketFlow(
url: String,
noinline checkReconnection: suspend (Throwable?) -> Boolean = { true },
noinline requestBuilder: HttpRequestBuilder.() -> Unit = {}
): Flow<T> = openWebSocketFlow(url, checkReconnection, requestBuilder)

View File

@@ -12,6 +12,7 @@ import io.ktor.http.*
import io.ktor.utils.io.core.ByteReadPacket
import kotlinx.serialization.*
@Deprecated("This class will be removed in next")
typealias BodyPair<T> = Pair<SerializationStrategy<T>, T>
class UnifiedRequester(
@@ -33,7 +34,7 @@ class UnifiedRequester(
suspend fun <BodyType, ResultType> unipost(
url: String,
bodyInfo: BodyPair<BodyType>,
bodyInfo: Pair<SerializationStrategy<BodyType>, BodyType>,
resultDeserializer: DeserializationStrategy<ResultType>
) = client.unipost(url, bodyInfo, resultDeserializer, serialFormat)
@@ -52,7 +53,7 @@ class UnifiedRequester(
url: String,
filename: String,
inputProvider: InputProvider,
otherData: BodyPair<BodyType>,
otherData: Pair<SerializationStrategy<BodyType>, BodyType>,
resultDeserializer: DeserializationStrategy<ResultType>,
mimetype: String = "*/*",
additionalParametersBuilder: FormBuilder.() -> Unit = {},
@@ -75,7 +76,7 @@ class UnifiedRequester(
suspend fun <BodyType, ResultType> unimultipart(
url: String,
mppFile: MPPFile,
otherData: BodyPair<BodyType>,
otherData: Pair<SerializationStrategy<BodyType>, BodyType>,
resultDeserializer: DeserializationStrategy<ResultType>,
mimetype: String = "*/*",
additionalParametersBuilder: FormBuilder.() -> Unit = {},
@@ -120,7 +121,7 @@ fun <T> SerializationStrategy<T>.encodeUrlQueryValue(
suspend fun <BodyType, ResultType> HttpClient.unipost(
url: String,
bodyInfo: BodyPair<BodyType>,
bodyInfo: Pair<SerializationStrategy<BodyType>, BodyType>,
resultDeserializer: DeserializationStrategy<ResultType>,
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
) = post(url) {
@@ -162,7 +163,7 @@ suspend fun <ResultType> HttpClient.unimultipart(
suspend fun <BodyType, ResultType> HttpClient.unimultipart(
url: String,
filename: String,
otherData: BodyPair<BodyType>,
otherData: Pair<SerializationStrategy<BodyType>, BodyType>,
inputProvider: InputProvider,
resultDeserializer: DeserializationStrategy<ResultType>,
mimetype: String = "*/*",
@@ -220,7 +221,7 @@ suspend fun <ResultType> HttpClient.unimultipart(
suspend fun <BodyType, ResultType> HttpClient.unimultipart(
url: String,
mppFile: MPPFile,
otherData: BodyPair<BodyType>,
otherData: Pair<SerializationStrategy<BodyType>, BodyType>,
resultDeserializer: DeserializationStrategy<ResultType>,
mimetype: String = "*/*",
additionalParametersBuilder: FormBuilder.() -> Unit = {},

View File

@@ -4,7 +4,10 @@ import dev.inmo.micro_utils.common.MPPFile
import dev.inmo.micro_utils.ktor.common.TemporalFileId
import io.ktor.client.HttpClient
import kotlinx.coroutines.*
import org.w3c.dom.mediasource.ENDED
import org.w3c.dom.mediasource.ReadyState
import org.w3c.xhr.*
import org.w3c.xhr.XMLHttpRequest.Companion.DONE
suspend fun tempUpload(
fullTempUploadDraftPath: String,
@@ -12,7 +15,7 @@ suspend fun tempUpload(
onUpload: (Long, Long) -> Unit
): TemporalFileId {
val formData = FormData()
val answer = CompletableDeferred<TemporalFileId>()
val answer = CompletableDeferred<TemporalFileId>(currentCoroutineContext().job)
formData.append(
"data",
@@ -37,17 +40,15 @@ suspend fun tempUpload(
request.open("POST", fullTempUploadDraftPath, true)
request.send(formData)
val handle = currentCoroutineContext().job.invokeOnCompletion {
answer.invokeOnCompletion {
runCatching {
request.abort()
if (request.readyState != DONE) {
request.abort()
}
}
}
return runCatching {
answer.await()
}.also {
handle.dispose()
}.getOrThrow()
return answer.await()
}

View File

@@ -0,0 +1,15 @@
package dev.inmo.micro_utils.ktor.server
import io.ktor.server.application.ApplicationCall
import io.ktor.server.response.responseType
import io.ktor.util.InternalAPI
import io.ktor.util.reflect.TypeInfo
@InternalAPI
suspend fun <T : Any> ApplicationCall.respond(
message: T,
typeInfo: TypeInfo
) {
response.responseType = typeInfo
response.pipeline.execute(this, message as Any)
}

View File

@@ -12,6 +12,7 @@ import io.ktor.websocket.send
import kotlinx.coroutines.flow.Flow
import kotlinx.serialization.SerializationStrategy
@Deprecated("This method will be removed soon")
fun <T> Route.includeWebsocketHandling(
suburl: String,
flow: Flow<T>,

View File

@@ -0,0 +1,31 @@
package dev.inmo.micro_utils.ktor.server
import dev.inmo.micro_utils.coroutines.safely
import dev.inmo.micro_utils.ktor.common.*
import io.ktor.http.URLProtocol
import io.ktor.server.application.install
import io.ktor.server.application.pluginOrNull
import io.ktor.server.routing.Route
import io.ktor.server.routing.application
import io.ktor.server.websocket.*
import io.ktor.websocket.send
import kotlinx.coroutines.flow.Flow
import kotlinx.serialization.SerializationStrategy
inline fun <reified T : Any> Route.includeWebsocketHandling(
suburl: String,
flow: Flow<T>,
protocol: URLProtocol? = null,
noinline dataMapper: suspend WebSocketServerSession.(T) -> T? = { it }
) {
application.apply {
pluginOrNull(WebSockets) ?: install(WebSockets)
}
webSocket(suburl, protocol ?.name) {
safely {
flow.collect {
sendSerialized(dataMapper(it) ?: return@collect)
}
}
}
}

View File

@@ -13,6 +13,7 @@ import io.ktor.http.content.streamProvider
import io.ktor.server.application.call
import io.ktor.server.request.receiveMultipart
import io.ktor.server.response.respond
import io.ktor.server.response.respondText
import io.ktor.server.routing.Route
import io.ktor.server.routing.post
import kotlinx.coroutines.*
@@ -111,7 +112,7 @@ class TemporalFilesRoutingConfigurator(
temporalFilesMutex.withLock {
temporalFilesMap[fileId] = file
}
call.respond(fileId.string)
call.respondText(fileId.string)
launchSafelyWithoutExceptions { filesFlow.emit(fileId) }
} ?: call.respond(HttpStatusCode.BadRequest)
}

View File

@@ -2,19 +2,19 @@ package dev.inmo.micro_utils.pagination.utils
import dev.inmo.micro_utils.pagination.*
suspend fun <T> doForAll(
inline fun <T> doForAll(
initialPagination: Pagination = FirstPagePagination(),
paginationMapper: (PaginationResult<T>) -> Pagination?,
block: suspend (Pagination) -> PaginationResult<T>
block: (Pagination) -> PaginationResult<T>
) {
doWithPagination(initialPagination) {
block(it).let(paginationMapper)
}
}
suspend fun <T> doForAllWithNextPaging(
inline fun <T> doForAllWithNextPaging(
initialPagination: Pagination = FirstPagePagination(),
block: suspend (Pagination) -> PaginationResult<T>
block: (Pagination) -> PaginationResult<T>
) {
doForAll(
initialPagination,
@@ -23,9 +23,9 @@ suspend fun <T> doForAllWithNextPaging(
)
}
suspend fun <T> doAllWithCurrentPaging(
inline fun <T> doAllWithCurrentPaging(
initialPagination: Pagination = FirstPagePagination(),
block: suspend (Pagination) -> PaginationResult<T>
block: (Pagination) -> PaginationResult<T>
) {
doForAll(
initialPagination,
@@ -34,7 +34,7 @@ suspend fun <T> doAllWithCurrentPaging(
)
}
suspend fun <T> doForAllWithCurrentPaging(
inline fun <T> doForAllWithCurrentPaging(
initialPagination: Pagination = FirstPagePagination(),
block: suspend (Pagination) -> PaginationResult<T>
block: (Pagination) -> PaginationResult<T>
) = doAllWithCurrentPaging(initialPagination, block)

View File

@@ -2,10 +2,10 @@ package dev.inmo.micro_utils.pagination.utils
import dev.inmo.micro_utils.pagination.*
suspend fun <T> getAll(
inline fun <T> getAll(
initialPagination: Pagination = FirstPagePagination(),
paginationMapper: (PaginationResult<T>) -> Pagination?,
block: suspend (Pagination) -> PaginationResult<T>
block: (Pagination) -> PaginationResult<T>
): List<T> {
val results = mutableListOf<T>()
doForAll(initialPagination, paginationMapper) {
@@ -16,46 +16,45 @@ suspend fun <T> getAll(
return results.toList()
}
suspend fun <T, R> R.getAllBy(
inline fun <T, R> R.getAllBy(
initialPagination: Pagination = FirstPagePagination(),
paginationMapper: R.(PaginationResult<T>) -> Pagination?,
block: suspend R.(Pagination) -> PaginationResult<T>
block: R.(Pagination) -> PaginationResult<T>
): List<T> = getAll(
initialPagination,
{ paginationMapper(it) },
{ block(it) }
)
suspend fun <T> getAllWithNextPaging(
inline fun <T> getAllWithNextPaging(
initialPagination: Pagination = FirstPagePagination(),
block: suspend (Pagination) -> PaginationResult<T>
block: (Pagination) -> PaginationResult<T>
): List<T> = getAll(
initialPagination,
{ it.nextPageIfNotEmpty() },
block
)
suspend fun <T, R> R.getAllByWithNextPaging(
inline fun <T, R> R.getAllByWithNextPaging(
initialPagination: Pagination = FirstPagePagination(),
block: suspend R.(Pagination) -> PaginationResult<T>
block: R.(Pagination) -> PaginationResult<T>
): List<T> = getAllWithNextPaging(
initialPagination,
{ block(it) }
)
suspend fun <T> getAllWithCurrentPaging(
inline fun <T> getAllWithCurrentPaging(
initialPagination: Pagination = FirstPagePagination(),
block: suspend (Pagination) -> PaginationResult<T>
block: (Pagination) -> PaginationResult<T>
): List<T> = getAll(
initialPagination,
{ it.currentPageIfNotEmpty() },
block
)
suspend fun <T, R> R.getAllByWithCurrentPaging(
inline fun <T, R> R.getAllByWithCurrentPaging(
initialPagination: Pagination = FirstPagePagination(),
block: suspend R.(Pagination) -> PaginationResult<T>
block: R.(Pagination) -> PaginationResult<T>
): List<T> = getAllWithCurrentPaging(
initialPagination,
{ block(it) }
)
initialPagination
) { block(it) }

View File

@@ -1,16 +1,16 @@
package dev.inmo.micro_utils.repos.cache
import dev.inmo.micro_utils.repos.*
import dev.inmo.micro_utils.repos.cache.cache.KVCache
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.*
open class ReadCRUDCacheRepo<ObjectType, IdType>(
protected val parentRepo: ReadCRUDRepo<ObjectType, IdType>,
protected val kvCache: KVCache<IdType, ObjectType>,
protected val idGetter: (ObjectType) -> IdType
) : ReadCRUDRepo<ObjectType, IdType> by parentRepo {
protected open val parentRepo: ReadCRUDRepo<ObjectType, IdType>,
protected open val kvCache: KVCache<IdType, ObjectType>,
protected open val idGetter: (ObjectType) -> IdType
) : ReadCRUDRepo<ObjectType, IdType> by parentRepo, CacheRepo {
override suspend fun getById(id: IdType): ObjectType? = kvCache.get(id) ?: (parentRepo.getById(id) ?.also {
kvCache.set(id, it)
})
@@ -18,8 +18,63 @@ open class ReadCRUDCacheRepo<ObjectType, IdType>(
override suspend fun contains(id: IdType): Boolean = kvCache.contains(id) || parentRepo.contains(id)
}
fun <ObjectType, IdType> ReadCRUDRepo<ObjectType, IdType>.cached(
kvCache: KVCache<IdType, ObjectType>,
idGetter: (ObjectType) -> IdType
) = ReadCRUDCacheRepo(this, kvCache, idGetter)
open class WriteCRUDCacheRepo<ObjectType, IdType, InputValueType>(
protected open val parentRepo: WriteCRUDRepo<ObjectType, IdType, InputValueType>,
protected open val kvCache: KVCache<IdType, ObjectType>,
protected open val scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
protected open val idGetter: (ObjectType) -> IdType
) : WriteCRUDRepo<ObjectType, IdType, InputValueType>, CacheRepo {
override val newObjectsFlow: Flow<ObjectType> by parentRepo::newObjectsFlow
override val updatedObjectsFlow: Flow<ObjectType> by parentRepo::updatedObjectsFlow
override val deletedObjectsIdsFlow: Flow<IdType> by parentRepo::deletedObjectsIdsFlow
val deletedObjectsFlowJob = parentRepo.deletedObjectsIdsFlow.onEach {
kvCache.unset(it)
}.launchIn(scope)
override suspend fun deleteById(ids: List<IdType>) = parentRepo.deleteById(ids)
override suspend fun update(values: List<UpdatedValuePair<IdType, InputValueType>>): List<ObjectType> {
val updated = parentRepo.update(values)
kvCache.unset(values.map { it.id })
kvCache.set(updated.associateBy { idGetter(it) })
return updated
}
override suspend fun update(id: IdType, value: InputValueType): ObjectType? {
return parentRepo.update(id, value) ?.also {
kvCache.unset(id)
kvCache.set(idGetter(it), it)
}
}
override suspend fun create(values: List<InputValueType>): List<ObjectType> {
val created = parentRepo.create(values)
kvCache.set(
created.associateBy { idGetter(it) }
)
return created
}
}
fun <ObjectType, IdType, InputType> WriteCRUDRepo<ObjectType, IdType, InputType>.caching(
kvCache: KVCache<IdType, ObjectType>,
scope: CoroutineScope,
idGetter: (ObjectType) -> IdType
) = WriteCRUDCacheRepo(this, kvCache, scope, idGetter)
open class CRUDCacheRepo<ObjectType, IdType, InputValueType>(
parentRepo: CRUDRepo<ObjectType, IdType, InputValueType>,
override val parentRepo: CRUDRepo<ObjectType, IdType, InputValueType>,
kvCache: KVCache<IdType, ObjectType>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
idGetter: (ObjectType) -> IdType
@@ -27,8 +82,17 @@ open class CRUDCacheRepo<ObjectType, IdType, InputValueType>(
parentRepo,
kvCache,
idGetter
), CRUDRepo<ObjectType, IdType, InputValueType>, WriteCRUDRepo<ObjectType, IdType, InputValueType> by parentRepo {
protected val onNewJob = parentRepo.newObjectsFlow.onEach { kvCache.set(idGetter(it), it) }.launchIn(scope)
protected val onUpdatedJob = parentRepo.updatedObjectsFlow.onEach { kvCache.set(idGetter(it), it) }.launchIn(scope)
protected val onRemoveJob = parentRepo.deletedObjectsIdsFlow.onEach { kvCache.unset(it) }.launchIn(scope)
}
),
WriteCRUDRepo<ObjectType, IdType, InputValueType> by WriteCRUDCacheRepo(
parentRepo,
kvCache,
scope,
idGetter
),
CRUDRepo<ObjectType, IdType, InputValueType>
fun <ObjectType, IdType, InputType> CRUDRepo<ObjectType, IdType, InputType>.cached(
kvCache: KVCache<IdType, ObjectType>,
scope: CoroutineScope,
idGetter: (ObjectType) -> IdType
) = CRUDCacheRepo(this, kvCache, scope, idGetter)

View File

@@ -0,0 +1,3 @@
package dev.inmo.micro_utils.repos.cache
interface CacheRepo

View File

@@ -1,41 +1,6 @@
package dev.inmo.micro_utils.repos.cache
import dev.inmo.micro_utils.repos.*
import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
interface KVCache<K, V> : KeyValueRepo<K, V>
open class SimpleKVCache<K, V>(
protected val cachedValuesCount: Int,
private val kvParent: KeyValueRepo<K, V> = MapKeyValueRepo<K, V>()
) : KVCache<K, V>, KeyValueRepo<K, V> by kvParent {
protected open val cacheStack = ArrayList<K>(cachedValuesCount)
protected val syncMutex = Mutex()
protected suspend fun makeUnset(toUnset: List<K>) {
cacheStack.removeAll(toUnset)
kvParent.unset(toUnset)
}
override suspend fun set(toSet: Map<K, V>) {
syncMutex.withLock {
if (toSet.size > cachedValuesCount) {
cacheStack.clear()
kvParent.unset(getAllWithNextPaging { kvParent.keys(it) })
val keysToInclude = toSet.keys.drop(toSet.size - cachedValuesCount)
cacheStack.addAll(keysToInclude)
kvParent.set(keysToInclude.associateWith { toSet.getValue(it) })
} else {
makeUnset(cacheStack.take(toSet.size))
}
}
}
override suspend fun unset(toUnset: List<K>) {
syncMutex.withLock { makeUnset(toUnset) }
}
}
@Deprecated("Replaced", ReplaceWith("KVCache", "dev.inmo.micro_utils.repos.cache.cache.KVCache"))
typealias KVCache<K, V> = dev.inmo.micro_utils.repos.cache.cache.KVCache<K, V>
@Deprecated("Replaced", ReplaceWith("SimpleKVCache", "dev.inmo.micro_utils.repos.cache.cache.SimpleKVCache"))
typealias SimpleKVCache<K, V> = dev.inmo.micro_utils.repos.cache.cache.SimpleKVCache<K, V>

View File

@@ -1,25 +1,35 @@
package dev.inmo.micro_utils.repos.cache
import dev.inmo.micro_utils.pagination.Pagination
import dev.inmo.micro_utils.pagination.PaginationResult
import dev.inmo.micro_utils.repos.*
import dev.inmo.micro_utils.repos.cache.cache.KVCache
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
open class ReadKeyValueCacheRepo<Key,Value>(
protected val parentRepo: ReadKeyValueRepo<Key, Value>,
protected val kvCache: KVCache<Key, Value>,
) : ReadKeyValueRepo<Key,Value> by parentRepo {
protected open val parentRepo: ReadKeyValueRepo<Key, Value>,
protected open val kvCache: KVCache<Key, Value>,
) : ReadKeyValueRepo<Key,Value> by parentRepo, CacheRepo {
override suspend fun get(k: Key): Value? = kvCache.get(k) ?: parentRepo.get(k) ?.also { kvCache.set(k, it) }
override suspend fun contains(key: Key): Boolean = kvCache.contains(key) || parentRepo.contains(key)
}
fun <Key, Value> ReadKeyValueRepo<Key, Value>.cached(
kvCache: KVCache<Key, Value>
) = ReadKeyValueCacheRepo(this, kvCache)
open class KeyValueCacheRepo<Key,Value>(
parentRepo: KeyValueRepo<Key, Value>,
kvCache: KVCache<Key, Value>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
) : ReadKeyValueCacheRepo<Key,Value>(parentRepo, kvCache), KeyValueRepo<Key,Value>, WriteKeyValueRepo<Key, Value> by parentRepo {
) : ReadKeyValueCacheRepo<Key,Value>(parentRepo, kvCache), KeyValueRepo<Key,Value>, WriteKeyValueRepo<Key, Value> by parentRepo, CacheRepo {
protected val onNewJob = parentRepo.onNewValue.onEach { kvCache.set(it.first, it.second) }.launchIn(scope)
protected val onRemoveJob = parentRepo.onValueRemoved.onEach { kvCache.unset(it) }.launchIn(scope)
}
fun <Key, Value> KeyValueRepo<Key, Value>.cached(
kvCache: KVCache<Key, Value>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
) = KeyValueCacheRepo(this, kvCache, scope)

View File

@@ -5,16 +5,15 @@ import dev.inmo.micro_utils.pagination.PaginationResult
import dev.inmo.micro_utils.pagination.utils.paginate
import dev.inmo.micro_utils.pagination.utils.reverse
import dev.inmo.micro_utils.repos.*
import dev.inmo.micro_utils.repos.cache.cache.KVCache
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
open class ReadKeyValuesCacheRepo<Key,Value>(
protected val parentRepo: ReadKeyValuesRepo<Key, Value>,
protected val kvCache: KVCache<Key, List<Value>>
) : ReadKeyValuesRepo<Key,Value> by parentRepo {
protected open val parentRepo: ReadKeyValuesRepo<Key, Value>,
protected open val kvCache: KVCache<Key, List<Value>>
) : ReadKeyValuesRepo<Key,Value> by parentRepo, CacheRepo {
override suspend fun get(k: Key, pagination: Pagination, reversed: Boolean): PaginationResult<Value> {
return kvCache.get(k) ?.paginate(
pagination.let { if (reversed) it.reverse(count(k)) else it }
@@ -31,12 +30,21 @@ open class ReadKeyValuesCacheRepo<Key,Value>(
override suspend fun contains(k: Key): Boolean = kvCache.contains(k) || parentRepo.contains(k)
}
fun <Key, Value> ReadKeyValuesRepo<Key, Value>.cached(
kvCache: KVCache<Key, List<Value>>
) = ReadKeyValuesCacheRepo(this, kvCache)
open class KeyValuesCacheRepo<Key,Value>(
parentRepo: KeyValuesRepo<Key, Value>,
kvCache: KVCache<Key, List<Value>>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
) : ReadKeyValuesCacheRepo<Key,Value>(parentRepo, kvCache), KeyValuesRepo<Key,Value>, WriteKeyValuesRepo<Key,Value> by parentRepo {
) : ReadKeyValuesCacheRepo<Key,Value>(parentRepo, kvCache), KeyValuesRepo<Key,Value>, WriteKeyValuesRepo<Key,Value> by parentRepo, CacheRepo {
protected val onNewJob = parentRepo.onNewValue.onEach { kvCache.set(it.first, kvCache.get(it.first) ?.plus(it.second) ?: listOf(it.second)) }.launchIn(scope)
protected val onRemoveJob = parentRepo.onValueRemoved.onEach { kvCache.set(it.first, kvCache.get(it.first) ?.minus(it.second) ?: return@onEach) }.launchIn(scope)
protected val onDataClearedJob = parentRepo.onDataCleared.onEach { kvCache.unset(it) }.launchIn(scope)
}
fun <Key, Value> KeyValuesRepo<Key, Value>.cached(
kvCache: KVCache<Key, List<Value>>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
) = KeyValuesCacheRepo(this, kvCache, scope)

View File

@@ -0,0 +1,8 @@
package dev.inmo.micro_utils.repos.cache.cache
/**
* This interface declares that current type of [KVCache] will contains all the data all the time of its life
*/
interface FullKVCache<K, V> : KVCache<K, V> {
companion object
}

View File

@@ -0,0 +1,7 @@
package dev.inmo.micro_utils.repos.cache.cache
import dev.inmo.micro_utils.repos.*
interface KVCache<K, V> : KeyValueRepo<K, V> {
companion object
}

View File

@@ -0,0 +1,28 @@
package dev.inmo.micro_utils.repos.cache.cache
import dev.inmo.micro_utils.repos.KeyValueRepo
import dev.inmo.micro_utils.repos.MapKeyValueRepo
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
open class SimpleFullKVCache<K, V>(
private val kvParent: KeyValueRepo<K, V> = MapKeyValueRepo<K, V>()
) : FullKVCache<K, V>, KeyValueRepo<K, V> by kvParent {
protected val syncMutex = Mutex()
override suspend fun set(toSet: Map<K, V>) {
syncMutex.withLock {
kvParent.set(toSet)
}
}
override suspend fun unset(toUnset: List<K>) {
syncMutex.withLock {
kvParent.unset(toUnset)
}
}
}
inline fun <K, V> FullKVCache(
kvParent: KeyValueRepo<K, V> = MapKeyValueRepo<K, V>()
) = SimpleFullKVCache<K, V>(kvParent)

View File

@@ -0,0 +1,42 @@
package dev.inmo.micro_utils.repos.cache.cache
import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging
import dev.inmo.micro_utils.repos.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
open class SimpleKVCache<K, V>(
protected val cachedValuesCount: Int,
private val kvParent: KeyValueRepo<K, V> = MapKeyValueRepo<K, V>()
) : KVCache<K, V>, KeyValueRepo<K, V> by kvParent {
protected open val cacheQueue = ArrayDeque<K>(cachedValuesCount)
protected val syncMutex = Mutex()
protected suspend fun makeUnset(toUnset: List<K>) {
cacheQueue.removeAll(toUnset)
kvParent.unset(toUnset)
}
override suspend fun set(toSet: Map<K, V>) {
syncMutex.withLock {
for ((k, v) in toSet) {
if (cacheQueue.size >= cachedValuesCount) {
cacheQueue.removeFirstOrNull() ?.let {
kvParent.unset(it)
}
}
cacheQueue.addLast(k)
kvParent.set(k, v)
}
}
}
override suspend fun unset(toUnset: List<K>) {
syncMutex.withLock { makeUnset(toUnset) }
}
}
inline fun <K, V> KVCache(
cachedValuesCount: Int,
kvParent: KeyValueRepo<K, V> = MapKeyValueRepo<K, V>()
) = SimpleKVCache<K, V>(cachedValuesCount, kvParent)

View File

@@ -0,0 +1,95 @@
package dev.inmo.micro_utils.repos.cache.full
import dev.inmo.micro_utils.common.*
import dev.inmo.micro_utils.pagination.Pagination
import dev.inmo.micro_utils.pagination.PaginationResult
import dev.inmo.micro_utils.pagination.utils.doForAllWithNextPaging
import dev.inmo.micro_utils.repos.*
import dev.inmo.micro_utils.repos.cache.*
import dev.inmo.micro_utils.repos.cache.cache.FullKVCache
import dev.inmo.micro_utils.repos.cache.cache.KVCache
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
open class FullReadCRUDCacheRepo<ObjectType, IdType>(
protected open val parentRepo: ReadCRUDRepo<ObjectType, IdType>,
protected open val kvCache: FullKVCache<IdType, ObjectType>,
protected open val idGetter: (ObjectType) -> IdType
) : ReadCRUDRepo<ObjectType, IdType>, FullCacheRepo {
protected inline fun <T> doOrTakeAndActualize(
action: FullKVCache<IdType, ObjectType>.() -> Optional<T>,
actionElse: ReadCRUDRepo<ObjectType, IdType>.() -> T,
actualize: FullKVCache<IdType, ObjectType>.(T) -> Unit
): T {
kvCache.action().onPresented {
return it
}.onAbsent {
return parentRepo.actionElse().also {
kvCache.actualize(it)
}
}
error("The result should be returned above")
}
protected open suspend fun actualizeAll() {
kvCache.clear()
doForAllWithNextPaging {
parentRepo.getByPagination(it).also {
kvCache.set(it.results.associateBy { idGetter(it) })
}
}
}
override suspend fun getByPagination(pagination: Pagination): PaginationResult<ObjectType> = doOrTakeAndActualize(
{ values(pagination).takeIf { it.results.isNotEmpty() }.optionalOrAbsentIfNull },
{ getByPagination(pagination) },
{ if (it.results.isNotEmpty()) actualizeAll() }
)
override suspend fun count(): Long = doOrTakeAndActualize(
{ count().takeIf { it != 0L }.optionalOrAbsentIfNull },
{ count() },
{ if (it != 0L) actualizeAll() }
)
override suspend fun contains(id: IdType): Boolean = doOrTakeAndActualize(
{ contains(id).takeIf { it }.optionalOrAbsentIfNull },
{ contains(id) },
{ if (it) parentRepo.getById(id) ?.let { set(id, it) } }
)
override suspend fun getById(id: IdType): ObjectType? = doOrTakeAndActualize(
{ get(id) ?.optional ?: Optional.absent() },
{ getById(id) },
{ it ?.let { set(idGetter(it), it) } }
)
}
fun <ObjectType, IdType> ReadCRUDRepo<ObjectType, IdType>.cached(
kvCache: FullKVCache<IdType, ObjectType>,
idGetter: (ObjectType) -> IdType
) = FullReadCRUDCacheRepo(this, kvCache, idGetter)
open class FullCRUDCacheRepo<ObjectType, IdType, InputValueType>(
override val parentRepo: CRUDRepo<ObjectType, IdType, InputValueType>,
kvCache: FullKVCache<IdType, ObjectType>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
idGetter: (ObjectType) -> IdType
) : FullReadCRUDCacheRepo<ObjectType, IdType>(
parentRepo,
kvCache,
idGetter
),
WriteCRUDRepo<ObjectType, IdType, InputValueType> by WriteCRUDCacheRepo(
parentRepo,
kvCache,
scope,
idGetter
),
CRUDRepo<ObjectType, IdType, InputValueType>
fun <ObjectType, IdType, InputType> CRUDRepo<ObjectType, IdType, InputType>.cached(
kvCache: FullKVCache<IdType, ObjectType>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
idGetter: (ObjectType) -> IdType
) = FullCRUDCacheRepo(this, kvCache, scope, idGetter)

View File

@@ -0,0 +1,5 @@
package dev.inmo.micro_utils.repos.cache.full
import dev.inmo.micro_utils.repos.cache.CacheRepo
interface FullCacheRepo : CacheRepo

View File

@@ -0,0 +1,104 @@
package dev.inmo.micro_utils.repos.cache.full
import dev.inmo.micro_utils.common.*
import dev.inmo.micro_utils.pagination.Pagination
import dev.inmo.micro_utils.pagination.PaginationResult
import dev.inmo.micro_utils.repos.*
import dev.inmo.micro_utils.repos.cache.cache.FullKVCache
import dev.inmo.micro_utils.repos.pagination.getAll
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.*
open class FullReadKeyValueCacheRepo<Key,Value>(
protected open val parentRepo: ReadKeyValueRepo<Key, Value>,
protected open val kvCache: FullKVCache<Key, Value>,
) : ReadKeyValueRepo<Key, Value>, FullCacheRepo {
protected inline fun <T> doOrTakeAndActualize(
action: FullKVCache<Key, Value>.() -> Optional<T>,
actionElse: ReadKeyValueRepo<Key, Value>.() -> T,
actualize: FullKVCache<Key, Value>.(T) -> Unit
): T {
kvCache.action().onPresented {
return it
}.onAbsent {
return parentRepo.actionElse().also {
kvCache.actualize(it)
}
}
error("The result should be returned above")
}
protected open suspend fun actualizeAll() {
kvCache.clear()
kvCache.set(parentRepo.getAll { keys(it) }.toMap())
}
override suspend fun get(k: Key): Value? = doOrTakeAndActualize(
{ get(k) ?.optional ?: Optional.absent() },
{ get(k) },
{ set(k, it ?: return@doOrTakeAndActualize) }
)
override suspend fun values(pagination: Pagination, reversed: Boolean): PaginationResult<Value> = doOrTakeAndActualize(
{ values(pagination, reversed).takeIf { it.results.isNotEmpty() }.optionalOrAbsentIfNull },
{ values(pagination, reversed) },
{ if (it.results.isNotEmpty()) actualizeAll() }
)
override suspend fun count(): Long = doOrTakeAndActualize(
{ count().takeIf { it != 0L }.optionalOrAbsentIfNull },
{ count() },
{ if (it != 0L) actualizeAll() }
)
override suspend fun contains(key: Key): Boolean = doOrTakeAndActualize(
{ contains(key).takeIf { it }.optionalOrAbsentIfNull },
{ contains(key) },
{ if (it) parentRepo.get(key) ?.also { kvCache.set(key, it) } }
)
override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<Key> = doOrTakeAndActualize(
{ keys(pagination, reversed).takeIf { it.results.isNotEmpty() }.optionalOrAbsentIfNull },
{ keys(pagination, reversed) },
{ if (it.results.isNotEmpty()) actualizeAll() }
)
override suspend fun keys(v: Value, pagination: Pagination, reversed: Boolean): PaginationResult<Key> = doOrTakeAndActualize(
{ keys(v, pagination, reversed).takeIf { it.results.isNotEmpty() }.optionalOrAbsentIfNull },
{ parentRepo.keys(v, pagination, reversed) },
{ if (it.results.isNotEmpty()) actualizeAll() }
)
}
fun <Key, Value> ReadKeyValueRepo<Key, Value>.cached(
kvCache: FullKVCache<Key, Value>
) = FullReadKeyValueCacheRepo(this, kvCache)
open class FullWriteKeyValueCacheRepo<Key,Value>(
protected open val parentRepo: WriteKeyValueRepo<Key, Value>,
protected open val kvCache: FullKVCache<Key, Value>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
) : WriteKeyValueRepo<Key, Value> by parentRepo, FullCacheRepo {
protected val onNewJob = parentRepo.onNewValue.onEach { kvCache.set(it.first, it.second) }.launchIn(scope)
protected val onRemoveJob = parentRepo.onValueRemoved.onEach { kvCache.unset(it) }.launchIn(scope)
}
fun <Key, Value> WriteKeyValueRepo<Key, Value>.caching(
kvCache: FullKVCache<Key, Value>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
) = FullWriteKeyValueCacheRepo(this, kvCache, scope)
open class FullKeyValueCacheRepo<Key,Value>(
parentRepo: KeyValueRepo<Key, Value>,
kvCache: FullKVCache<Key, Value>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
) : FullWriteKeyValueCacheRepo<Key,Value>(parentRepo, kvCache, scope),
KeyValueRepo<Key,Value>,
ReadKeyValueRepo<Key, Value> by FullReadKeyValueCacheRepo(parentRepo, kvCache) {
override suspend fun unsetWithValues(toUnset: List<Value>) = parentRepo.unsetWithValues(toUnset)
}
fun <Key, Value> KeyValueRepo<Key, Value>.cached(
kvCache: FullKVCache<Key, Value>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
) = FullKeyValueCacheRepo(this, kvCache, scope)

View File

@@ -0,0 +1,154 @@
package dev.inmo.micro_utils.repos.cache.full
import dev.inmo.micro_utils.common.*
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.pagination.utils.*
import dev.inmo.micro_utils.repos.*
import dev.inmo.micro_utils.repos.cache.cache.FullKVCache
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.*
open class FullReadKeyValuesCacheRepo<Key,Value>(
protected open val parentRepo: ReadKeyValuesRepo<Key, Value>,
protected open val kvCache: FullKVCache<Key, List<Value>>,
) : ReadKeyValuesRepo<Key, Value>, FullCacheRepo {
protected inline fun <T> doOrTakeAndActualize(
action: FullKVCache<Key, List<Value>>.() -> Optional<T>,
actionElse: ReadKeyValuesRepo<Key, Value>.() -> T,
actualize: FullKVCache<Key, List<Value>>.(T) -> Unit
): T {
kvCache.action().onPresented {
return it
}.onAbsent {
return parentRepo.actionElse().also {
kvCache.actualize(it)
}
}
error("The result should be returned above")
}
protected open suspend fun actualizeKey(k: Key) {
kvCache.set(k, parentRepo.getAll(k))
}
protected open suspend fun actualizeAll() {
doAllWithCurrentPaging { kvCache.keys(it).also { kvCache.unset(it.results) } }
kvCache.set(parentRepo.getAll())
}
override suspend fun get(k: Key, pagination: Pagination, reversed: Boolean): PaginationResult<Value> {
return doOrTakeAndActualize(
{
get(k) ?.paginate(
pagination.let { if (reversed) it.reverse(count(k)) else it }
) ?.let {
if (reversed) it.copy(results = it.results.reversed()) else it
}.optionalOrAbsentIfNull
},
{ get(k, pagination, reversed) },
{ actualizeKey(k) }
)
}
override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<Key> {
return doOrTakeAndActualize(
{
kvCache.keys(pagination, reversed).takeIf { it.results.isNotEmpty() }.optionalOrAbsentIfNull
},
{ parentRepo.keys(pagination, reversed) },
{ actualizeAll() }
)
}
override suspend fun count(): Long = doOrTakeAndActualize(
{ count().takeIf { it != 0L }.optionalOrAbsentIfNull },
{ count() },
{ if (it != 0L) actualizeAll() }
)
override suspend fun count(k: Key): Long = doOrTakeAndActualize(
{ count().takeIf { it != 0L }.optionalOrAbsentIfNull },
{ count() },
{ if (it != 0L) actualizeKey(k) }
)
override suspend fun contains(k: Key, v: Value): Boolean = doOrTakeAndActualize(
{ get(k) ?.contains(v).takeIf { it == true }.optionalOrAbsentIfNull },
{ contains(k, v) },
{ if (it) actualizeKey(k) }
)
override suspend fun contains(k: Key): Boolean = doOrTakeAndActualize(
{ contains(k).takeIf { it }.optionalOrAbsentIfNull },
{ contains(k) },
{ if (it) actualizeKey(k) }
)
override suspend fun keys(
v: Value,
pagination: Pagination,
reversed: Boolean
): PaginationResult<Key> = doOrTakeAndActualize(
{
val keys = getAllWithNextPaging { keys(it) }.filter { get(it) ?.contains(v) == true }.optionallyReverse(reversed)
if (keys.isNotEmpty()) {
keys.paginate(pagination.optionallyReverse(keys.size, reversed)).takeIf { it.results.isNotEmpty() }.optionalOrAbsentIfNull
} else {
Optional.absent()
}
},
{ parentRepo.keys(v, pagination, reversed) },
{ if (it.results.isNotEmpty()) actualizeAll() }
)
}
fun <Key, Value> ReadKeyValuesRepo<Key, Value>.cached(
kvCache: FullKVCache<Key, List<Value>>
) = FullReadKeyValuesCacheRepo(this, kvCache)
open class FullWriteKeyValuesCacheRepo<Key,Value>(
protected open val parentRepo: WriteKeyValuesRepo<Key, Value>,
protected open val kvCache: FullKVCache<Key, List<Value>>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
) : WriteKeyValuesRepo<Key, Value> by parentRepo, FullCacheRepo {
protected val onNewJob = parentRepo.onNewValue.onEach {
kvCache.set(
it.first,
kvCache.get(it.first) ?.plus(it.second) ?: listOf(it.second)
)
}.launchIn(scope)
protected val onRemoveJob = parentRepo.onValueRemoved.onEach {
kvCache.set(
it.first,
kvCache.get(it.first) ?.minus(it.second) ?: return@onEach
)
}.launchIn(scope)
}
fun <Key, Value> WriteKeyValuesRepo<Key, Value>.caching(
kvCache: FullKVCache<Key, List<Value>>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
) = FullWriteKeyValuesCacheRepo(this, kvCache, scope)
open class FullKeyValuesCacheRepo<Key,Value>(
parentRepo: KeyValuesRepo<Key, Value>,
kvCache: FullKVCache<Key, List<Value>>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
) : FullWriteKeyValuesCacheRepo<Key, Value>(parentRepo, kvCache, scope),
KeyValuesRepo<Key, Value>,
ReadKeyValuesRepo<Key, Value> by FullReadKeyValuesCacheRepo(parentRepo, kvCache) {
override suspend fun clearWithValue(v: Value) {
doAllWithCurrentPaging {
keys(v, it).also {
remove(it.results.associateWith { listOf(v) })
}
}
}
}
fun <Key, Value> KeyValuesRepo<Key, Value>.caching(
kvCache: FullKVCache<Key, List<Value>>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
) = FullKeyValuesCacheRepo(this, kvCache, scope)

View File

@@ -1,11 +1,10 @@
package dev.inmo.micro_utils.repos
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.pagination.utils.doForAllWithCurrentPaging
import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging
import kotlinx.coroutines.flow.Flow
interface ReadOneToManyKeyValueRepo<Key, Value> : Repo {
interface ReadKeyValuesRepo<Key, Value> : Repo {
suspend fun get(k: Key, pagination: Pagination, reversed: Boolean = false): PaginationResult<Value>
suspend fun keys(pagination: Pagination, reversed: Boolean = false): PaginationResult<Key>
suspend fun keys(v: Value, pagination: Pagination, reversed: Boolean = false): PaginationResult<Key>
@@ -36,9 +35,9 @@ interface ReadOneToManyKeyValueRepo<Key, Value> : Repo {
}
}
}
typealias ReadKeyValuesRepo<Key,Value> = ReadOneToManyKeyValueRepo<Key, Value>
typealias ReadOneToManyKeyValueRepo<Key,Value> = ReadKeyValuesRepo<Key, Value>
interface WriteOneToManyKeyValueRepo<Key, Value> : Repo {
interface WriteKeyValuesRepo<Key, Value> : Repo {
val onNewValue: Flow<Pair<Key, Value>>
val onValueRemoved: Flow<Pair<Key, Value>>
val onDataCleared: Flow<Key>
@@ -55,41 +54,41 @@ interface WriteOneToManyKeyValueRepo<Key, Value> : Repo {
add(toSet)
}
}
typealias WriteKeyValuesRepo<Key,Value> = WriteOneToManyKeyValueRepo<Key, Value>
typealias WriteOneToManyKeyValueRepo<Key,Value> = WriteKeyValuesRepo<Key, Value>
suspend inline fun <Key, Value, REPO : WriteOneToManyKeyValueRepo<Key, Value>> REPO.add(
suspend inline fun <Key, Value, REPO : WriteKeyValuesRepo<Key, Value>> REPO.add(
keysAndValues: List<Pair<Key, List<Value>>>
) = add(keysAndValues.toMap())
suspend inline fun <Key, Value, REPO : WriteOneToManyKeyValueRepo<Key, Value>> REPO.add(
suspend inline fun <Key, Value, REPO : WriteKeyValuesRepo<Key, Value>> REPO.add(
vararg keysAndValues: Pair<Key, List<Value>>
) = add(keysAndValues.toMap())
suspend inline fun <Key, Value> WriteOneToManyKeyValueRepo<Key, Value>.add(
suspend inline fun <Key, Value> WriteKeyValuesRepo<Key, Value>.add(
k: Key, v: List<Value>
) = add(mapOf(k to v))
suspend inline fun <Key, Value> WriteOneToManyKeyValueRepo<Key, Value>.add(
suspend inline fun <Key, Value> WriteKeyValuesRepo<Key, Value>.add(
k: Key, vararg v: Value
) = add(k, v.toList())
suspend inline fun <Key, Value, REPO : WriteOneToManyKeyValueRepo<Key, Value>> REPO.set(
suspend inline fun <Key, Value, REPO : WriteKeyValuesRepo<Key, Value>> REPO.set(
keysAndValues: List<Pair<Key, List<Value>>>
) = set(keysAndValues.toMap())
suspend inline fun <Key, Value, REPO : WriteOneToManyKeyValueRepo<Key, Value>> REPO.set(
suspend inline fun <Key, Value, REPO : WriteKeyValuesRepo<Key, Value>> REPO.set(
vararg keysAndValues: Pair<Key, List<Value>>
) = set(keysAndValues.toMap())
suspend inline fun <Key, Value> WriteOneToManyKeyValueRepo<Key, Value>.set(
suspend inline fun <Key, Value> WriteKeyValuesRepo<Key, Value>.set(
k: Key, v: List<Value>
) = set(mapOf(k to v))
suspend inline fun <Key, Value> WriteOneToManyKeyValueRepo<Key, Value>.set(
suspend inline fun <Key, Value> WriteKeyValuesRepo<Key, Value>.set(
k: Key, vararg v: Value
) = set(k, v.toList())
interface OneToManyKeyValueRepo<Key, Value> : ReadOneToManyKeyValueRepo<Key, Value>, WriteOneToManyKeyValueRepo<Key, Value> {
interface KeyValuesRepo<Key, Value> : ReadKeyValuesRepo<Key, Value>, WriteKeyValuesRepo<Key, Value> {
override suspend fun clearWithValue(v: Value) {
doWithPagination {
val keysResult = keys(v, it)
@@ -102,22 +101,29 @@ interface OneToManyKeyValueRepo<Key, Value> : ReadOneToManyKeyValueRepo<Key, Val
}
}
}
typealias KeyValuesRepo<Key,Value> = OneToManyKeyValueRepo<Key, Value>
typealias OneToManyKeyValueRepo<Key,Value> = KeyValuesRepo<Key, Value>
suspend inline fun <Key, Value> WriteOneToManyKeyValueRepo<Key, Value>.remove(
class DelegateBasedKeyValuesRepo<Key, Value>(
readDelegate: ReadKeyValuesRepo<Key, Value>,
writeDelegate: WriteKeyValuesRepo<Key, Value>
) : KeyValuesRepo<Key, Value>,
ReadKeyValuesRepo<Key, Value> by readDelegate,
WriteKeyValuesRepo<Key, Value> by writeDelegate
suspend inline fun <Key, Value> WriteKeyValuesRepo<Key, Value>.remove(
keysAndValues: List<Pair<Key, List<Value>>>
) = remove(keysAndValues.toMap())
suspend inline fun <Key, Value> WriteOneToManyKeyValueRepo<Key, Value>.remove(
suspend inline fun <Key, Value> WriteKeyValuesRepo<Key, Value>.remove(
vararg keysAndValues: Pair<Key, List<Value>>
) = remove(keysAndValues.toMap())
suspend inline fun <Key, Value> WriteOneToManyKeyValueRepo<Key, Value>.remove(
suspend inline fun <Key, Value> WriteKeyValuesRepo<Key, Value>.remove(
k: Key,
v: List<Value>
) = remove(mapOf(k to v))
suspend inline fun <Key, Value> WriteOneToManyKeyValueRepo<Key, Value>.remove(
suspend inline fun <Key, Value> WriteKeyValuesRepo<Key, Value>.remove(
k: Key,
vararg v: Value
) = remove(k, v.toList())

View File

@@ -1,22 +1,51 @@
package dev.inmo.micro_utils.repos
import dev.inmo.micro_utils.common.*
@Suppress("UNCHECKED_CAST")
interface MapperRepo<FromKey, FromValue, ToKey, ToValue> {
val keyMapper: SimpleSuspendableMapper<FromKey, ToKey>
get() = simpleSuspendableMapper(
{ it.toInnerKey() },
{ it.toOutKey() }
)
val valueMapper: SimpleSuspendableMapper<FromValue, ToValue>
get() = simpleSuspendableMapper(
{ it.toInnerValue() },
{ it.toOutValue() }
)
suspend fun FromKey.toOutKey() = this as ToKey
suspend fun FromValue.toOutValue() = this as ToValue
suspend fun ToKey.toInnerKey() = this as FromKey
suspend fun ToValue.toInnerValue() = this as FromValue
companion object
}
inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> mapper(
crossinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },
crossinline valueFromToTo: suspend FromValue.() -> ToValue = { this as ToValue },
crossinline keyToToFrom: suspend ToKey.() -> FromKey = { this as FromKey },
crossinline valueToToFrom: suspend ToValue.() -> FromValue = { this as FromValue },
) = object : MapperRepo<FromKey, FromValue, ToKey, ToValue> {
class SimpleMapperRepo<FromKey, FromValue, ToKey, ToValue>(
private val keyFromToTo: suspend FromKey.() -> ToKey,
private val valueFromToTo: suspend FromValue.() -> ToValue,
private val keyToToFrom: suspend ToKey.() -> FromKey,
private val valueToToFrom: suspend ToValue.() -> FromValue
) : MapperRepo<FromKey, FromValue, ToKey, ToValue> {
override suspend fun FromKey.toOutKey(): ToKey = keyFromToTo()
override suspend fun FromValue.toOutValue(): ToValue = valueFromToTo()
override suspend fun ToKey.toInnerKey(): FromKey = keyToToFrom()
override suspend fun ToValue.toInnerValue(): FromValue = valueToToFrom()
}
operator fun <FromKey, FromValue, ToKey, ToValue> MapperRepo.Companion.invoke(
keyFromToTo: suspend FromKey.() -> ToKey,
valueFromToTo: suspend FromValue.() -> ToValue,
keyToToFrom: suspend ToKey.() -> FromKey,
valueToToFrom: suspend ToValue.() -> FromValue
) = SimpleMapperRepo(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom)
inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> mapper(
noinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },
noinline valueFromToTo: suspend FromValue.() -> ToValue = { this as ToValue },
noinline keyToToFrom: suspend ToKey.() -> FromKey = { this as FromKey },
noinline valueToToFrom: suspend ToValue.() -> FromValue = { this as FromValue },
) = MapperRepo(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom)

View File

@@ -4,13 +4,13 @@ import dev.inmo.micro_utils.pagination.Pagination
import dev.inmo.micro_utils.pagination.PaginationResult
import kotlinx.coroutines.flow.Flow
interface ReadStandardCRUDRepo<ObjectType, IdType> : Repo {
interface ReadCRUDRepo<ObjectType, IdType> : Repo {
suspend fun getByPagination(pagination: Pagination): PaginationResult<ObjectType>
suspend fun getById(id: IdType): ObjectType?
suspend fun contains(id: IdType): Boolean
suspend fun count(): Long
}
typealias ReadCRUDRepo<ObjectType, IdType> = ReadStandardCRUDRepo<ObjectType, IdType>
typealias ReadStandardCRUDRepo<ObjectType, IdType> = ReadCRUDRepo<ObjectType, IdType>
typealias UpdatedValuePair<IdType, ValueType> = Pair<IdType, ValueType>
val <IdType> UpdatedValuePair<IdType, *>.id
@@ -18,7 +18,7 @@ val <IdType> UpdatedValuePair<IdType, *>.id
val <ValueType> UpdatedValuePair<*, ValueType>.value
get() = second
interface WriteStandardCRUDRepo<ObjectType, IdType, InputValueType> : Repo {
interface WriteCRUDRepo<ObjectType, IdType, InputValueType> : Repo {
val newObjectsFlow: Flow<ObjectType>
val updatedObjectsFlow: Flow<ObjectType>
val deletedObjectsIdsFlow: Flow<IdType>
@@ -28,18 +28,25 @@ interface WriteStandardCRUDRepo<ObjectType, IdType, InputValueType> : Repo {
suspend fun update(values: List<UpdatedValuePair<IdType, InputValueType>>): List<ObjectType>
suspend fun deleteById(ids: List<IdType>)
}
typealias WriteCRUDRepo<ObjectType, IdType, InputValueType> = WriteStandardCRUDRepo<ObjectType, IdType, InputValueType>
typealias WriteStandardCRUDRepo<ObjectType, IdType, InputValueType> = WriteCRUDRepo<ObjectType, IdType, InputValueType>
suspend fun <ObjectType, IdType, InputValueType> WriteStandardCRUDRepo<ObjectType, IdType, InputValueType>.create(
suspend fun <ObjectType, IdType, InputValueType> WriteCRUDRepo<ObjectType, IdType, InputValueType>.create(
vararg values: InputValueType
): List<ObjectType> = create(values.toList())
suspend fun <ObjectType, IdType, InputValueType> WriteStandardCRUDRepo<ObjectType, IdType, InputValueType>.update(
suspend fun <ObjectType, IdType, InputValueType> WriteCRUDRepo<ObjectType, IdType, InputValueType>.update(
vararg values: UpdatedValuePair<IdType, InputValueType>
): List<ObjectType> = update(values.toList())
suspend fun <ObjectType, IdType, InputValueType> WriteStandardCRUDRepo<ObjectType, IdType, InputValueType>.deleteById(
suspend fun <ObjectType, IdType, InputValueType> WriteCRUDRepo<ObjectType, IdType, InputValueType>.deleteById(
vararg ids: IdType
) = deleteById(ids.toList())
interface StandardCRUDRepo<ObjectType, IdType, InputValueType> : ReadStandardCRUDRepo<ObjectType, IdType>,
WriteStandardCRUDRepo<ObjectType, IdType, InputValueType>
typealias CRUDRepo<ObjectType, IdType, InputValueType> = StandardCRUDRepo<ObjectType, IdType, InputValueType>
interface CRUDRepo<ObjectType, IdType, InputValueType> : ReadCRUDRepo<ObjectType, IdType>,
WriteCRUDRepo<ObjectType, IdType, InputValueType>
typealias StandardCRUDRepo<ObjectType, IdType, InputValueType> = CRUDRepo<ObjectType, IdType, InputValueType>
class DelegateBasedCRUDRepo<ObjectType, IdType, InputValueType>(
readDelegate: ReadCRUDRepo<ObjectType, IdType>,
writeDelegate: WriteCRUDRepo<ObjectType, IdType, InputValueType>
) : CRUDRepo<ObjectType, IdType, InputValueType>,
ReadCRUDRepo<ObjectType, IdType> by readDelegate,
WriteCRUDRepo<ObjectType, IdType, InputValueType> by writeDelegate

View File

@@ -4,7 +4,7 @@ import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.pagination.utils.doAllWithCurrentPaging
import kotlinx.coroutines.flow.Flow
interface ReadStandardKeyValueRepo<Key, Value> : Repo {
interface ReadKeyValueRepo<Key, Value> : Repo {
suspend fun get(k: Key): Value?
suspend fun values(pagination: Pagination, reversed: Boolean = false): PaginationResult<Value>
suspend fun keys(pagination: Pagination, reversed: Boolean = false): PaginationResult<Key>
@@ -12,9 +12,9 @@ interface ReadStandardKeyValueRepo<Key, Value> : Repo {
suspend fun contains(key: Key): Boolean
suspend fun count(): Long
}
typealias ReadKeyValueRepo<Key,Value> = ReadStandardKeyValueRepo<Key, Value>
typealias ReadStandardKeyValueRepo<Key,Value> = ReadKeyValueRepo<Key, Value>
interface WriteStandardKeyValueRepo<Key, Value> : Repo {
interface WriteKeyValueRepo<Key, Value> : Repo {
val onNewValue: Flow<Pair<Key, Value>>
val onValueRemoved: Flow<Key>
@@ -22,25 +22,25 @@ interface WriteStandardKeyValueRepo<Key, Value> : Repo {
suspend fun unset(toUnset: List<Key>)
suspend fun unsetWithValues(toUnset: List<Value>)
}
typealias WriteKeyValueRepo<Key,Value> = WriteStandardKeyValueRepo<Key, Value>
typealias WriteStandardKeyValueRepo<Key,Value> = WriteKeyValueRepo<Key, Value>
suspend inline fun <Key, Value> WriteStandardKeyValueRepo<Key, Value>.set(
suspend inline fun <Key, Value> WriteKeyValueRepo<Key, Value>.set(
vararg toSet: Pair<Key, Value>
) = set(toSet.toMap())
suspend inline fun <Key, Value> WriteStandardKeyValueRepo<Key, Value>.set(
suspend inline fun <Key, Value> WriteKeyValueRepo<Key, Value>.set(
k: Key, v: Value
) = set(k to v)
suspend inline fun <Key, Value> WriteStandardKeyValueRepo<Key, Value>.unset(
suspend inline fun <Key, Value> WriteKeyValueRepo<Key, Value>.unset(
vararg k: Key
) = unset(k.toList())
suspend inline fun <Key, Value> WriteStandardKeyValueRepo<Key, Value>.unsetWithValues(
suspend inline fun <Key, Value> WriteKeyValueRepo<Key, Value>.unsetWithValues(
vararg v: Value
) = unsetWithValues(v.toList())
interface StandardKeyValueRepo<Key, Value> : ReadStandardKeyValueRepo<Key, Value>, WriteStandardKeyValueRepo<Key, Value> {
interface KeyValueRepo<Key, Value> : ReadKeyValueRepo<Key, Value>, WriteKeyValueRepo<Key, Value> {
override suspend fun unsetWithValues(toUnset: List<Value>) = toUnset.forEach { v ->
doAllWithCurrentPaging {
keys(v, it).also {
@@ -48,5 +48,16 @@ interface StandardKeyValueRepo<Key, Value> : ReadStandardKeyValueRepo<Key, Value
}
}
}
suspend fun clear() {
doAllWithCurrentPaging { keys(it).also { unset(it.results) } }
}
}
typealias KeyValueRepo<Key,Value> = StandardKeyValueRepo<Key, Value>
typealias StandardKeyValueRepo<Key,Value> = KeyValueRepo<Key, Value>
class DelegateBasedKeyValueRepo<Key, Value>(
readDelegate: ReadKeyValueRepo<Key, Value>,
writeDelegate: WriteKeyValueRepo<Key, Value>
) : KeyValueRepo<Key, Value>,
ReadKeyValueRepo<Key, Value> by readDelegate,
WriteKeyValueRepo<Key, Value> by writeDelegate

View File

@@ -0,0 +1,130 @@
package dev.inmo.micro_utils.repos.mappers
import dev.inmo.micro_utils.common.*
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
open class MapperReadCRUDRepo<FromId, FromRegistered, ToId, ToRegistered>(
private val to: ReadCRUDRepo<ToRegistered, ToId>,
mapper: MapperRepo<FromId, FromRegistered, ToId, ToRegistered>
) : ReadCRUDRepo<FromRegistered, FromId>, MapperRepo<FromId, FromRegistered, ToId, ToRegistered> by mapper {
override suspend fun getByPagination(
pagination: Pagination
): PaginationResult<FromRegistered> = to.getByPagination(
pagination
).let {
it.changeResultsUnchecked(
it.results.map { it.toInnerValue() }
)
}
override suspend fun count(): Long = to.count()
override suspend fun contains(id: FromId): Boolean = to.contains(id.toOutKey())
override suspend fun getById(id: FromId): FromRegistered? = to.getById(
id.toOutKey()
) ?.toInnerValue()
}
@Suppress("NOTHING_TO_INLINE")
inline fun <FromKey, FromValue, ToKey, ToValue> ReadCRUDRepo<ToValue, ToKey>.withMapper(
mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue>
): ReadCRUDRepo<FromValue, FromKey> = MapperReadCRUDRepo(this, mapper)
@Suppress("NOTHING_TO_INLINE")
inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> ReadCRUDRepo<ToValue, ToKey>.withMapper(
noinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },
noinline valueFromToTo: suspend FromValue.() -> ToValue = { this as ToValue },
noinline keyToToFrom: suspend ToKey.() -> FromKey = { this as FromKey },
noinline valueToToFrom: suspend ToValue.() -> FromValue = { this as FromValue },
): ReadCRUDRepo<FromValue, FromKey> = withMapper(
mapper(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom)
)
open class MapperWriteCRUDRepo<FromId, FromRegistered, FromInput, ToId, ToRegistered, ToInput>(
private val to: WriteCRUDRepo<ToRegistered, ToId, ToInput>,
mapper: MapperRepo<FromId, FromRegistered, ToId, ToRegistered>,
inputMapper: SimpleSuspendableMapper<FromInput, ToInput>
) : WriteCRUDRepo<FromRegistered, FromId, FromInput>,
MapperRepo<FromId, FromRegistered, ToId, ToRegistered> by mapper,
SimpleSuspendableMapper<FromInput, ToInput> by inputMapper {
override val newObjectsFlow: Flow<FromRegistered> = to.newObjectsFlow.map { it.toInnerValue() }
override val updatedObjectsFlow: Flow<FromRegistered> = to.updatedObjectsFlow.map { it.toInnerValue() }
override val deletedObjectsIdsFlow: Flow<FromId> = to.deletedObjectsIdsFlow.map { it.toInnerKey() }
override suspend fun deleteById(ids: List<FromId>) = to.deleteById(ids.map { it.toOutKey() })
override suspend fun update(
values: List<UpdatedValuePair<FromId, FromInput>>
): List<FromRegistered> = to.update(
values.map {
it.first.toOutKey() to convert(it.second)
}
).map { it.toInnerValue() }
override suspend fun update(
id: FromId,
value: FromInput
): FromRegistered? = to.update(id.toOutKey(), convert(value)) ?.toInnerValue()
override suspend fun create(values: List<FromInput>): List<FromRegistered> = to.create(
values.map {
convert(it)
}
).map {
it.toInnerValue()
}
}
@Suppress("NOTHING_TO_INLINE")
inline fun <FromKey, FromValue, FromInput, ToKey, ToValue, ToInput> WriteCRUDRepo<ToValue, ToKey, ToInput>.withMapper(
mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue>,
simpleSuspendableMapper: SimpleSuspendableMapper<FromInput, ToInput>
): WriteCRUDRepo<FromValue, FromKey, FromInput> = MapperWriteCRUDRepo(this, mapper, simpleSuspendableMapper)
@Suppress("NOTHING_TO_INLINE")
inline fun <reified FromKey, reified FromValue, reified FromInput, reified ToKey, reified ToValue, reified ToInput> WriteCRUDRepo<ToValue, ToKey, ToInput>.withMapper(
noinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },
noinline valueFromToTo: suspend FromValue.() -> ToValue = { this as ToValue },
noinline inputFromToTo: suspend FromInput.() -> ToInput = { this as ToInput },
noinline keyToToFrom: suspend ToKey.() -> FromKey = { this as FromKey },
noinline valueToToFrom: suspend ToValue.() -> FromValue = { this as FromValue },
noinline inputToToFrom: suspend ToInput.() -> FromInput = { this as FromInput },
): WriteCRUDRepo<FromValue, FromKey, FromInput> = withMapper(
mapper(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom),
simpleSuspendableMapper({ inputToToFrom(it) }, { inputFromToTo(it) })
)
@Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE")
open class MapperCRUDRepo<FromId, FromRegistered, FromInput, ToId, ToRegistered, ToInput>(
private val to: CRUDRepo<ToRegistered, ToId, ToInput>,
private val mapper: MapperRepo<FromId, FromRegistered, ToId, ToRegistered>,
private val inputMapper: SimpleSuspendableMapper<FromInput, ToInput>
) : CRUDRepo<FromRegistered, FromId, FromInput>,
MapperRepo<FromId, FromRegistered, ToId, ToRegistered> by mapper,
ReadCRUDRepo<FromRegistered, FromId> by MapperReadCRUDRepo(to, mapper),
WriteCRUDRepo<FromRegistered, FromId, FromInput> by MapperWriteCRUDRepo(to, mapper, inputMapper)
@Suppress("NOTHING_TO_INLINE")
inline fun <FromKey, FromValue, FromInput, ToKey, ToValue, ToInput> CRUDRepo<ToValue, ToKey, ToInput>.withMapper(
mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue>,
simpleSuspendableMapper: SimpleSuspendableMapper<FromInput, ToInput>
): CRUDRepo<FromValue, FromKey, FromInput> = MapperCRUDRepo(this, mapper, simpleSuspendableMapper)
@Suppress("NOTHING_TO_INLINE")
inline fun <reified FromKey, reified FromValue, reified FromInput, reified ToKey, reified ToValue, reified ToInput> CRUDRepo<ToValue, ToKey, ToInput>.withMapper(
noinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },
noinline valueFromToTo: suspend FromValue.() -> ToValue = { this as ToValue },
noinline inputFromToTo: suspend FromInput.() -> ToInput = { this as ToInput },
noinline keyToToFrom: suspend ToKey.() -> FromKey = { this as FromKey },
noinline valueToToFrom: suspend ToValue.() -> FromValue = { this as FromValue },
noinline inputToToFrom: suspend ToInput.() -> FromInput = { this as FromInput },
): CRUDRepo<FromValue, FromKey, FromInput> = withMapper(
mapper(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom),
simpleSuspendableMapper({ inputToToFrom(it) }, { inputFromToTo(it) })
)

View File

@@ -6,10 +6,12 @@ import dev.inmo.micro_utils.repos.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
open class MapperReadStandardKeyValueRepo<FromKey, FromValue, ToKey, ToValue>(
private val to: ReadStandardKeyValueRepo<ToKey, ToValue>,
@Deprecated("Renamed", ReplaceWith("MapperReadKeyValueRepo", "dev.inmo.micro_utils.repos.mappers.MapperReadKeyValueRepo"))
typealias MapperReadStandardKeyValueRepo<FromKey, FromValue, ToKey, ToValue> = MapperReadKeyValueRepo<FromKey, FromValue, ToKey, ToValue>
open class MapperReadKeyValueRepo<FromKey, FromValue, ToKey, ToValue>(
private val to: ReadKeyValueRepo<ToKey, ToValue>,
mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue>
) : ReadStandardKeyValueRepo<FromKey, FromValue>, MapperRepo<FromKey, FromValue, ToKey, ToValue> by mapper {
) : ReadKeyValueRepo<FromKey, FromValue>, MapperRepo<FromKey, FromValue, ToKey, ToValue> by mapper {
override suspend fun get(k: FromKey): FromValue? = to.get(
k.toOutKey()
) ?.toInnerValue()
@@ -69,24 +71,26 @@ open class MapperReadStandardKeyValueRepo<FromKey, FromValue, ToKey, ToValue>(
}
@Suppress("NOTHING_TO_INLINE")
inline fun <FromKey, FromValue, ToKey, ToValue> ReadStandardKeyValueRepo<ToKey, ToValue>.withMapper(
inline fun <FromKey, FromValue, ToKey, ToValue> ReadKeyValueRepo<ToKey, ToValue>.withMapper(
mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue>
): ReadStandardKeyValueRepo<FromKey, FromValue> = MapperReadStandardKeyValueRepo(this, mapper)
): ReadKeyValueRepo<FromKey, FromValue> = MapperReadKeyValueRepo(this, mapper)
@Suppress("NOTHING_TO_INLINE")
inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> ReadStandardKeyValueRepo<ToKey, ToValue>.withMapper(
crossinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },
crossinline valueFromToTo: suspend FromValue.() -> ToValue = { this as ToValue },
crossinline keyToToFrom: suspend ToKey.() -> FromKey = { this as FromKey },
crossinline valueToToFrom: suspend ToValue.() -> FromValue = { this as FromValue },
): ReadStandardKeyValueRepo<FromKey, FromValue> = withMapper(
inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> ReadKeyValueRepo<ToKey, ToValue>.withMapper(
noinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },
noinline valueFromToTo: suspend FromValue.() -> ToValue = { this as ToValue },
noinline keyToToFrom: suspend ToKey.() -> FromKey = { this as FromKey },
noinline valueToToFrom: suspend ToValue.() -> FromValue = { this as FromValue },
): ReadKeyValueRepo<FromKey, FromValue> = withMapper(
mapper(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom)
)
open class MapperWriteStandardKeyValueRepo<FromKey, FromValue, ToKey, ToValue>(
private val to: WriteStandardKeyValueRepo<ToKey, ToValue>,
@Deprecated("Renamed", ReplaceWith("MapperWriteKeyValueRepo", "dev.inmo.micro_utils.repos.mappers.MapperWriteKeyValueRepo"))
typealias MapperWriteStandardKeyValueRepo<FromKey, FromValue, ToKey, ToValue> = MapperWriteKeyValueRepo<FromKey, FromValue, ToKey, ToValue>
open class MapperWriteKeyValueRepo<FromKey, FromValue, ToKey, ToValue>(
private val to: WriteKeyValueRepo<ToKey, ToValue>,
mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue>
) : WriteStandardKeyValueRepo<FromKey, FromValue>, MapperRepo<FromKey, FromValue, ToKey, ToValue> by mapper {
) : WriteKeyValueRepo<FromKey, FromValue>, MapperRepo<FromKey, FromValue, ToKey, ToValue> by mapper {
override val onNewValue: Flow<Pair<FromKey, FromValue>> = to.onNewValue.map { (k, v) ->
k.toInnerKey() to v.toInnerValue()
}
@@ -112,40 +116,42 @@ open class MapperWriteStandardKeyValueRepo<FromKey, FromValue, ToKey, ToValue>(
}
@Suppress("NOTHING_TO_INLINE")
inline fun <FromKey, FromValue, ToKey, ToValue> WriteStandardKeyValueRepo<ToKey, ToValue>.withMapper(
inline fun <FromKey, FromValue, ToKey, ToValue> WriteKeyValueRepo<ToKey, ToValue>.withMapper(
mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue>
): WriteStandardKeyValueRepo<FromKey, FromValue> = MapperWriteStandardKeyValueRepo(this, mapper)
): WriteKeyValueRepo<FromKey, FromValue> = MapperWriteKeyValueRepo(this, mapper)
@Suppress("NOTHING_TO_INLINE")
inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> WriteStandardKeyValueRepo<ToKey, ToValue>.withMapper(
crossinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },
crossinline valueFromToTo: suspend FromValue.() -> ToValue = { this as ToValue },
crossinline keyToToFrom: suspend ToKey.() -> FromKey = { this as FromKey },
crossinline valueToToFrom: suspend ToValue.() -> FromValue = { this as FromValue },
): WriteStandardKeyValueRepo<FromKey, FromValue> = withMapper(
inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> WriteKeyValueRepo<ToKey, ToValue>.withMapper(
noinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },
noinline valueFromToTo: suspend FromValue.() -> ToValue = { this as ToValue },
noinline keyToToFrom: suspend ToKey.() -> FromKey = { this as FromKey },
noinline valueToToFrom: suspend ToValue.() -> FromValue = { this as FromValue },
): WriteKeyValueRepo<FromKey, FromValue> = withMapper(
mapper(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom)
)
@Deprecated("Renamed", ReplaceWith("MapperKeyValueRepo", "dev.inmo.micro_utils.repos.mappers.MapperKeyValueRepo"))
typealias MapperStandardKeyValueRepo<FromKey, FromValue, ToKey, ToValue> = MapperKeyValueRepo<FromKey, FromValue, ToKey, ToValue>
@Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE")
open class MapperStandardKeyValueRepo<FromKey, FromValue, ToKey, ToValue>(
private val to: StandardKeyValueRepo<ToKey, ToValue>,
open class MapperKeyValueRepo<FromKey, FromValue, ToKey, ToValue>(
private val to: KeyValueRepo<ToKey, ToValue>,
private val mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue>
) : StandardKeyValueRepo<FromKey, FromValue>,
) : KeyValueRepo<FromKey, FromValue>,
MapperRepo<FromKey, FromValue, ToKey, ToValue> by mapper,
ReadStandardKeyValueRepo<FromKey, FromValue> by MapperReadStandardKeyValueRepo(to, mapper),
WriteStandardKeyValueRepo<FromKey, FromValue> by MapperWriteStandardKeyValueRepo(to, mapper)
ReadKeyValueRepo<FromKey, FromValue> by MapperReadKeyValueRepo(to, mapper),
WriteKeyValueRepo<FromKey, FromValue> by MapperWriteKeyValueRepo(to, mapper)
@Suppress("NOTHING_TO_INLINE")
inline fun <FromKey, FromValue, ToKey, ToValue> StandardKeyValueRepo<ToKey, ToValue>.withMapper(
inline fun <FromKey, FromValue, ToKey, ToValue> KeyValueRepo<ToKey, ToValue>.withMapper(
mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue>
): StandardKeyValueRepo<FromKey, FromValue> = MapperStandardKeyValueRepo(this, mapper)
): KeyValueRepo<FromKey, FromValue> = MapperKeyValueRepo(this, mapper)
@Suppress("NOTHING_TO_INLINE")
inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> StandardKeyValueRepo<ToKey, ToValue>.withMapper(
crossinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },
crossinline valueFromToTo: suspend FromValue.() -> ToValue = { this as ToValue },
crossinline keyToToFrom: suspend ToKey.() -> FromKey = { this as FromKey },
crossinline valueToToFrom: suspend ToValue.() -> FromValue = { this as FromValue },
): StandardKeyValueRepo<FromKey, FromValue> = withMapper(
inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> KeyValueRepo<ToKey, ToValue>.withMapper(
noinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },
noinline valueFromToTo: suspend FromValue.() -> ToValue = { this as ToValue },
noinline keyToToFrom: suspend ToKey.() -> FromKey = { this as FromKey },
noinline valueToToFrom: suspend ToValue.() -> FromValue = { this as FromValue },
): KeyValueRepo<FromKey, FromValue> = withMapper(
mapper(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom)
)

View File

@@ -6,10 +6,12 @@ import dev.inmo.micro_utils.repos.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
open class MapperReadOneToManyKeyValueRepo<FromKey, FromValue, ToKey, ToValue>(
private val to: ReadOneToManyKeyValueRepo<ToKey, ToValue>,
@Deprecated("Renamed", ReplaceWith("MapperReadKeyValuesRepo", "dev.inmo.micro_utils.repos.mappers.MapperReadKeyValuesRepo"))
typealias MapperReadOneToManyKeyValueRepo<FromKey, FromValue, ToKey, ToValue> = MapperReadKeyValuesRepo<FromKey, FromValue, ToKey, ToValue>
open class MapperReadKeyValuesRepo<FromKey, FromValue, ToKey, ToValue>(
private val to: ReadKeyValuesRepo<ToKey, ToValue>,
mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue>
) : ReadOneToManyKeyValueRepo<FromKey, FromValue>, MapperRepo<FromKey, FromValue, ToKey, ToValue> by mapper {
) : ReadKeyValuesRepo<FromKey, FromValue>, MapperRepo<FromKey, FromValue, ToKey, ToValue> by mapper {
override suspend fun get(
k: FromKey,
pagination: Pagination,
@@ -67,24 +69,26 @@ open class MapperReadOneToManyKeyValueRepo<FromKey, FromValue, ToKey, ToValue>(
}
@Suppress("NOTHING_TO_INLINE")
inline fun <FromKey, FromValue, ToKey, ToValue> ReadOneToManyKeyValueRepo<ToKey, ToValue>.withMapper(
inline fun <FromKey, FromValue, ToKey, ToValue> ReadKeyValuesRepo<ToKey, ToValue>.withMapper(
mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue>
): ReadOneToManyKeyValueRepo<FromKey, FromValue> = MapperReadOneToManyKeyValueRepo(this, mapper)
): ReadKeyValuesRepo<FromKey, FromValue> = MapperReadKeyValuesRepo(this, mapper)
@Suppress("NOTHING_TO_INLINE")
inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> ReadOneToManyKeyValueRepo<ToKey, ToValue>.withMapper(
crossinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },
crossinline valueFromToTo: suspend FromValue.() -> ToValue = { this as ToValue },
crossinline keyToToFrom: suspend ToKey.() -> FromKey = { this as FromKey },
crossinline valueToToFrom: suspend ToValue.() -> FromValue = { this as FromValue },
): ReadOneToManyKeyValueRepo<FromKey, FromValue> = withMapper(
inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> ReadKeyValuesRepo<ToKey, ToValue>.withMapper(
noinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },
noinline valueFromToTo: suspend FromValue.() -> ToValue = { this as ToValue },
noinline keyToToFrom: suspend ToKey.() -> FromKey = { this as FromKey },
noinline valueToToFrom: suspend ToValue.() -> FromValue = { this as FromValue },
): ReadKeyValuesRepo<FromKey, FromValue> = withMapper(
mapper(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom)
)
open class MapperWriteOneToManyKeyValueRepo<FromKey, FromValue, ToKey, ToValue>(
private val to: WriteOneToManyKeyValueRepo<ToKey, ToValue>,
@Deprecated("Renamed", ReplaceWith("MapperWriteKeyValuesRepo", "dev.inmo.micro_utils.repos.mappers.MapperWriteKeyValuesRepo"))
typealias MapperWriteOneToManyKeyValueRepo<FromKey, FromValue, ToKey, ToValue> = MapperWriteKeyValuesRepo<FromKey, FromValue, ToKey, ToValue>
open class MapperWriteKeyValuesRepo<FromKey, FromValue, ToKey, ToValue>(
private val to: WriteKeyValuesRepo<ToKey, ToValue>,
mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue>
) : WriteOneToManyKeyValueRepo<FromKey, FromValue>, MapperRepo<FromKey, FromValue, ToKey, ToValue> by mapper {
) : WriteKeyValuesRepo<FromKey, FromValue>, MapperRepo<FromKey, FromValue, ToKey, ToValue> by mapper {
override val onNewValue: Flow<Pair<FromKey, FromValue>> = to.onNewValue.map { (k, v) ->
k.toInnerKey() to v.toInnerValue()
}
@@ -118,40 +122,42 @@ open class MapperWriteOneToManyKeyValueRepo<FromKey, FromValue, ToKey, ToValue>(
}
@Suppress("NOTHING_TO_INLINE")
inline fun <FromKey, FromValue, ToKey, ToValue> WriteOneToManyKeyValueRepo<ToKey, ToValue>.withMapper(
inline fun <FromKey, FromValue, ToKey, ToValue> WriteKeyValuesRepo<ToKey, ToValue>.withMapper(
mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue>
): WriteOneToManyKeyValueRepo<FromKey, FromValue> = MapperWriteOneToManyKeyValueRepo(this, mapper)
): WriteKeyValuesRepo<FromKey, FromValue> = MapperWriteKeyValuesRepo(this, mapper)
@Suppress("NOTHING_TO_INLINE")
inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> WriteOneToManyKeyValueRepo<ToKey, ToValue>.withMapper(
crossinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },
crossinline valueFromToTo: suspend FromValue.() -> ToValue = { this as ToValue },
crossinline keyToToFrom: suspend ToKey.() -> FromKey = { this as FromKey },
crossinline valueToToFrom: suspend ToValue.() -> FromValue = { this as FromValue },
): WriteOneToManyKeyValueRepo<FromKey, FromValue> = withMapper(
inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> WriteKeyValuesRepo<ToKey, ToValue>.withMapper(
noinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },
noinline valueFromToTo: suspend FromValue.() -> ToValue = { this as ToValue },
noinline keyToToFrom: suspend ToKey.() -> FromKey = { this as FromKey },
noinline valueToToFrom: suspend ToValue.() -> FromValue = { this as FromValue },
): WriteKeyValuesRepo<FromKey, FromValue> = withMapper(
mapper(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom)
)
@Deprecated("Renamed", ReplaceWith("MapperKeyValuesRepo", "dev.inmo.micro_utils.repos.mappers.MapperKeyValuesRepo"))
typealias MapperOneToManyKeyValueRepo<FromKey, FromValue, ToKey, ToValue> = MapperKeyValuesRepo<FromKey, FromValue, ToKey, ToValue>
@Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE")
open class MapperOneToManyKeyValueRepo<FromKey, FromValue, ToKey, ToValue>(
private val to: OneToManyKeyValueRepo<ToKey, ToValue>,
open class MapperKeyValuesRepo<FromKey, FromValue, ToKey, ToValue>(
private val to: KeyValuesRepo<ToKey, ToValue>,
mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue>
) : OneToManyKeyValueRepo<FromKey, FromValue>,
) : KeyValuesRepo<FromKey, FromValue>,
MapperRepo<FromKey, FromValue, ToKey, ToValue> by mapper,
ReadOneToManyKeyValueRepo<FromKey, FromValue> by MapperReadOneToManyKeyValueRepo(to, mapper),
WriteOneToManyKeyValueRepo<FromKey, FromValue> by MapperWriteOneToManyKeyValueRepo(to, mapper)
ReadKeyValuesRepo<FromKey, FromValue> by MapperReadKeyValuesRepo(to, mapper),
WriteKeyValuesRepo<FromKey, FromValue> by MapperWriteKeyValuesRepo(to, mapper)
@Suppress("NOTHING_TO_INLINE")
inline fun <FromKey, FromValue, ToKey, ToValue> OneToManyKeyValueRepo<ToKey, ToValue>.withMapper(
inline fun <FromKey, FromValue, ToKey, ToValue> KeyValuesRepo<ToKey, ToValue>.withMapper(
mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue>
): OneToManyKeyValueRepo<FromKey, FromValue> = MapperOneToManyKeyValueRepo(this, mapper)
): KeyValuesRepo<FromKey, FromValue> = MapperKeyValuesRepo(this, mapper)
@Suppress("NOTHING_TO_INLINE")
inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> OneToManyKeyValueRepo<ToKey, ToValue>.withMapper(
crossinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },
crossinline valueFromToTo: suspend FromValue.() -> ToValue = { this as ToValue },
crossinline keyToToFrom: suspend ToKey.() -> FromKey = { this as FromKey },
crossinline valueToToFrom: suspend ToValue.() -> FromValue = { this as FromValue },
): OneToManyKeyValueRepo<FromKey, FromValue> = withMapper(
inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> KeyValuesRepo<ToKey, ToValue>.withMapper(
noinline keyFromToTo: suspend FromKey.() -> ToKey = { this as ToKey },
noinline valueFromToTo: suspend FromValue.() -> ToValue = { this as ToValue },
noinline keyToToFrom: suspend ToKey.() -> FromKey = { this as FromKey },
noinline valueToToFrom: suspend ToValue.() -> FromValue = { this as FromValue },
): KeyValuesRepo<FromKey, FromValue> = withMapper(
mapper(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom)
)

View File

@@ -1,11 +1,10 @@
package dev.inmo.micro_utils.repos.pagination
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.pagination.utils.doForAllWithNextPaging
import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging
import dev.inmo.micro_utils.repos.ReadStandardCRUDRepo
import dev.inmo.micro_utils.repos.ReadCRUDRepo
suspend inline fun <T, ID, REPO : ReadStandardCRUDRepo<T, ID>> REPO.getAll(
suspend inline fun <T, ID, REPO : ReadCRUDRepo<T, ID>> REPO.getAll(
@Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE")
crossinline methodCaller: suspend REPO.(Pagination) -> PaginationResult<T>
): List<T> = getAllWithNextPaging {

View File

@@ -2,9 +2,9 @@ package dev.inmo.micro_utils.repos.pagination
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging
import dev.inmo.micro_utils.repos.ReadStandardKeyValueRepo
import dev.inmo.micro_utils.repos.ReadKeyValueRepo
suspend inline fun <Key, Value, REPO : ReadStandardKeyValueRepo<Key, Value>> REPO.getAll(
suspend inline fun <Key, Value, REPO : ReadKeyValueRepo<Key, Value>> REPO.getAll(
@Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE")
crossinline methodCaller: suspend REPO.(Pagination) -> PaginationResult<Key>
): List<Pair<Key, Value>> = getAllWithNextPaging {

View File

@@ -2,9 +2,9 @@ package dev.inmo.micro_utils.repos.pagination
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging
import dev.inmo.micro_utils.repos.ReadOneToManyKeyValueRepo
import dev.inmo.micro_utils.repos.ReadKeyValuesRepo
suspend inline fun <Key, Value, REPO : ReadOneToManyKeyValueRepo<Key, Value>> REPO.getAll(
suspend inline fun <Key, Value, REPO : ReadKeyValuesRepo<Key, Value>> REPO.getAll(
@Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE")
crossinline methodCaller: suspend REPO.(Pagination) -> PaginationResult<Key>
): List<Pair<Key, List<Value>>> = getAllWithNextPaging {

View File

@@ -1,10 +1,10 @@
package dev.inmo.micro_utils.repos.versions
import dev.inmo.micro_utils.repos.StandardKeyValueRepo
import dev.inmo.micro_utils.repos.KeyValueRepo
import dev.inmo.micro_utils.repos.set
class KeyValueBasedVersionsRepoProxy<T>(
private val keyValueStore: StandardKeyValueRepo<String, Int>,
private val keyValueStore: KeyValueRepo<String, Int>,
override val database: T
) : StandardVersionsRepoProxy<T> {
override suspend fun getTableVersion(tableName: String): Int? = keyValueStore.get(tableName)

View File

@@ -13,9 +13,12 @@ import java.nio.file.StandardWatchEventKinds.*
private inline val String.isAbsolute
get() = startsWith(File.separator)
class FileReadStandardKeyValueRepo(
@Deprecated("Renamed", ReplaceWith("FileReadKeyValueRepo", "dev.inmo.micro_utils.repos.FileReadKeyValueRepo"))
typealias FileReadStandardKeyValueRepo = FileReadKeyValueRepo
class FileReadKeyValueRepo(
private val folder: File
) : ReadStandardKeyValueRepo<String, File> {
) : ReadKeyValueRepo<String, File> {
init {
folder.mkdirs()
}
@@ -31,7 +34,7 @@ class FileReadStandardKeyValueRepo(
override suspend fun values(pagination: Pagination, reversed: Boolean): PaginationResult<File> {
val count = count()
val resultPagination = if (reversed) pagination.reverse(count) else pagination
val filesPaths = folder.list() ?.copyOfRange(resultPagination.firstIndex, resultPagination.lastIndex) ?: return emptyPaginationResult()
val filesPaths = folder.list() ?.copyOfRange(resultPagination.firstIndex, resultPagination.lastIndexExclusive) ?: return emptyPaginationResult()
if (reversed) {
filesPaths.reverse()
}
@@ -44,7 +47,7 @@ class FileReadStandardKeyValueRepo(
override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<String> {
val count = count()
val resultPagination = if (reversed) pagination.reverse(count) else pagination
val filesPaths = folder.list() ?.copyOfRange(resultPagination.firstIndex, resultPagination.lastIndex) ?: return emptyPaginationResult()
val filesPaths = folder.list() ?.copyOfRange(resultPagination.firstIndex, resultPagination.lastIndexExclusive) ?: return emptyPaginationResult()
if (reversed) {
filesPaths.reverse()
}
@@ -79,21 +82,26 @@ class FileReadStandardKeyValueRepo(
override suspend fun count(): Long = folder.list() ?.size ?.toLong() ?: 0L
}
@Deprecated("Renamed", ReplaceWith("FileWriteKeyValueRepo", "dev.inmo.micro_utils.repos.FileWriteKeyValueRepo"))
typealias FileWriteStandardKeyValueRepo = FileWriteKeyValueRepo
/**
* Files watching will not correctly works on Android with version of API lower than API 26
*/
@Warning("Files watching will not correctly works on Android Platform with version of API lower than API 26")
class FileWriteStandardKeyValueRepo(
class FileWriteKeyValueRepo(
private val folder: File,
filesChangedProcessingScope: CoroutineScope? = null
) : WriteStandardKeyValueRepo<String, File> {
) : WriteKeyValueRepo<String, File> {
private val _onNewValue = MutableSharedFlow<Pair<String, File>>()
override val onNewValue: Flow<Pair<String, File>> = _onNewValue.asSharedFlow()
private val _onValueRemoved = MutableSharedFlow<String>()
override val onValueRemoved: Flow<String> = _onValueRemoved.asSharedFlow()
init {
folder.mkdirs()
if (!folder.mkdirs() && !folder.exists()) {
error("Unable to create folder ${folder.absolutePath}")
}
filesChangedProcessingScope ?.let {
it.launch {
try {
@@ -138,15 +146,17 @@ class FileWriteStandardKeyValueRepo(
}
override suspend fun set(toSet: Map<String, File>) {
supervisorScope {
toSet.map { (filename, fileSource) ->
launch {
val file = File(folder, filename)
val scope = CoroutineScope(currentCoroutineContext())
toSet.map { (filename, fileSource) ->
scope.launch {
val file = File(folder, filename)
file.delete()
fileSource.copyTo(file, overwrite = true)
_onNewValue.emit(filename to file)
file.delete()
fileSource.copyTo(file, overwrite = true)
if (!file.exists()) {
error("Can't create file $file with new content")
}
_onNewValue.emit(filename to file)
}
}.joinAll()
}
@@ -174,12 +184,15 @@ class FileWriteStandardKeyValueRepo(
}
}
@Deprecated("Renamed", ReplaceWith("FileKeyValueRepo", "dev.inmo.micro_utils.repos.FileKeyValueRepo"))
typealias FileStandardKeyValueRepo = FileKeyValueRepo
@Warning("Files watching will not correctly works on Android Platform with version of API lower than API 26")
@Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE")
class FileStandardKeyValueRepo(
class FileKeyValueRepo(
folder: File,
filesChangedProcessingScope: CoroutineScope? = null
) : StandardKeyValueRepo<String, File>,
WriteStandardKeyValueRepo<String, File> by FileWriteStandardKeyValueRepo(folder, filesChangedProcessingScope),
ReadStandardKeyValueRepo<String, File> by FileReadStandardKeyValueRepo(folder) {
) : KeyValueRepo<String, File>,
WriteKeyValueRepo<String, File> by FileWriteKeyValueRepo(folder, filesChangedProcessingScope),
ReadKeyValueRepo<String, File> by FileReadKeyValueRepo(folder) {
}

View File

@@ -12,7 +12,7 @@ val <T> T.asId: String
abstract class AbstractAndroidCRUDRepo<ObjectType, IdType>(
protected val helper: StandardSQLHelper
) : ReadStandardCRUDRepo<ObjectType, IdType> {
) : ReadCRUDRepo<ObjectType, IdType> {
protected abstract val tableName: String
protected abstract val idColumnName: String
protected abstract suspend fun Cursor.toObject(): ObjectType

View File

@@ -9,9 +9,9 @@ abstract class AbstractMutableAndroidCRUDRepo<ObjectType, IdType, InputValueType
helper: StandardSQLHelper,
replyInFlows: Int = 0,
extraBufferCapacityInFlows: Int = 64
) : WriteStandardCRUDRepo<ObjectType, IdType, InputValueType>,
) : WriteCRUDRepo<ObjectType, IdType, InputValueType>,
AbstractAndroidCRUDRepo<ObjectType, IdType>(helper),
StandardCRUDRepo<ObjectType, IdType, InputValueType> {
CRUDRepo<ObjectType, IdType, InputValueType> {
protected val newObjectsChannel = MutableSharedFlow<ObjectType>(replyInFlows, extraBufferCapacityInFlows)
protected val updateObjectsChannel = MutableSharedFlow<ObjectType>(replyInFlows, extraBufferCapacityInFlows)
protected val deleteObjectsIdsChannel = MutableSharedFlow<IdType>(replyInFlows, extraBufferCapacityInFlows)

View File

@@ -7,7 +7,7 @@ import dev.inmo.micro_utils.pagination.Pagination
import dev.inmo.micro_utils.pagination.PaginationResult
import dev.inmo.micro_utils.pagination.utils.paginate
import dev.inmo.micro_utils.pagination.utils.reverse
import dev.inmo.micro_utils.repos.StandardKeyValueRepo
import dev.inmo.micro_utils.repos.KeyValueRepo
import kotlinx.coroutines.flow.*
private val cache = HashMap<String, KeyValueStore<*>>()
@@ -15,7 +15,7 @@ private val cache = HashMap<String, KeyValueStore<*>>()
fun <T : Any> Context.keyValueStore(
name: String = "default",
cacheValues: Boolean = false
): StandardKeyValueRepo<String, T> {
): KeyValueRepo<String, T> {
@Suppress("UNCHECKED_CAST")
return cache.getOrPut(name) {
KeyValueStore<T>(this, name, cacheValues)
@@ -26,7 +26,7 @@ class KeyValueStore<T : Any> internal constructor (
c: Context,
preferencesName: String,
useCache: Boolean = false
) : SharedPreferences.OnSharedPreferenceChangeListener, StandardKeyValueRepo<String, T> {
) : SharedPreferences.OnSharedPreferenceChangeListener, KeyValueRepo<String, T> {
private val sharedPreferences = c.getSharedPreferences(preferencesName, Context.MODE_PRIVATE)
private val cachedData = if (useCache) {

View File

@@ -9,7 +9,6 @@ import dev.inmo.micro_utils.repos.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.KSerializer
import kotlinx.serialization.json.Json
@@ -26,7 +25,7 @@ class OneToManyAndroidRepo<Key, Value>(
private val keyFromString: String.() -> Key,
private val valueFromString: String.() -> Value,
private val helper: SQLiteOpenHelper
) : OneToManyKeyValueRepo<Key, Value> {
) : KeyValuesRepo<Key, Value> {
private val _onNewValue: MutableSharedFlow<Pair<Key, Value>> = MutableSharedFlow()
override val onNewValue: Flow<Pair<Key, Value>> = _onNewValue.asSharedFlow()
private val _onValueRemoved: MutableSharedFlow<Pair<Key, Value>> = MutableSharedFlow()

View File

@@ -4,13 +4,11 @@ package dev.inmo.micro_utils.repos.versions
import android.content.Context
import android.database.sqlite.SQLiteOpenHelper
import androidx.core.content.contentValuesOf
import dev.inmo.micro_utils.repos.*
import dev.inmo.micro_utils.repos.keyvalue.keyValueStore
import kotlinx.coroutines.runBlocking
/**
* Will create [VersionsRepo] based on [T], but versions will be stored in [StandardKeyValueRepo]
* Will create [VersionsRepo] based on [T], but versions will be stored in [KeyValueRepo]
*
* @receiver Will be used to create [KeyValueBasedVersionsRepoProxy] via [keyValueStore] and pass it to [StandardVersionsRepo]
*
@@ -26,9 +24,9 @@ inline fun <T> Context.versionsKeyValueRepo(
)
)
/**
* Will create [VersionsRepo] based on [SQLiteOpenHelper], but versions will be stored in [StandardKeyValueRepo]
* Will create [VersionsRepo] based on [SQLiteOpenHelper], but versions will be stored in [KeyValueRepo]
*
* @receiver Will be used to create [StandardKeyValueRepo] via [keyValueStore] and pass it to [StandardVersionsRepo]
* @receiver Will be used to create [KeyValueRepo] via [keyValueStore] and pass it to [StandardVersionsRepo]
*
* @see [keyValueStore]
*/
@@ -37,9 +35,9 @@ inline fun Context.versionsKeyValueRepoForSQL(
) = versionsKeyValueRepo(database)
/**
* Will create [VersionsRepo] based on [SQLiteOpenHelper], but versions will be stored in [StandardKeyValueRepo]
* Will create [VersionsRepo] based on [SQLiteOpenHelper], but versions will be stored in [KeyValueRepo]
*
* @param context Will be used to create [StandardKeyValueRepo] via [keyValueStore] and pass it to [StandardVersionsRepo]
* @param context Will be used to create [KeyValueRepo] via [keyValueStore] and pass it to [StandardVersionsRepo]
*
* @see [keyValueStore]
*/

View File

@@ -1,6 +1,6 @@
package dev.inmo.micro_utils.repos.exposed
import dev.inmo.micro_utils.repos.StandardCRUDRepo
import dev.inmo.micro_utils.repos.CRUDRepo
abstract class AbstractExposedCRUDRepo<ObjectType, IdType, InputValueType>(
flowsChannelsSize: Int = 0,
@@ -11,4 +11,4 @@ abstract class AbstractExposedCRUDRepo<ObjectType, IdType, InputValueType>(
tableName
),
ExposedCRUDRepo<ObjectType, IdType>,
StandardCRUDRepo<ObjectType, IdType, InputValueType>
CRUDRepo<ObjectType, IdType, InputValueType>

View File

@@ -1,14 +1,14 @@
package dev.inmo.micro_utils.repos.exposed
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.ReadStandardCRUDRepo
import dev.inmo.micro_utils.repos.ReadCRUDRepo
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction
abstract class AbstractExposedReadCRUDRepo<ObjectType, IdType>(
tableName: String
) :
ReadStandardCRUDRepo<ObjectType, IdType>,
ReadCRUDRepo<ObjectType, IdType>,
ExposedCRUDRepo<ObjectType, IdType>,
Table(tableName)
{

View File

@@ -1,7 +1,7 @@
package dev.inmo.micro_utils.repos.exposed
import dev.inmo.micro_utils.repos.UpdatedValuePair
import dev.inmo.micro_utils.repos.WriteStandardCRUDRepo
import dev.inmo.micro_utils.repos.WriteCRUDRepo
import kotlinx.coroutines.flow.*
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.statements.InsertStatement
@@ -15,7 +15,7 @@ abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
) :
AbstractExposedReadCRUDRepo<ObjectType, IdType>(tableName),
ExposedCRUDRepo<ObjectType, IdType>,
WriteStandardCRUDRepo<ObjectType, IdType, InputValueType>
WriteCRUDRepo<ObjectType, IdType, InputValueType>
{
protected val _newObjectsFlow = MutableSharedFlow<ObjectType>(replyCacheInFlows, flowsChannelsSize)
protected val _updatedObjectsFlow = MutableSharedFlow<ObjectType>(replyCacheInFlows, flowsChannelsSize)

View File

@@ -1,9 +1,7 @@
package dev.inmo.micro_utils.repos.exposed.keyvalue
import dev.inmo.micro_utils.repos.StandardKeyValueRepo
import dev.inmo.micro_utils.repos.KeyValueRepo
import dev.inmo.micro_utils.repos.exposed.ColumnAllocator
import dev.inmo.micro_utils.repos.exposed.initTable
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.*
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction
@@ -13,7 +11,7 @@ open class ExposedKeyValueRepo<Key, Value>(
keyColumnAllocator: ColumnAllocator<Key>,
valueColumnAllocator: ColumnAllocator<Value>,
tableName: String? = null
) : StandardKeyValueRepo<Key, Value>, ExposedReadKeyValueRepo<Key, Value>(
) : KeyValueRepo<Key, Value>, ExposedReadKeyValueRepo<Key, Value>(
database,
keyColumnAllocator,
valueColumnAllocator,

View File

@@ -1,7 +1,7 @@
package dev.inmo.micro_utils.repos.exposed.keyvalue
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.ReadStandardKeyValueRepo
import dev.inmo.micro_utils.repos.ReadKeyValueRepo
import dev.inmo.micro_utils.repos.exposed.*
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction
@@ -11,7 +11,7 @@ open class ExposedReadKeyValueRepo<Key, Value>(
keyColumnAllocator: ColumnAllocator<Key>,
valueColumnAllocator: ColumnAllocator<Value>,
tableName: String? = null
) : ReadStandardKeyValueRepo<Key, Value>, ExposedRepo, Table(tableName ?: "") {
) : ReadKeyValueRepo<Key, Value>, ExposedRepo, Table(tableName ?: "") {
val keyColumn: Column<Key> = keyColumnAllocator()
val valueColumn: Column<Value> = valueColumnAllocator()
override val primaryKey: PrimaryKey = PrimaryKey(keyColumn, valueColumn)

View File

@@ -1,18 +1,18 @@
package dev.inmo.micro_utils.repos.exposed.onetomany
import dev.inmo.micro_utils.repos.OneToManyKeyValueRepo
import dev.inmo.micro_utils.repos.KeyValuesRepo
import dev.inmo.micro_utils.repos.exposed.ColumnAllocator
import kotlinx.coroutines.flow.*
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction
typealias ExposedKeyValuesRepo<Key, Value> = ExposedOneToManyKeyValueRepo<Key, Value>
open class ExposedOneToManyKeyValueRepo<Key, Value>(
typealias ExposedOneToManyKeyValueRepo1<Key, Value> = ExposedKeyValuesRepo<Key, Value>
open class ExposedKeyValuesRepo<Key, Value>(
database: Database,
keyColumnAllocator: ColumnAllocator<Key>,
valueColumnAllocator: ColumnAllocator<Value>,
tableName: String? = null
) : OneToManyKeyValueRepo<Key, Value>, ExposedReadOneToManyKeyValueRepo<Key, Value>(
) : KeyValuesRepo<Key, Value>, ExposedReadKeyValuesRepo<Key, Value>(
database,
keyColumnAllocator,
valueColumnAllocator,

View File

@@ -1,20 +1,19 @@
package dev.inmo.micro_utils.repos.exposed.onetomany
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.ReadOneToManyKeyValueRepo
import dev.inmo.micro_utils.repos.ReadKeyValuesRepo
import dev.inmo.micro_utils.repos.exposed.*
import dev.inmo.micro_utils.repos.exposed.keyvalue.ExposedReadKeyValueRepo
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction
typealias ExposedReadKeyValuesRepo<Key, Value> = ExposedReadOneToManyKeyValueRepo<Key, Value>
typealias ExposedReadOneToManyKeyValueRepo<Key, Value> = ExposedReadKeyValuesRepo<Key, Value>
open class ExposedReadOneToManyKeyValueRepo<Key, Value>(
open class ExposedReadKeyValuesRepo<Key, Value>(
override val database: Database,
keyColumnAllocator: ColumnAllocator<Key>,
valueColumnAllocator: ColumnAllocator<Value>,
tableName: String? = null
) : ReadOneToManyKeyValueRepo<Key, Value>, ExposedRepo, Table(tableName ?: "") {
) : ReadKeyValuesRepo<Key, Value>, ExposedRepo, Table(tableName ?: "") {
val keyColumn: Column<Key> = keyColumnAllocator()
val valueColumn: Column<Value> = valueColumnAllocator()

View File

@@ -5,7 +5,7 @@ import kotlinx.coroutines.flow.*
class ReadMapCRUDRepo<ObjectType, IdType>(
private val map: Map<IdType, ObjectType> = emptyMap()
) : ReadStandardCRUDRepo<ObjectType, IdType> {
) : ReadCRUDRepo<ObjectType, IdType> {
override suspend fun getByPagination(pagination: Pagination): PaginationResult<ObjectType> {
return map.keys.drop(pagination.firstIndex).take(pagination.size).mapNotNull {
map[it]
@@ -24,7 +24,7 @@ class ReadMapCRUDRepo<ObjectType, IdType>(
abstract class WriteMapCRUDRepo<ObjectType, IdType, InputValueType>(
protected val map: MutableMap<IdType, ObjectType> = mutableMapOf()
) : WriteStandardCRUDRepo<ObjectType, IdType, InputValueType> {
) : WriteCRUDRepo<ObjectType, IdType, InputValueType> {
protected val _newObjectsFlow: MutableSharedFlow<ObjectType> = MutableSharedFlow()
override val newObjectsFlow: Flow<ObjectType> = _newObjectsFlow.asSharedFlow()
protected val _updatedObjectsFlow: MutableSharedFlow<ObjectType> = MutableSharedFlow()
@@ -68,25 +68,30 @@ abstract class WriteMapCRUDRepo<ObjectType, IdType, InputValueType>(
abstract class MapCRUDRepo<ObjectType, IdType, InputValueType>(
map: MutableMap<IdType, ObjectType>
) : StandardCRUDRepo<ObjectType, IdType, InputValueType>,
ReadStandardCRUDRepo<ObjectType, IdType> by ReadMapCRUDRepo(map),
) : CRUDRepo<ObjectType, IdType, InputValueType>,
ReadCRUDRepo<ObjectType, IdType> by ReadMapCRUDRepo(map),
WriteMapCRUDRepo<ObjectType, IdType, InputValueType>(map)
fun <ObjectType, IdType, InputValueType> MapCRUDRepo(
map: MutableMap<IdType, ObjectType>,
updateCallback: suspend (newValue: InputValueType, id: IdType, old: ObjectType) -> ObjectType,
createCallback: suspend (newValue: InputValueType) -> Pair<IdType, ObjectType>
updateCallback: suspend MutableMap<IdType, ObjectType>.(newValue: InputValueType, id: IdType, old: ObjectType) -> ObjectType,
createCallback: suspend MutableMap<IdType, ObjectType>.(newValue: InputValueType) -> Pair<IdType, ObjectType>
) = object : MapCRUDRepo<ObjectType, IdType, InputValueType>(map) {
override suspend fun updateObject(
newValue: InputValueType,
id: IdType,
old: ObjectType
): ObjectType = updateCallback(newValue, id, old)
): ObjectType = map.updateCallback(newValue, id, old)
override suspend fun createObject(newValue: InputValueType): Pair<IdType, ObjectType> = createCallback(newValue)
override suspend fun createObject(newValue: InputValueType): Pair<IdType, ObjectType> = map.createCallback(newValue)
}
fun <ObjectType, IdType, InputValueType> MapCRUDRepo(
updateCallback: suspend MutableMap<IdType, ObjectType>.(newValue: InputValueType, id: IdType, old: ObjectType) -> ObjectType,
createCallback: suspend MutableMap<IdType, ObjectType>.(newValue: InputValueType) -> Pair<IdType, ObjectType>
) = MapCRUDRepo(mutableMapOf(), updateCallback, createCallback)
fun <ObjectType, IdType, InputValueType> MutableMap<IdType, ObjectType>.asCrudRepo(
updateCallback: suspend (newValue: InputValueType, id: IdType, old: ObjectType) -> ObjectType,
createCallback: suspend (newValue: InputValueType) -> Pair<IdType, ObjectType>
updateCallback: suspend MutableMap<IdType, ObjectType>.(newValue: InputValueType, id: IdType, old: ObjectType) -> ObjectType,
createCallback: suspend MutableMap<IdType, ObjectType>.(newValue: InputValueType) -> Pair<IdType, ObjectType>
) = MapCRUDRepo(this, updateCallback, createCallback)

View File

@@ -9,7 +9,7 @@ import kotlinx.coroutines.flow.MutableSharedFlow
class ReadMapKeyValueRepo<Key, Value>(
protected val map: Map<Key, Value> = emptyMap()
) : ReadStandardKeyValueRepo<Key, Value> {
) : ReadKeyValueRepo<Key, Value> {
override suspend fun get(k: Key): Value? = map[k]
override suspend fun values(
@@ -58,7 +58,7 @@ class ReadMapKeyValueRepo<Key, Value>(
class WriteMapKeyValueRepo<Key, Value>(
private val map: MutableMap<Key, Value> = mutableMapOf()
) : WriteStandardKeyValueRepo<Key, Value> {
) : WriteKeyValueRepo<Key, Value> {
private val _onNewValue: MutableSharedFlow<Pair<Key, Value>> = MutableSharedFlow()
override val onNewValue: Flow<Pair<Key, Value>>
get() = _onNewValue
@@ -78,19 +78,19 @@ class WriteMapKeyValueRepo<Key, Value>(
}
override suspend fun unsetWithValues(toUnset: List<Value>) {
map.forEach {
if (it.value in toUnset) {
map.remove(it.key)
_onValueRemoved.emit(it.key)
}
map.mapNotNull { (k, v) ->
k.takeIf { v in toUnset }
}.forEach {
map.remove(it)
_onValueRemoved.emit(it)
}
}
}
class MapKeyValueRepo<Key, Value>(
private val map: MutableMap<Key, Value> = mutableMapOf()
) : StandardKeyValueRepo<Key, Value>,
ReadStandardKeyValueRepo<Key, Value> by ReadMapKeyValueRepo(map),
WriteStandardKeyValueRepo<Key, Value> by WriteMapKeyValueRepo(map)
) : KeyValueRepo<Key, Value>,
ReadKeyValueRepo<Key, Value> by ReadMapKeyValueRepo(map),
WriteKeyValueRepo<Key, Value> by WriteMapKeyValueRepo(map)
fun <K, V> MutableMap<K, V>.asKeyValueRepo(): StandardKeyValueRepo<K, V> = MapKeyValueRepo(this)
fun <K, V> MutableMap<K, V>.asKeyValueRepo(): KeyValueRepo<K, V> = MapKeyValueRepo(this)

View File

@@ -5,9 +5,11 @@ import dev.inmo.micro_utils.pagination.utils.paginate
import dev.inmo.micro_utils.pagination.utils.reverse
import kotlinx.coroutines.flow.*
class MapReadOneToManyKeyValueRepo<Key, Value>(
@Deprecated("Renamed", ReplaceWith("MapReadKeyValuesRepo", "dev.inmo.micro_utils.repos.MapReadKeyValuesRepo"))
typealias MapReadOneToManyKeyValueRepo<Key, Value> = MapReadKeyValuesRepo<Key, Value>
class MapReadKeyValuesRepo<Key, Value>(
private val map: Map<Key, List<Value>> = emptyMap()
) : ReadOneToManyKeyValueRepo<Key, Value> {
) : ReadKeyValuesRepo<Key, Value> {
override suspend fun get(k: Key, pagination: Pagination, reversed: Boolean): PaginationResult<Value> {
val list = map[k] ?: return emptyPaginationResult()
@@ -53,9 +55,11 @@ class MapReadOneToManyKeyValueRepo<Key, Value>(
override suspend fun count(): Long = map.size.toLong()
}
class MapWriteOneToManyKeyValueRepo<Key, Value>(
@Deprecated("Renamed", ReplaceWith("MapWriteKeyValuesRepo", "dev.inmo.micro_utils.repos.MapWriteKeyValuesRepo"))
typealias MapWriteOneToManyKeyValueRepo<Key, Value> = MapWriteKeyValuesRepo<Key, Value>
class MapWriteKeyValuesRepo<Key, Value>(
private val map: MutableMap<Key, MutableList<Value>> = mutableMapOf()
) : WriteOneToManyKeyValueRepo<Key, Value> {
) : WriteKeyValuesRepo<Key, Value> {
private val _onNewValue: MutableSharedFlow<Pair<Key, Value>> = MutableSharedFlow()
override val onNewValue: Flow<Pair<Key, Value>> = _onNewValue.asSharedFlow()
private val _onValueRemoved: MutableSharedFlow<Pair<Key, Value>> = MutableSharedFlow()
@@ -80,6 +84,10 @@ class MapWriteOneToManyKeyValueRepo<Key, Value>(
_onValueRemoved.emit(k to v)
}
}
if (map[k] ?.isEmpty() == true) {
map.remove(k)
_onDataCleared.emit(k)
}
}
}
@@ -94,12 +102,17 @@ class MapWriteOneToManyKeyValueRepo<Key, Value>(
}
}
class MapOneToManyKeyValueRepo<Key, Value>(
@Deprecated("Renamed", ReplaceWith("MapKeyValuesRepo", "dev.inmo.micro_utils.repos.MapKeyValuesRepo"))
typealias MapOneToManyKeyValueRepo1<Key, Value> = MapKeyValuesRepo<Key, Value>
class MapKeyValuesRepo<Key, Value>(
private val map: MutableMap<Key, MutableList<Value>> = mutableMapOf()
) : OneToManyKeyValueRepo<Key, Value>,
ReadOneToManyKeyValueRepo<Key, Value> by MapReadOneToManyKeyValueRepo(map),
WriteOneToManyKeyValueRepo<Key, Value> by MapWriteOneToManyKeyValueRepo(map)
) : KeyValuesRepo<Key, Value>,
ReadKeyValuesRepo<Key, Value> by MapReadKeyValuesRepo(map),
WriteKeyValuesRepo<Key, Value> by MapWriteKeyValuesRepo(map)
fun <K, V> MutableMap<K, List<V>>.asOneToManyKeyValueRepo(): OneToManyKeyValueRepo<K, V> = MapOneToManyKeyValueRepo(
fun <K, V> MutableMap<K, List<V>>.asKeyValuesRepo(): KeyValuesRepo<K, V> = MapKeyValuesRepo(
map { (k, v) -> k to v.toMutableList() }.toMap().toMutableMap()
)
@Deprecated("Renamed", ReplaceWith("asKeyValuesRepo", "dev.inmo.micro_utils.repos.asKeyValuesRepo"))
fun <K, V> MutableMap<K, List<V>>.asOneToManyKeyValueRepo(): KeyValuesRepo<K, V> = asKeyValuesRepo()

View File

@@ -0,0 +1,106 @@
package dev.inmo.micro_utils.repos.ktor.client.crud
import dev.inmo.micro_utils.ktor.common.*
import dev.inmo.micro_utils.pagination.PaginationResult
import dev.inmo.micro_utils.repos.*
import io.ktor.client.HttpClient
import io.ktor.http.ContentType
import io.ktor.util.reflect.TypeInfo
import io.ktor.util.reflect.typeInfo
import kotlinx.serialization.*
class KtorCRUDRepoClient<ObjectType, IdType, InputValue> (
readDelegate: ReadCRUDRepo<ObjectType, IdType>,
writeDelegate: WriteCRUDRepo<ObjectType, IdType, InputValue>
) : CRUDRepo<ObjectType, IdType, InputValue> by DelegateBasedCRUDRepo(
readDelegate,
writeDelegate
) {
companion object {
inline operator fun <reified ObjectType, reified IdType, reified InputValue> invoke(
baseUrl: String,
httpClient: HttpClient,
contentType: ContentType,
noinline idSerializer: suspend (IdType) -> String
) = KtorCRUDRepoClient(
KtorReadCRUDRepoClient(
baseUrl,
httpClient,
typeInfo<ObjectType>(),
typeInfo<PaginationResult<ObjectType>>(),
contentType,
idSerializer
),
KtorWriteCrudRepoClient<ObjectType, IdType, InputValue>(
baseUrl,
httpClient,
contentType
)
)
inline operator fun <reified ObjectType, reified IdType, reified InputValue> invoke(
baseUrl: String,
subpart: String,
httpClient: HttpClient,
contentType: ContentType,
noinline idSerializer: suspend (IdType) -> String
) = KtorCRUDRepoClient<ObjectType, IdType, InputValue>(
buildStandardUrl(baseUrl, subpart),
httpClient,
contentType,
idSerializer
)
}
}
inline fun <reified ObjectType, reified IdType, reified InputValue> KtorCRUDRepoClient(
baseUrl: String,
httpClient: HttpClient,
idsSerializer: KSerializer<IdType>,
serialFormat: StringFormat,
contentType: ContentType,
) = KtorCRUDRepoClient<ObjectType, IdType, InputValue>(baseUrl, httpClient, contentType) {
serialFormat.encodeToString(idsSerializer, it)
}
inline fun <reified ObjectType, reified IdType, reified InputValue> KtorCRUDRepoClient(
baseUrl: String,
httpClient: HttpClient,
idsSerializer: KSerializer<IdType>,
serialFormat: BinaryFormat,
contentType: ContentType,
) = KtorCRUDRepoClient<ObjectType, IdType, InputValue>(baseUrl, httpClient, contentType) {
serialFormat.encodeHex(idsSerializer, it)
}
inline fun <reified ObjectType, reified IdType, reified InputValue> KtorCRUDRepoClient(
baseUrl: String,
subpart: String,
httpClient: HttpClient,
contentType: ContentType,
noinline idSerializer: suspend (IdType) -> String
) = KtorCRUDRepoClient<ObjectType, IdType, InputValue>(
buildStandardUrl(baseUrl, subpart),
httpClient,
contentType,
idSerializer
)
inline fun <reified ObjectType, reified IdType, reified InputValue> KtorCRUDRepoClient(
baseUrl: String,
subpart: String,
httpClient: HttpClient,
idsSerializer: KSerializer<IdType>,
serialFormat: StringFormat,
contentType: ContentType,
) = KtorCRUDRepoClient<ObjectType, IdType, InputValue>(buildStandardUrl(baseUrl, subpart), httpClient, idsSerializer, serialFormat, contentType)
inline fun <reified ObjectType, reified IdType, reified InputValue> KtorCRUDRepoClient(
baseUrl: String,
subpart: String,
httpClient: HttpClient,
idsSerializer: KSerializer<IdType>,
serialFormat: BinaryFormat,
contentType: ContentType,
) = KtorCRUDRepoClient<ObjectType, IdType, InputValue>(buildStandardUrl(baseUrl, subpart), httpClient, idsSerializer, serialFormat, contentType)

View File

@@ -0,0 +1,97 @@
package dev.inmo.micro_utils.repos.ktor.client.crud
import dev.inmo.micro_utils.ktor.common.*
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.ReadCRUDRepo
import dev.inmo.micro_utils.repos.ktor.common.countRouting
import dev.inmo.micro_utils.repos.ktor.common.crud.*
import dev.inmo.micro_utils.repos.ktor.common.idParameterName
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.request.get
import io.ktor.http.*
import io.ktor.util.reflect.TypeInfo
import io.ktor.util.reflect.typeInfo
import kotlinx.serialization.*
class KtorReadCRUDRepoClient<ObjectType, IdType> (
private val baseUrl: String,
private val httpClient: HttpClient,
private val objectType: TypeInfo,
private val paginationObjectType: TypeInfo,
private val contentType: ContentType,
private val idSerializer: suspend (IdType) -> String
) : ReadCRUDRepo<ObjectType, IdType> {
override suspend fun getByPagination(pagination: Pagination): PaginationResult<ObjectType> = httpClient.get(
buildStandardUrl(baseUrl, getByPaginationRouting, pagination.asUrlQueryParts)
) {
contentType(contentType)
}.body(paginationObjectType)
override suspend fun getById(id: IdType): ObjectType? = httpClient.get(
buildStandardUrl(
baseUrl,
getByIdRouting,
mapOf(
idParameterName to idSerializer(id)
)
)
) {
contentType(contentType)
}.takeIf { it.status != HttpStatusCode.NoContent } ?.body<ObjectType>(objectType)
override suspend fun contains(id: IdType): Boolean = httpClient.get(
buildStandardUrl(
baseUrl,
containsRouting,
mapOf(
idParameterName to idSerializer(id)
)
)
) {
contentType(contentType)
}.body()
override suspend fun count(): Long = httpClient.get(
buildStandardUrl(
baseUrl,
countRouting
)
) {
contentType(contentType)
}.body()
}
inline fun <reified ObjectType, IdType> KtorReadCRUDRepoClient(
baseUrl: String,
httpClient: HttpClient,
contentType: ContentType,
noinline idSerializer: suspend (IdType) -> String
) = KtorReadCRUDRepoClient<ObjectType, IdType>(
baseUrl,
httpClient,
typeInfo<ObjectType>(),
typeInfo<PaginationResult<ObjectType>>(),
contentType,
idSerializer
)
inline fun <reified ObjectType, IdType> KtorReadCRUDRepoClient(
baseUrl: String,
httpClient: HttpClient,
idsSerializer: KSerializer<IdType>,
serialFormat: StringFormat,
contentType: ContentType,
) = KtorReadCRUDRepoClient<ObjectType, IdType>(baseUrl, httpClient, contentType) {
serialFormat.encodeToString(idsSerializer, it)
}
inline fun <reified ObjectType, IdType> KtorReadCRUDRepoClient(
baseUrl: String,
httpClient: HttpClient,
idsSerializer: KSerializer<IdType>,
serialFormat: BinaryFormat,
contentType: ContentType,
) = KtorReadCRUDRepoClient<ObjectType, IdType>(baseUrl, httpClient, contentType) {
serialFormat.encodeHex(idsSerializer, it)
}

View File

@@ -3,19 +3,22 @@ package dev.inmo.micro_utils.repos.ktor.client.crud
import dev.inmo.micro_utils.ktor.client.*
import dev.inmo.micro_utils.ktor.common.*
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.ReadStandardCRUDRepo
import dev.inmo.micro_utils.repos.ReadCRUDRepo
import dev.inmo.micro_utils.repos.ktor.common.countRouting
import dev.inmo.micro_utils.repos.ktor.common.crud.*
import dev.inmo.micro_utils.repos.ktor.common.idParameterName
import io.ktor.client.HttpClient
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.serializer
@Deprecated("Use KtorReadCRUDRepoClient instead")
class KtorReadStandardCrudRepo<ObjectType, IdType> (
private val baseUrl: String,
private val unifiedRequester: UnifiedRequester,
private val objectsSerializer: KSerializer<ObjectType>,
private val objectsSerializerNullable: KSerializer<ObjectType?>,
private val idsSerializer: KSerializer<IdType>
) : ReadStandardCRUDRepo<ObjectType, IdType> {
) : ReadCRUDRepo<ObjectType, IdType> {
private val paginationResultSerializer = PaginationResult.serializer(objectsSerializer)
constructor(
@@ -38,9 +41,7 @@ class KtorReadStandardCrudRepo<ObjectType, IdType> (
buildStandardUrl(
baseUrl,
getByIdRouting,
mapOf(
"id" to unifiedRequester.encodeUrlQueryValue(idsSerializer, id)
)
idParameterName to unifiedRequester.encodeUrlQueryValue(idsSerializer, id)
),
objectsSerializerNullable
)
@@ -49,9 +50,7 @@ class KtorReadStandardCrudRepo<ObjectType, IdType> (
buildStandardUrl(
baseUrl,
containsRouting,
mapOf(
"id" to unifiedRequester.encodeUrlQueryValue(idsSerializer, id)
)
idParameterName to unifiedRequester.encodeUrlQueryValue(idsSerializer, id)
),
Boolean.serializer()
)

View File

@@ -7,6 +7,7 @@ import dev.inmo.micro_utils.repos.*
import io.ktor.client.HttpClient
import kotlinx.serialization.KSerializer
@Deprecated("Use KtorCRUDRepoClient instead")
class KtorStandardCrudRepo<ObjectType, IdType, InputValue> (
baseUrl: String,
baseSubpart: String,
@@ -15,15 +16,15 @@ class KtorStandardCrudRepo<ObjectType, IdType, InputValue> (
objectsNullableSerializer: KSerializer<ObjectType?>,
inputsSerializer: KSerializer<InputValue>,
idsSerializer: KSerializer<IdType>
) : StandardCRUDRepo<ObjectType, IdType, InputValue>,
ReadStandardCRUDRepo<ObjectType, IdType> by KtorReadStandardCrudRepo(
) : CRUDRepo<ObjectType, IdType, InputValue>,
ReadCRUDRepo<ObjectType, IdType> by KtorReadStandardCrudRepo(
"$baseUrl/$baseSubpart",
unifiedRequester,
objectsSerializer,
objectsNullableSerializer,
idsSerializer
),
WriteStandardCRUDRepo<ObjectType, IdType, InputValue> by KtorWriteStandardCrudRepo(
WriteCRUDRepo<ObjectType, IdType, InputValue> by KtorWriteStandardCrudRepo(
"$baseUrl/$baseSubpart",
unifiedRequester,
objectsSerializer,

View File

@@ -0,0 +1,85 @@
package dev.inmo.micro_utils.repos.ktor.client.crud
import dev.inmo.micro_utils.ktor.client.*
import dev.inmo.micro_utils.ktor.common.*
import dev.inmo.micro_utils.repos.UpdatedValuePair
import dev.inmo.micro_utils.repos.WriteCRUDRepo
import dev.inmo.micro_utils.repos.ktor.common.crud.*
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.request.*
import io.ktor.client.statement.HttpResponse
import io.ktor.http.ContentType
import io.ktor.http.contentType
import kotlinx.coroutines.flow.Flow
class KtorWriteCrudRepoClient<ObjectType, IdType, InputValue> (
private val baseUrl: String,
private val httpClient: HttpClient,
override val newObjectsFlow: Flow<ObjectType>,
override val updatedObjectsFlow: Flow<ObjectType>,
override val deletedObjectsIdsFlow: Flow<IdType>,
private val createSetup: suspend HttpRequestBuilder.(List<InputValue>) -> Unit,
private val updateSetup: suspend HttpRequestBuilder.(List<UpdatedValuePair<IdType, InputValue>>) -> Unit,
private val deleteByIdSetup: suspend HttpRequestBuilder.(List<IdType>) -> Unit,
private val createBodyGetter: suspend HttpResponse.() -> List<ObjectType>,
private val updateBodyGetter: suspend HttpResponse.() -> List<ObjectType>
) : WriteCRUDRepo<ObjectType, IdType, InputValue> {
override suspend fun create(values: List<InputValue>): List<ObjectType> = httpClient.post(
buildStandardUrl(baseUrl, createRouting)
) {
createSetup(values)
}.createBodyGetter()
override suspend fun update(
values: List<UpdatedValuePair<IdType, InputValue>>
): List<ObjectType> = httpClient.post(
buildStandardUrl(baseUrl, updateRouting)
) {
updateSetup(values)
}.updateBodyGetter()
override suspend fun update(id: IdType, value: InputValue): ObjectType? = update(listOf(id to value)).firstOrNull()
override suspend fun deleteById(ids: List<IdType>) {
httpClient.post(
buildStandardUrl(baseUrl, deleteByIdRouting)
) {
deleteByIdSetup(ids)
}.throwOnUnsuccess { "Unable to delete $ids" }
}
companion object {
inline operator fun <reified ObjectType, reified IdType, reified InputValue> invoke(
baseUrl: String,
httpClient: HttpClient,
contentType: ContentType
) = KtorWriteCrudRepoClient<ObjectType, IdType, InputValue>(
baseUrl,
httpClient,
httpClient.createStandardWebsocketFlow(
buildStandardUrl(baseUrl, newObjectsFlowRouting),
),
httpClient.createStandardWebsocketFlow(
buildStandardUrl(baseUrl, updatedObjectsFlowRouting),
),
httpClient.createStandardWebsocketFlow(
buildStandardUrl(baseUrl, deletedObjectsIdsFlowRouting),
),
{
contentType(contentType)
setBody(it)
},
{
contentType(contentType)
setBody(it)
},
{
contentType(contentType)
setBody(it)
},
{ body() },
{ body() }
)
}
}

View File

@@ -3,13 +3,14 @@ package dev.inmo.micro_utils.repos.ktor.client.crud
import dev.inmo.micro_utils.ktor.client.*
import dev.inmo.micro_utils.ktor.common.*
import dev.inmo.micro_utils.repos.UpdatedValuePair
import dev.inmo.micro_utils.repos.WriteStandardCRUDRepo
import dev.inmo.micro_utils.repos.WriteCRUDRepo
import dev.inmo.micro_utils.repos.ktor.common.crud.*
import io.ktor.client.HttpClient
import kotlinx.coroutines.flow.Flow
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.*
@Deprecated("Use KtorWriteCRUDRepoClient instead")
class KtorWriteStandardCrudRepo<ObjectType, IdType, InputValue> (
private val baseUrl: String,
private val unifiedRequester: UnifiedRequester,
@@ -17,7 +18,7 @@ class KtorWriteStandardCrudRepo<ObjectType, IdType, InputValue> (
private val objectsNullableSerializer: KSerializer<ObjectType?>,
private val inputsSerializer: KSerializer<InputValue>,
private val idsSerializer: KSerializer<IdType>
) : WriteStandardCRUDRepo<ObjectType, IdType, InputValue> {
) : WriteCRUDRepo<ObjectType, IdType, InputValue> {
private val listObjectsSerializer = ListSerializer(objectsSerializer)
private val listInputSerializer = ListSerializer(inputsSerializer)
private val listIdsSerializer = ListSerializer(idsSerializer)
@@ -55,25 +56,25 @@ class KtorWriteStandardCrudRepo<ObjectType, IdType, InputValue> (
override suspend fun create(values: List<InputValue>): List<ObjectType> = unifiedRequester.unipost(
buildStandardUrl(baseUrl, createRouting),
BodyPair(listInputSerializer, values),
Pair(listInputSerializer, values),
listObjectsSerializer
)
override suspend fun update(id: IdType, value: InputValue): ObjectType? = unifiedRequester.unipost(
buildStandardUrl(baseUrl, updateRouting),
BodyPair(inputUpdateSerializer, id to value),
Pair(inputUpdateSerializer, id to value),
objectsNullableSerializer
)
override suspend fun update(values: List<UpdatedValuePair<IdType, InputValue>>): List<ObjectType> = unifiedRequester.unipost(
buildStandardUrl(baseUrl, updateManyRouting),
BodyPair(listInputUpdateSerializer, values),
Pair(listInputUpdateSerializer, values),
listObjectsSerializer
)
override suspend fun deleteById(ids: List<IdType>) = unifiedRequester.unipost(
buildStandardUrl(baseUrl, deleteByIdRouting),
BodyPair(listIdsSerializer, ids),
Pair(listIdsSerializer, ids),
Unit.serializer()
)
}

View File

@@ -0,0 +1,85 @@
package dev.inmo.micro_utils.repos.ktor.client.key.value
import dev.inmo.micro_utils.ktor.common.*
import dev.inmo.micro_utils.repos.*
import io.ktor.client.HttpClient
import io.ktor.http.ContentType
import io.ktor.http.encodeURLQueryComponent
import kotlinx.serialization.*
class KtorKeyValueRepoClient<Key, Value> (
readDelegate: ReadKeyValueRepo<Key, Value>,
writeDelegate: WriteKeyValueRepo<Key, Value>
) : KeyValueRepo<Key, Value> by DelegateBasedKeyValueRepo(
readDelegate,
writeDelegate
) {
companion object {
inline operator fun <reified Key, reified Value> invoke(
baseUrl: String,
httpClient: HttpClient,
contentType: ContentType,
noinline idSerializer: suspend (Key) -> String,
noinline valueSerializer: suspend (Value) -> String
) = KtorKeyValueRepoClient(
KtorReadKeyValueRepoClient(
baseUrl, httpClient, contentType, idSerializer, valueSerializer
),
KtorWriteKeyValueRepoClient(
baseUrl,
httpClient,
contentType
)
)
inline operator fun <reified Key, reified Value> invoke(
baseUrl: String,
subpart: String,
httpClient: HttpClient,
contentType: ContentType,
noinline idSerializer: suspend (Key) -> String,
noinline valueSerializer: suspend (Value) -> String
) = KtorKeyValueRepoClient(
buildStandardUrl(baseUrl, subpart),
httpClient,
contentType,
idSerializer,
valueSerializer
)
}
}
inline fun <reified Key, reified Value> KtorKeyValueRepoClient(
baseUrl: String,
httpClient: HttpClient,
contentType: ContentType,
idSerializer: SerializationStrategy<Key>,
valueSerializer: SerializationStrategy<Value>,
serialFormat: StringFormat,
) = KtorKeyValueRepoClient<Key, Value>(
baseUrl,
httpClient,
contentType,
{
serialFormat.encodeToString(idSerializer, it).encodeURLQueryComponent()
}
) {
serialFormat.encodeToString(valueSerializer, it).encodeURLQueryComponent()
}
inline fun <reified Key, reified Value> KtorKeyValueRepoClient(
baseUrl: String,
httpClient: HttpClient,
contentType: ContentType,
idSerializer: SerializationStrategy<Key>,
valueSerializer: SerializationStrategy<Value>,
serialFormat: BinaryFormat,
) = KtorKeyValueRepoClient<Key, Value>(
baseUrl,
httpClient,
contentType,
{
serialFormat.encodeHex(idSerializer, it)
}
) {
serialFormat.encodeHex(valueSerializer, it)
}

View File

@@ -0,0 +1,144 @@
package dev.inmo.micro_utils.repos.ktor.client.key.value
import dev.inmo.micro_utils.ktor.common.*
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.ReadKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.*
import dev.inmo.micro_utils.repos.ktor.common.containsRoute
import dev.inmo.micro_utils.repos.ktor.common.keyParameterName
import dev.inmo.micro_utils.repos.ktor.common.key_value.*
import dev.inmo.micro_utils.repos.ktor.common.reversedParameterName
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.request.get
import io.ktor.http.*
import io.ktor.util.reflect.TypeInfo
import io.ktor.util.reflect.typeInfo
import kotlinx.serialization.*
class KtorReadKeyValueRepoClient<Key, Value>(
private val baseUrl: String,
private val httpClient: HttpClient,
private val contentType: ContentType,
private val objectType: TypeInfo,
private val paginationResultObjectsTypeInfo: TypeInfo,
private val paginationResultIdsTypeInfo: TypeInfo,
private val idSerializer: suspend (Key) -> String,
private val valueSerializer: suspend (Value) -> String
) : ReadKeyValueRepo<Key, Value> {
override suspend fun get(k: Key): Value? = httpClient.get(
buildStandardUrl(
baseUrl,
getRoute,
mapOf(
keyParameterName to idSerializer(k)
)
)
) {
contentType(contentType)
}.takeIf { it.status != HttpStatusCode.NoContent } ?.body<Value>(objectType)
override suspend fun contains(key: Key): Boolean = httpClient.get(
buildStandardUrl(
baseUrl,
containsRoute,
keyParameterName to idSerializer(key)
)
) {
contentType(contentType)
}.body()
override suspend fun values(
pagination: Pagination,
reversed: Boolean
): PaginationResult<Value> = httpClient.get(
buildStandardUrl(baseUrl, valuesRoute, pagination.asUrlQueryParts + (reversedParameterName to reversed.toString()))
) {
contentType(contentType)
}.body(paginationResultObjectsTypeInfo)
override suspend fun keys(
pagination: Pagination,
reversed: Boolean
): PaginationResult<Key> = httpClient.get(
buildStandardUrl(baseUrl, keysRoute, pagination.asUrlQueryParts + (reversedParameterName to reversed.toString()))
) {
contentType(contentType)
}.body(paginationResultIdsTypeInfo)
override suspend fun keys(
v: Value,
pagination: Pagination,
reversed: Boolean
): PaginationResult<Key> = httpClient.get(
buildStandardUrl(
baseUrl,
keysRoute,
pagination.asUrlQueryParts + (reversedParameterName to reversed.toString()) + (valueParameterName to valueSerializer(v))
)
) {
contentType(contentType)
}.body(paginationResultIdsTypeInfo)
override suspend fun count(): Long = httpClient.get(
buildStandardUrl(
baseUrl,
dev.inmo.micro_utils.repos.ktor.common.countRoute
)
) {
contentType(contentType)
}.body()
}
inline fun <reified Key, reified Value> KtorReadKeyValueRepoClient(
baseUrl: String,
httpClient: HttpClient,
contentType: ContentType,
noinline idSerializer: suspend (Key) -> String,
noinline valueSerializer: suspend (Value) -> String
) = KtorReadKeyValueRepoClient<Key, Value>(
baseUrl,
httpClient,
contentType,
typeInfo<Value>(),
typeInfo<PaginationResult<Value>>(),
typeInfo<PaginationResult<Key>>(),
idSerializer,
valueSerializer
)
inline fun <reified Key, reified Value> KtorReadKeyValueRepoClient(
baseUrl: String,
httpClient: HttpClient,
idsSerializer: KSerializer<Key>,
valueSerializer: KSerializer<Value>,
serialFormat: StringFormat,
contentType: ContentType,
) = KtorReadKeyValueRepoClient<Key, Value>(
baseUrl,
httpClient,
contentType,
{
serialFormat.encodeToString(idsSerializer, it).encodeURLQueryComponent()
}
) {
serialFormat.encodeToString(valueSerializer, it).encodeURLQueryComponent()
}
inline fun <reified Key, reified Value> KtorReadKeyValueRepoClient(
baseUrl: String,
httpClient: HttpClient,
idsSerializer: KSerializer<Key>,
valuesSerializer: KSerializer<Value>,
serialFormat: BinaryFormat,
contentType: ContentType,
) = KtorReadKeyValueRepoClient<Key, Value>(
baseUrl,
httpClient,
contentType,
{
serialFormat.encodeHex(idsSerializer, it)
}
) {
serialFormat.encodeHex(valuesSerializer, it)
}

View File

@@ -0,0 +1,79 @@
package dev.inmo.micro_utils.repos.ktor.client.key.value
import dev.inmo.micro_utils.ktor.client.createStandardWebsocketFlow
import dev.inmo.micro_utils.ktor.client.throwOnUnsuccess
import dev.inmo.micro_utils.ktor.common.*
import dev.inmo.micro_utils.repos.WriteKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.key_value.*
import io.ktor.client.HttpClient
import io.ktor.client.request.post
import io.ktor.http.*
import io.ktor.util.InternalAPI
import io.ktor.util.reflect.TypeInfo
import io.ktor.util.reflect.typeInfo
import kotlinx.coroutines.flow.Flow
class KtorWriteKeyValueRepoClient<Key, Value>(
private val baseUrl: String,
private val httpClient: HttpClient,
private val contentType: ContentType,
override val onNewValue: Flow<Pair<Key, Value>>,
override val onValueRemoved: Flow<Key>,
private val idsListTypeInfo: TypeInfo,
private val objectsListTypeInfo: TypeInfo,
private val idsToObjectsMapTypeInfo: TypeInfo
) : WriteKeyValueRepo<Key, Value> {
@OptIn(InternalAPI::class)
override suspend fun unsetWithValues(toUnset: List<Value>) {
httpClient.post(
buildStandardUrl(baseUrl, unsetWithValuesRoute)
) {
body = toUnset
bodyType = objectsListTypeInfo
contentType(contentType)
}.throwOnUnsuccess { "Unable to unset data with values $toUnset" }
}
@OptIn(InternalAPI::class)
override suspend fun unset(toUnset: List<Key>) {
httpClient.post(
buildStandardUrl(baseUrl, unsetRoute)
) {
body = toUnset
bodyType = idsListTypeInfo
contentType(contentType)
}.throwOnUnsuccess { "Unable to unset $toUnset" }
}
@OptIn(InternalAPI::class)
override suspend fun set(toSet: Map<Key, Value>) {
httpClient.post(
buildStandardUrl(baseUrl, setRoute)
) {
body = toSet
bodyType = idsToObjectsMapTypeInfo
contentType(contentType)
}.throwOnUnsuccess { "Unable to set $toSet" }
}
companion object {
inline operator fun <reified Key, reified Value> invoke(
baseUrl: String,
httpClient: HttpClient,
contentType: ContentType
) = KtorWriteKeyValueRepoClient<Key, Value>(
baseUrl,
httpClient,
contentType,
httpClient.createStandardWebsocketFlow(
buildStandardUrl(baseUrl, onNewValueRoute),
),
httpClient.createStandardWebsocketFlow(
buildStandardUrl(baseUrl, onValueRemovedRoute),
),
typeInfo<List<Key>>(),
typeInfo<List<Value>>(),
typeInfo<Map<Key, Value>>()
)
}
}

View File

@@ -0,0 +1,89 @@
package dev.inmo.micro_utils.repos.ktor.client.key.values
import dev.inmo.micro_utils.ktor.common.*
import dev.inmo.micro_utils.repos.*
import io.ktor.client.HttpClient
import io.ktor.http.ContentType
import io.ktor.http.encodeURLQueryComponent
import kotlinx.serialization.*
class KtorKeyValuesRepoClient<Key, Value> (
readDelegate: ReadKeyValuesRepo<Key, Value>,
writeDelegate: WriteKeyValuesRepo<Key, Value>
) : KeyValuesRepo<Key, Value> by DelegateBasedKeyValuesRepo(
readDelegate,
writeDelegate
) {
companion object {
inline operator fun <reified Key : Any, reified Value : Any> invoke(
baseUrl: String,
httpClient: HttpClient,
contentType: ContentType,
noinline keySerializer: suspend (Key) -> String,
noinline valueSerializer: suspend (Value) -> String
) = KtorKeyValuesRepoClient(
KtorReadKeyValuesRepoClient(
baseUrl,
httpClient,
contentType,
keySerializer,
valueSerializer
),
KtorWriteKeyValuesRepoClient(
baseUrl,
httpClient,
contentType
)
)
inline operator fun <reified Key : Any, reified Value : Any> invoke(
baseUrl: String,
subpart: String,
httpClient: HttpClient,
contentType: ContentType,
noinline keySerializer: suspend (Key) -> String,
noinline valueSerializer: suspend (Value) -> String
) = KtorKeyValuesRepoClient(
buildStandardUrl(baseUrl, subpart),
httpClient,
contentType,
keySerializer,
valueSerializer
)
}
}
inline fun <reified Key : Any, reified Value : Any> KtorKeyValuesRepoClient(
baseUrl: String,
httpClient: HttpClient,
contentType: ContentType,
keySerializer: SerializationStrategy<Key>,
valueSerializer: SerializationStrategy<Value>,
serialFormat: StringFormat,
) = KtorKeyValuesRepoClient<Key, Value>(
baseUrl,
httpClient,
contentType,
{
serialFormat.encodeToString(keySerializer, it).encodeURLQueryComponent()
}
) {
serialFormat.encodeToString(valueSerializer, it).encodeURLQueryComponent()
}
inline fun <reified Key : Any, reified Value : Any> KtorKeyValuesRepoClient(
baseUrl: String,
httpClient: HttpClient,
contentType: ContentType,
keySerializer: SerializationStrategy<Key>,
valueSerializer: SerializationStrategy<Value>,
serialFormat: BinaryFormat,
) = KtorKeyValuesRepoClient<Key, Value>(
baseUrl,
httpClient,
contentType,
{
serialFormat.encodeHex(keySerializer, it)
}
) {
serialFormat.encodeHex(valueSerializer, it)
}

View File

@@ -0,0 +1,158 @@
package dev.inmo.micro_utils.repos.ktor.client.key.values
import dev.inmo.micro_utils.ktor.common.*
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.ReadKeyValuesRepo
import dev.inmo.micro_utils.repos.ktor.common.*
import dev.inmo.micro_utils.repos.ktor.common.one_to_many.*
import dev.inmo.micro_utils.repos.ktor.common.reversedParameterName
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.request.get
import io.ktor.http.*
import io.ktor.util.reflect.TypeInfo
import io.ktor.util.reflect.typeInfo
import kotlinx.serialization.*
class KtorReadKeyValuesRepoClient<Key, Value>(
private val baseUrl: String,
private val httpClient: HttpClient,
private val contentType: ContentType,
private val paginationResultValuesTypeInfo: TypeInfo,
private val paginationResultKeysTypeInfo: TypeInfo,
private val keySerializer: suspend (Key) -> String,
private val valueSerializer: suspend (Value) -> String
) : ReadKeyValuesRepo<Key, Value> {
override suspend fun get(
k: Key,
pagination: Pagination,
reversed: Boolean
): PaginationResult<Value> = httpClient.get(
buildStandardUrl(
baseUrl,
getRoute,
pagination.asUrlQueryParts + (reversedParameterName to reversed.toString()) + (keyParameterName to keySerializer(k))
)
) {
contentType(contentType)
}.body(paginationResultValuesTypeInfo)
override suspend fun keys(
pagination: Pagination,
reversed: Boolean
): PaginationResult<Key> = httpClient.get(
buildStandardUrl(
baseUrl,
keysRoute,
pagination.asUrlQueryParts + (reversedParameterName to reversed.toString())
)
) {
contentType(contentType)
}.body(paginationResultKeysTypeInfo)
override suspend fun keys(
v: Value,
pagination: Pagination,
reversed: Boolean
): PaginationResult<Key> = httpClient.get(
buildStandardUrl(
baseUrl,
keysRoute,
pagination.asUrlQueryParts + (reversedParameterName to reversed.toString()) + (valueParameterName to valueSerializer(v))
)
) {
contentType(contentType)
}.body(paginationResultKeysTypeInfo)
override suspend fun contains(k: Key): Boolean = httpClient.get(
buildStandardUrl(
baseUrl,
containsRoute,
keyParameterName to keySerializer(k)
)
) {
contentType(contentType)
}.body()
override suspend fun contains(k: Key, v: Value): Boolean = httpClient.get(
buildStandardUrl(
baseUrl,
containsRoute,
keyParameterName to keySerializer(k),
valueParameterName to valueSerializer(v)
)
) {
contentType(contentType)
}.body()
override suspend fun count(): Long = httpClient.get(
buildStandardUrl(
baseUrl,
countRouting
)
) {
contentType(contentType)
}.body()
override suspend fun count(k: Key): Long = httpClient.get(
buildStandardUrl(
baseUrl,
countRouting,
keyParameterName to keySerializer(k),
)
) {
contentType(contentType)
}.body()
}
inline fun <reified Key, reified Value> KtorReadKeyValuesRepoClient(
baseUrl: String,
httpClient: HttpClient,
contentType: ContentType,
noinline keySerializer: suspend (Key) -> String,
noinline valueSerializer: suspend (Value) -> String
) = KtorReadKeyValuesRepoClient<Key, Value>(
baseUrl,
httpClient,
contentType,
typeInfo<PaginationResult<Value>>(),
typeInfo<PaginationResult<Key>>(),
keySerializer,
valueSerializer
)
inline fun <reified Key, reified Value> KtorReadKeyValuesRepoClient(
baseUrl: String,
httpClient: HttpClient,
idsSerializer: KSerializer<Key>,
valueSerializer: KSerializer<Value>,
serialFormat: StringFormat,
contentType: ContentType,
) = KtorReadKeyValuesRepoClient<Key, Value>(
baseUrl,
httpClient,
contentType,
{
serialFormat.encodeToString(idsSerializer, it).encodeURLQueryComponent()
}
) {
serialFormat.encodeToString(valueSerializer, it).encodeURLQueryComponent()
}
inline fun <reified Key, reified Value> KtorReadKeyValuesRepoClient(
baseUrl: String,
httpClient: HttpClient,
idsSerializer: KSerializer<Key>,
valuesSerializer: KSerializer<Value>,
serialFormat: BinaryFormat,
contentType: ContentType,
) = KtorReadKeyValuesRepoClient<Key, Value>(
baseUrl,
httpClient,
contentType,
{
serialFormat.encodeHex(idsSerializer, it)
}
) {
serialFormat.encodeHex(valuesSerializer, it)
}

View File

@@ -0,0 +1,106 @@
package dev.inmo.micro_utils.repos.ktor.client.key.values
import dev.inmo.micro_utils.ktor.client.createStandardWebsocketFlow
import dev.inmo.micro_utils.ktor.client.throwOnUnsuccess
import dev.inmo.micro_utils.ktor.common.*
import dev.inmo.micro_utils.repos.WriteKeyValuesRepo
import dev.inmo.micro_utils.repos.ktor.common.one_to_many.*
import io.ktor.client.HttpClient
import io.ktor.client.request.post
import io.ktor.http.*
import io.ktor.util.InternalAPI
import io.ktor.util.reflect.TypeInfo
import io.ktor.util.reflect.typeInfo
import kotlinx.coroutines.flow.Flow
class KtorWriteKeyValuesRepoClient<Key : Any, Value : Any>(
private val baseUrl: String,
private val httpClient: HttpClient,
private val contentType: ContentType,
override val onNewValue: Flow<Pair<Key, Value>>,
override val onValueRemoved: Flow<Pair<Key, Value>>,
override val onDataCleared: Flow<Key>,
private val keyTypeInfo: TypeInfo,
private val valueTypeInfo: TypeInfo,
private val keyToValuesMapTypeInfo: TypeInfo
) : WriteKeyValuesRepo<Key, Value> {
@OptIn(InternalAPI::class)
override suspend fun add(toAdd: Map<Key, List<Value>>) {
httpClient.post(
buildStandardUrl(baseUrl, addRoute)
) {
body = toAdd
bodyType = keyToValuesMapTypeInfo
contentType(contentType)
}.throwOnUnsuccess { "Unable to add $toAdd" }
}
@OptIn(InternalAPI::class)
override suspend fun remove(toRemove: Map<Key, List<Value>>) {
httpClient.post(
buildStandardUrl(baseUrl, removeRoute)
) {
body = toRemove
bodyType = keyToValuesMapTypeInfo
contentType(contentType)
}.throwOnUnsuccess { "Unable to remove $toRemove" }
}
@OptIn(InternalAPI::class)
override suspend fun clear(k: Key) {
httpClient.post(
buildStandardUrl(baseUrl, clearRoute)
) {
body = k
bodyType = keyTypeInfo
contentType(contentType)
}.throwOnUnsuccess { "Unable to clear data with key $k" }
}
@OptIn(InternalAPI::class)
override suspend fun clearWithValue(v: Value) {
httpClient.post(
buildStandardUrl(baseUrl, clearWithValueRoute)
) {
body = v
bodyType = valueTypeInfo
contentType(contentType)
}.throwOnUnsuccess { "Unable to clear data with value $v" }
}
@OptIn(InternalAPI::class)
override suspend fun set(toSet: Map<Key, List<Value>>) {
httpClient.post(
buildStandardUrl(baseUrl, setRoute)
) {
body = toSet
bodyType = keyToValuesMapTypeInfo
contentType(contentType)
}.throwOnUnsuccess { "Unable to set data $toSet" }
}
companion object {
inline operator fun <reified Key : Any, reified Value : Any> invoke(
baseUrl: String,
httpClient: HttpClient,
contentType: ContentType
) = KtorWriteKeyValuesRepoClient<Key, Value>(
baseUrl,
httpClient,
contentType,
httpClient.createStandardWebsocketFlow(
buildStandardUrl(baseUrl, onNewValueRoute),
),
httpClient.createStandardWebsocketFlow(
buildStandardUrl(baseUrl, onValueRemovedRoute),
),
httpClient.createStandardWebsocketFlow(
buildStandardUrl(baseUrl, onDataClearedRoute),
),
typeInfo<Key>(),
typeInfo<Value>(),
typeInfo<Map<Key, List<Value>>>()
)
}
}

View File

@@ -3,20 +3,25 @@ package dev.inmo.micro_utils.repos.ktor.client.key_value
import dev.inmo.micro_utils.ktor.client.*
import dev.inmo.micro_utils.ktor.common.*
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.ReadStandardKeyValueRepo
import dev.inmo.micro_utils.repos.ReadKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.*
import dev.inmo.micro_utils.repos.ktor.common.containsRoute
import dev.inmo.micro_utils.repos.ktor.common.countRoute
import dev.inmo.micro_utils.repos.ktor.common.key_value.*
import dev.inmo.micro_utils.repos.ktor.common.valueParameterName
import dev.inmo.micro_utils.repos.ktor.common.key_value.keyParameterName
import dev.inmo.micro_utils.repos.ktor.common.key_value.reversedParameterName
import io.ktor.client.HttpClient
import kotlinx.serialization.*
import kotlinx.serialization.builtins.serializer
@Deprecated("Replaced with KtorReadKeyValueRepoClient")
class KtorReadStandardKeyValueRepo<Key, Value> (
private val baseUrl: String,
private val unifiedRequester: UnifiedRequester,
private val keySerializer: KSerializer<Key>,
private val valueSerializer: KSerializer<Value>,
private val valueNullableSerializer: KSerializer<Value?>
) : ReadStandardKeyValueRepo<Key, Value> {
) : ReadKeyValueRepo<Key, Value> {
constructor(
baseUrl: String,
client: HttpClient,

View File

@@ -7,6 +7,7 @@ import dev.inmo.micro_utils.repos.*
import io.ktor.client.HttpClient
import kotlinx.serialization.*
@Deprecated("Replaced with KtorKeyValueRepoClient")
@Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE")
class KtorStandartKeyValueRepo<K, V> (
baseUrl: String,
@@ -15,15 +16,15 @@ class KtorStandartKeyValueRepo<K, V> (
keySerializer: KSerializer<K>,
valueSerializer: KSerializer<V>,
valueNullableSerializer: KSerializer<V?>
) : StandardKeyValueRepo<K, V>,
ReadStandardKeyValueRepo<K, V> by KtorReadStandardKeyValueRepo(
) : KeyValueRepo<K, V>,
ReadKeyValueRepo<K, V> by KtorReadStandardKeyValueRepo(
"$baseUrl/$baseSubpart",
unifiedRequester,
keySerializer,
valueSerializer,
valueNullableSerializer
),
WriteStandardKeyValueRepo<K, V> by KtorWriteStandardKeyValueRepo(
WriteKeyValueRepo<K, V> by KtorWriteStandardKeyValueRepo(
"$baseUrl/$baseSubpart",
unifiedRequester,
keySerializer,

View File

@@ -2,19 +2,20 @@ package dev.inmo.micro_utils.repos.ktor.client.key_value
import dev.inmo.micro_utils.ktor.client.*
import dev.inmo.micro_utils.ktor.common.*
import dev.inmo.micro_utils.repos.WriteStandardKeyValueRepo
import dev.inmo.micro_utils.repos.WriteKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.key_value.*
import io.ktor.client.HttpClient
import kotlinx.coroutines.flow.Flow
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.*
@Deprecated("Replaced with KtorWriteKeyValueRepoClient")
class KtorWriteStandardKeyValueRepo<K, V> (
private var baseUrl: String,
private var unifiedRequester: UnifiedRequester,
private var keySerializer: KSerializer<K>,
private var valueSerializer: KSerializer<V>,
) : WriteStandardKeyValueRepo<K, V> {
) : WriteKeyValueRepo<K, V> {
private val keyValueMapSerializer = MapSerializer(keySerializer, valueSerializer)
private val keysListSerializer = ListSerializer(keySerializer)
private val valuesListSerializer = ListSerializer(valueSerializer)
@@ -44,7 +45,7 @@ class KtorWriteStandardKeyValueRepo<K, V> (
baseUrl,
setRoute
),
BodyPair(keyValueMapSerializer, toSet),
Pair(keyValueMapSerializer, toSet),
Unit.serializer()
)
@@ -53,7 +54,7 @@ class KtorWriteStandardKeyValueRepo<K, V> (
baseUrl,
unsetRoute,
),
BodyPair(keysListSerializer, toUnset),
Pair(keysListSerializer, toUnset),
Unit.serializer()
)
@@ -62,7 +63,7 @@ class KtorWriteStandardKeyValueRepo<K, V> (
baseUrl,
unsetWithValuesRoute,
),
BodyPair(valuesListSerializer, toUnset),
Pair(valuesListSerializer, toUnset),
Unit.serializer()
)
}

View File

@@ -7,20 +7,21 @@ import dev.inmo.micro_utils.repos.*
import io.ktor.client.HttpClient
import kotlinx.serialization.KSerializer
@Deprecated("Should be replaced with KtorKeyValuesRepoClient")
class KtorOneToManyKeyValueRepo<Key, Value>(
baseUrl: String,
baseSubpart: String,
unifiedRequester: UnifiedRequester,
keySerializer: KSerializer<Key>,
valueSerializer: KSerializer<Value>,
) : OneToManyKeyValueRepo<Key, Value>,
ReadOneToManyKeyValueRepo<Key, Value> by KtorReadOneToManyKeyValueRepo<Key, Value> (
) : KeyValuesRepo<Key, Value>,
ReadKeyValuesRepo<Key, Value> by KtorReadOneToManyKeyValueRepo<Key, Value> (
"$baseUrl/$baseSubpart",
unifiedRequester,
keySerializer,
valueSerializer,
),
WriteOneToManyKeyValueRepo<Key, Value> by KtorWriteOneToManyKeyValueRepo<Key, Value> (
WriteKeyValuesRepo<Key, Value> by KtorWriteOneToManyKeyValueRepo<Key, Value> (
"$baseUrl/$baseSubpart",
unifiedRequester,
keySerializer,

View File

@@ -3,7 +3,7 @@ package dev.inmo.micro_utils.repos.ktor.client.one_to_many
import dev.inmo.micro_utils.ktor.client.*
import dev.inmo.micro_utils.ktor.common.*
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.ReadOneToManyKeyValueRepo
import dev.inmo.micro_utils.repos.ReadKeyValuesRepo
import dev.inmo.micro_utils.repos.ktor.common.keyParameterName
import dev.inmo.micro_utils.repos.ktor.common.one_to_many.*
import dev.inmo.micro_utils.repos.ktor.common.reversedParameterName
@@ -12,12 +12,13 @@ import io.ktor.client.HttpClient
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.serializer
@Deprecated("Should be replaced with KtorReadKeyValuesRepoClient")
class KtorReadOneToManyKeyValueRepo<Key, Value> (
private val baseUrl: String,
private val unifiedRequester: UnifiedRequester,
private val keySerializer: KSerializer<Key>,
private val valueSerializer: KSerializer<Value>
) : ReadOneToManyKeyValueRepo<Key, Value> {
) : ReadKeyValuesRepo<Key, Value> {
private val paginationValueResultSerializer = PaginationResult.serializer(valueSerializer)
private val paginationKeyResultSerializer = PaginationResult.serializer(keySerializer)

View File

@@ -2,19 +2,20 @@ package dev.inmo.micro_utils.repos.ktor.client.one_to_many
import dev.inmo.micro_utils.ktor.client.*
import dev.inmo.micro_utils.ktor.common.*
import dev.inmo.micro_utils.repos.WriteOneToManyKeyValueRepo
import dev.inmo.micro_utils.repos.WriteKeyValuesRepo
import dev.inmo.micro_utils.repos.ktor.common.one_to_many.*
import io.ktor.client.HttpClient
import kotlinx.coroutines.flow.Flow
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.*
@Deprecated("Should be replaced with KtorWriteKeyValuesRepoClient")
class KtorWriteOneToManyKeyValueRepo<Key, Value> (
private val baseUrl: String,
private val unifiedRequester: UnifiedRequester,
private val keySerializer: KSerializer<Key>,
private val valueSerializer: KSerializer<Value>
) : WriteOneToManyKeyValueRepo<Key, Value> {
) : WriteKeyValuesRepo<Key, Value> {
private val keyValueSerializer = PairSerializer(keySerializer, valueSerializer)
private val keyValueMapSerializer = MapSerializer(keySerializer, ListSerializer(valueSerializer))
@@ -46,7 +47,7 @@ class KtorWriteOneToManyKeyValueRepo<Key, Value> (
baseUrl,
removeRoute,
),
BodyPair(keyValueMapSerializer, toRemove),
Pair(keyValueMapSerializer, toRemove),
Unit.serializer(),
)
@@ -55,7 +56,7 @@ class KtorWriteOneToManyKeyValueRepo<Key, Value> (
baseUrl,
addRoute,
),
BodyPair(keyValueMapSerializer, toAdd),
Pair(keyValueMapSerializer, toAdd),
Unit.serializer(),
)
override suspend fun clear(k: Key) = unifiedRequester.unipost(
@@ -63,7 +64,7 @@ class KtorWriteOneToManyKeyValueRepo<Key, Value> (
baseUrl,
clearRoute,
),
BodyPair(keySerializer, k),
Pair(keySerializer, k),
Unit.serializer(),
)
@@ -72,7 +73,7 @@ class KtorWriteOneToManyKeyValueRepo<Key, Value> (
baseUrl,
clearWithValueRoute,
),
BodyPair(valueSerializer, v),
Pair(valueSerializer, v),
Unit.serializer(),
)
@@ -81,7 +82,7 @@ class KtorWriteOneToManyKeyValueRepo<Key, Value> (
baseUrl,
setRoute,
),
BodyPair(keyValueMapSerializer, toSet),
Pair(keyValueMapSerializer, toSet),
Unit.serializer(),
)
}

View File

@@ -13,5 +13,22 @@ kotlin {
api internalProject("micro_utils.repos.common")
}
}
jvmTest {
dependencies {
implementation internalProject("micro_utils.repos.common")
implementation internalProject("micro_utils.repos.ktor.client")
implementation internalProject("micro_utils.repos.ktor.server")
implementation internalProject("micro_utils.repos.inmemory")
implementation libs.kt.coroutines.test
implementation libs.ktor.server.cio
implementation libs.ktor.client.cio
implementation libs.ktor.server.content.negotiation
implementation libs.ktor.serialization.kotlinx.json
implementation libs.ktor.client.content.negotiation
implementation libs.ktor.client.logging
implementation libs.ktor.client.websockets
}
}
}
}

View File

@@ -0,0 +1,3 @@
package dev.inmo.micro_utils.repos.ktor.common
const val containsRoute = "contains"

View File

@@ -0,0 +1,3 @@
package dev.inmo.micro_utils.repos.ktor.common
const val countRoute = "count"

View File

@@ -0,0 +1,3 @@
package dev.inmo.micro_utils.repos.ktor.common
const val countRouting = "count"

View File

@@ -1,5 +1,6 @@
package dev.inmo.micro_utils.repos.ktor.common
const val idParameterName = "id"
const val keyParameterName = "key"
const val valueParameterName = "value"
const val reversedParameterName = "reversed"

View File

@@ -3,4 +3,3 @@ package dev.inmo.micro_utils.repos.ktor.common.crud
const val getByPaginationRouting = "getByPagination"
const val getByIdRouting = "getById"
const val containsRouting = "contains"
const val countRouting = "count"

View File

@@ -3,8 +3,8 @@ package dev.inmo.micro_utils.repos.ktor.common.key_value
const val getRoute = "get"
const val valuesRoute = "values"
const val keysRoute = "keys"
const val containsRoute = "contains"
const val countRoute = "count"
const val containsRoute = dev.inmo.micro_utils.repos.ktor.common.containsRoute
const val countRoute = dev.inmo.micro_utils.repos.ktor.common.countRoute
const val onNewValueRoute = "onNewValue"
const val onValueRemovedRoute = "onValueRemoved"

View File

@@ -5,7 +5,7 @@ const val keysRoute = "keys"
const val containsByKeyRoute = "containsByKey"
const val containsByKeyValueRoute = "containsByKeyValue"
const val countByKeyRoute = "countByKey"
const val countRoute = "count"
const val countRoute = dev.inmo.micro_utils.repos.ktor.common.countRoute
const val onNewValueRoute = "onNewValue"
const val onValueRemovedRoute = "onValueRemoved"

View File

@@ -0,0 +1,89 @@
import dev.inmo.micro_utils.repos.*
import dev.inmo.micro_utils.repos.ktor.client.crud.KtorCRUDRepoClient
import dev.inmo.micro_utils.repos.ktor.server.crud.configureCRUDRepoRoutes
import io.ktor.client.HttpClient
import io.ktor.client.plugins.logging.Logging
import io.ktor.http.ContentType
import io.ktor.serialization.kotlinx.KotlinxWebsocketSerializationConverter
import io.ktor.serialization.kotlinx.json.json
import io.ktor.server.application.install
import io.ktor.server.cio.CIO
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
import io.ktor.server.routing.routing
import io.ktor.server.websocket.WebSockets
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.json.Json
import kotlin.test.Test
import kotlin.test.assertEquals
class CRUDTests {
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun testCRUDFunctions() {
runTest {
val map = mutableMapOf<Int, ComplexData>()
val repo = MapCRUDRepo<ComplexData, Int, SimpleData>(
map,
{ newValue, id, oldValue ->
oldValue.copy(title = newValue.title)
}
) {
size to ComplexData(size, title = it.title)
}
val server = io.ktor.server.engine.embeddedServer(
CIO,
23456,
"127.0.0.1"
) {
install(ContentNegotiation) {
json()
}
install(WebSockets) {
contentConverter = KotlinxWebsocketSerializationConverter(Json)
}
routing {
configureCRUDRepoRoutes(
repo
) {
it.toInt()
}
}
}.start(false)
val client = HttpClient {
install(io.ktor.client.plugins.contentnegotiation.ContentNegotiation) {
json()
}
install(Logging)
install(io.ktor.client.plugins.websocket.WebSockets) {
contentConverter = KotlinxWebsocketSerializationConverter(Json)
}
}
val crudClient = KtorCRUDRepoClient<ComplexData, Int, SimpleData>(
"http://127.0.0.1:23456",
client,
ContentType.Application.Json
) {
it.toString()
}
val created = crudClient.create(SimpleData("Example")).single()
assertEquals(map.size, 1)
assertEquals(map.size.toLong(), crudClient.count())
assertEquals(1, crudClient.count())
assertEquals(map.getValue(map.keys.first()), created)
val updated = crudClient.update(created.id, SimpleData("Example2"))
assertEquals(map.size, 1)
assertEquals(map.size.toLong(), crudClient.count())
assertEquals(1, crudClient.count())
assertEquals(map.getValue(map.keys.first()), updated)
crudClient.deleteById(created.id)
assertEquals(map.size, 0)
assertEquals(map.size.toLong(), crudClient.count())
assertEquals(0, crudClient.count())
server.stop()
}
}
}

View File

@@ -0,0 +1,10 @@
import com.benasher44.uuid.uuid4
import kotlinx.serialization.Serializable
@Serializable
data class ComplexData(
val id: Int,
val simple: SimpleData = SimpleData(),
val simples: List<SimpleData> = (0 until 100).map { SimpleData(("$it")) },
val title: String = uuid4().toString()
)

View File

@@ -0,0 +1,139 @@
import dev.inmo.micro_utils.pagination.firstPageWithOneElementPagination
import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging
import dev.inmo.micro_utils.repos.*
import dev.inmo.micro_utils.repos.ktor.client.key.value.KtorKeyValueRepoClient
import dev.inmo.micro_utils.repos.ktor.server.key.value.configureKeyValueRepoRoutes
import io.ktor.client.HttpClient
import io.ktor.client.plugins.logging.Logging
import io.ktor.http.ContentType
import io.ktor.serialization.kotlinx.KotlinxWebsocketSerializationConverter
import io.ktor.serialization.kotlinx.json.json
import io.ktor.server.application.install
import io.ktor.server.cio.CIO
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
import io.ktor.server.routing.routing
import io.ktor.server.websocket.WebSockets
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.json.Json
import kotlin.test.*
class KVTests {
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun testKVFunctions() {
runTest {
val map = mutableMapOf<Int, ComplexData>()
val repo = MapKeyValueRepo<Int, ComplexData>(map)
val server = io.ktor.server.engine.embeddedServer(
CIO,
23456,
"127.0.0.1"
) {
install(ContentNegotiation) {
json()
}
install(WebSockets) {
contentConverter = KotlinxWebsocketSerializationConverter(Json)
}
routing {
configureKeyValueRepoRoutes(
repo,
Int.serializer(),
ComplexData.serializer(),
Json {}
)
}
}.start(false)
val client = HttpClient {
install(io.ktor.client.plugins.contentnegotiation.ContentNegotiation) {
json()
}
install(Logging)
install(io.ktor.client.plugins.websocket.WebSockets) {
contentConverter = KotlinxWebsocketSerializationConverter(Json)
}
}
val crudClient = KtorKeyValueRepoClient<Int, ComplexData>(
"http://127.0.0.1:23456",
client,
ContentType.Application.Json,
Int.serializer(),
ComplexData.serializer(),
Json
)
val dataInOneKey = ComplexData(1, title = "Example1")
val dataInMultipleKeys = ComplexData(2, title = "Example2")
val repeatCount = 3
val dataList = listOf(
1 to dataInOneKey
) + (0 until repeatCount).map {
(it + 2) to dataInMultipleKeys
}
dataList.forEachIndexed { i, (id, data) ->
crudClient.set(id, data)
assertEquals(map.size, i + 1)
assertEquals(map.size.toLong(), crudClient.count())
assertEquals(i + 1L, crudClient.count())
dataList.take(i + 1).forEach { (id, data) ->
assertEquals(data, map[id])
assertEquals(data, crudClient.get(id))
assertEquals(map[id], crudClient.get(id))
}
}
dataList.forEach { (id, data) ->
assertTrue(crudClient.contains(id))
assertEquals(data, crudClient.get(id))
}
assertEquals(
dataList.mapNotNull { if (it.second == dataInMultipleKeys) it.first else null },
getAllWithNextPaging(firstPageWithOneElementPagination) {
crudClient.keys(dataInMultipleKeys, it)
}
)
assertEquals(
dataList.mapNotNull { if (it.second == dataInOneKey) it.first else null },
getAllWithNextPaging(firstPageWithOneElementPagination) {
crudClient.keys(dataInOneKey, it)
}
)
assertEquals(
dataList.map { it.first },
getAllWithNextPaging(firstPageWithOneElementPagination) {
crudClient.keys(it)
}
)
assertEquals(
dataList.map { it.second },
getAllWithNextPaging(firstPageWithOneElementPagination) {
crudClient.values(it)
}
)
assertEquals(dataList.size.toLong(), crudClient.count())
crudClient.unsetWithValues(dataInMultipleKeys)
assertEquals(
dataList.filter { it.second == dataInOneKey }.size.toLong(),
crudClient.count()
)
crudClient.unset(dataList.first { it.second == dataInOneKey }.first)
assertEquals(
0,
crudClient.count()
)
server.stop()
}
}
}

View File

@@ -0,0 +1,140 @@
import dev.inmo.micro_utils.pagination.firstPageWithOneElementPagination
import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging
import dev.inmo.micro_utils.repos.*
import dev.inmo.micro_utils.repos.ktor.client.key.values.KtorKeyValuesRepoClient
import dev.inmo.micro_utils.repos.ktor.server.key.values.configureKeyValuesRepoRoutes
import io.ktor.client.HttpClient
import io.ktor.client.plugins.logging.Logging
import io.ktor.http.ContentType
import io.ktor.serialization.kotlinx.KotlinxWebsocketSerializationConverter
import io.ktor.serialization.kotlinx.json.json
import io.ktor.server.application.install
import io.ktor.server.cio.CIO
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
import io.ktor.server.routing.routing
import io.ktor.server.websocket.WebSockets
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.json.Json
import kotlin.test.*
class KVsTests {
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun testKVsFunctions() {
runTest {
val map = mutableMapOf<Int, MutableList<ComplexData>>()
val repo = MapKeyValuesRepo(map)
val server = io.ktor.server.engine.embeddedServer(
CIO,
23456,
"127.0.0.1"
) {
install(ContentNegotiation) {
json()
}
install(WebSockets) {
contentConverter = KotlinxWebsocketSerializationConverter(Json)
}
routing {
configureKeyValuesRepoRoutes(
repo,
Int.serializer(),
ComplexData.serializer(),
Json {}
)
}
}.start(false)
val client = HttpClient {
install(io.ktor.client.plugins.contentnegotiation.ContentNegotiation) {
json()
}
install(Logging)
install(io.ktor.client.plugins.websocket.WebSockets) {
contentConverter = KotlinxWebsocketSerializationConverter(Json)
}
}
val crudClient = KtorKeyValuesRepoClient(
"http://127.0.0.1:23456",
client,
ContentType.Application.Json,
Int.serializer(),
ComplexData.serializer(),
Json
)
val dataInOneKey = ComplexData(1, title = "Example1")
val dataInMultipleKeys = ComplexData(2, title = "Example2")
val repeatCount = 3
val dataList = listOf(
1 to listOf(dataInOneKey)
) + (0 until repeatCount).map {
(it + 2) to listOf(dataInMultipleKeys)
}
dataList.forEachIndexed { i, (id, data) ->
crudClient.set(id, data)
assertEquals(i + 1, map.size)
assertEquals(map.size.toLong(), crudClient.count())
assertEquals(i + 1L, crudClient.count())
dataList.take(i + 1).forEach { (id, data) ->
assertContentEquals(data, map[id])
assertContentEquals(data, crudClient.getAll(id))
assertContentEquals(map[id], crudClient.getAll(id))
}
}
dataList.forEach { (key, data) ->
assertTrue(crudClient.contains(key))
assertContentEquals(data, crudClient.getAll(key))
}
assertEquals(
dataList.mapNotNull { if (it.second.contains(dataInMultipleKeys)) it.first else null },
getAllWithNextPaging(firstPageWithOneElementPagination) {
crudClient.keys(dataInMultipleKeys, it)
}
)
assertEquals(
dataList.mapNotNull { if (it.second.contains(dataInOneKey)) it.first else null },
getAllWithNextPaging(firstPageWithOneElementPagination) {
crudClient.keys(dataInOneKey, it)
}
)
assertEquals(
dataList.map { it.first },
getAllWithNextPaging(firstPageWithOneElementPagination) {
crudClient.keys(it)
}
)
assertEquals(
dataList.map { it.first },
getAllWithNextPaging(firstPageWithOneElementPagination) {
crudClient.keys(it)
}
)
assertEquals(dataList.size.toLong(), crudClient.count())
crudClient.remove(dataList.filter { it.second.contains(dataInMultipleKeys) })
println(map)
assertEquals(
dataList.filter { it.second.contains(dataInOneKey) }.size.toLong(),
crudClient.count()
)
crudClient.remove(dataList.filter { it.second.contains(dataInOneKey) })
assertEquals(
0,
crudClient.count()
)
server.stop()
}
}
}

View File

@@ -0,0 +1,7 @@
import com.benasher44.uuid.uuid4
import kotlinx.serialization.Serializable
@Serializable
data class SimpleData(
val title: String = uuid4().toString()
)

198
repos/ktor/crud.yml Normal file
View File

@@ -0,0 +1,198 @@
openapi: "3.0.0"
info:
description: "This is a template for the CRUD repositories from [microutils](https://github.com/InsanusMokrassar/MicroUtils/tree/master/repos/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/repos/ktor/server/crud)"
version: "0.11.3"
title: "CRUD Repo"
contact:
email: "ovsyannikov.alexey95@gmail.com"
tags:
- name: "Read"
description: "Operations with `get` request in most cases"
- name: "Write"
description: "Operations with `post` request in most cases"
components:
parameters:
IdInQuery:
in: "query"
name: "id"
schema:
$ref: "#/components/schemas/Key"
PaginationInQueryPage:
in: "query"
name: "ppage"
description: "Page of pagination"
schema:
type: integer
required: false
PaginationInQuerySize:
in: "query"
name: "psize"
description: "Size of each page in pagination"
schema:
type: integer
required: false
schemas:
Key:
type: integer
description: "REWRITE THIS TYPE AS KEY IN SWAGGER FILE"
Value:
type: integer
description: "REWRITE THIS TYPE AS VALUE IN SWAGGER FILE"
NewValue:
type: integer
description: "REWRITE THIS TYPE AS NEW VALUE IN SWAGGER FILE"
Pair:
type: object
description: "Pair of objects"
properties:
first:
second:
PaginationResult:
type: object
properties:
page:
type: integer
description: "Page of pagination"
pagesNumber:
type: integer
description: "Count of pages with the size from this pagination"
size:
type: integer
description: "Size of each page in pagination"
results:
type: array
description: "Array of all elements on that page. Size of pagination and size of array can be different and it can be interpreted like current page is the last one"
items:
type: object
paths:
/getByPagination:
get:
tags:
- "Read"
parameters:
- $ref: "#/components/parameters/PaginationInQueryPage"
- $ref: "#/components/parameters/PaginationInQuerySize"
responses:
"200":
description: "Pagination with elements"
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/PaginationResult"
- properties:
results:
items:
$ref: "#/components/schemas/Value"
/getById:
get:
tags:
- "Read"
parameters:
- $ref: "#/components/parameters/IdInQuery"
required: true
responses:
"200":
description: "Result object"
content:
application/json:
schema:
$ref: "#/components/schemas/Value"
"204":
description: "No value by id"
/contains:
get:
tags:
- "Read"
parameters:
- $ref: "#/components/parameters/IdInQuery"
required: true
responses:
"200":
description: "Object with id availability in repo"
content:
application/json:
schema:
type: boolean
/count:
get:
tags:
- "Read"
responses:
"200":
description: "Amount of objects in repo"
content:
application/json:
schema:
type: integer
/create:
post:
tags:
- "Write"
requestBody:
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/NewValue"
responses:
"200":
description: "Objects has been created and saved"
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Value"
/update:
post:
tags:
- "Write"
requestBody:
content:
application/json:
schema:
type: array
items:
allOf:
- $ref: "#/components/schemas/Pair"
- properties:
first:
$ref: "#/components/schemas/Key"
second:
$ref: "#/components/schemas/NewValue"
responses:
"200":
description: "Objects has been updated"
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Value"
/deleteById:
post:
tags:
- "Write"
requestBody:
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Key"
responses:
"200":
description: "Objects has been updated"
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Value"

201
repos/ktor/kv.yml Normal file
View File

@@ -0,0 +1,201 @@
openapi: "3.0.0"
info:
description: "This is a template for the KeyValue repositories from [microutils](https://github.com/InsanusMokrassar/MicroUtils/tree/master/repos/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/repos/ktor/server/key/value)"
version: "0.11.1"
title: "KeyValue Repo"
contact:
email: "ovsyannikov.alexey95@gmail.com"
tags:
- name: "Read"
description: "Operations with `get` request in most cases"
- name: "Write"
description: "Operations with `post` request in most cases"
components:
parameters:
KeyInQuery:
in: "query"
name: "key"
schema:
$ref: "#/components/schemas/Key"
PaginationInQueryPage:
in: "query"
name: "ppage"
description: "Page of pagination"
schema:
type: integer
required: false
PaginationInQuerySize:
in: "query"
name: "psize"
description: "Size of each page in pagination"
schema:
type: integer
required: false
ReversedInQuery:
in: "query"
name: "reversed"
description: "If passed, will tell to reverse the result pages"
schema:
type: boolean
required: false
ValueInQuery:
in: "query"
name: "value"
schema:
$ref: "#/components/schemas/Value"
schemas:
Key:
type: integer
description: "REWRITE THIS TYPE AS KEY IN SWAGGER FILE"
Value:
type: integer
description: "REWRITE THIS TYPE AS VALUE IN SWAGGER FILE"
PaginationResult:
type: object
properties:
page:
type: integer
description: "Page of pagination"
pagesNumber:
type: integer
description: "Count of pages with the size from this pagination"
size:
type: integer
description: "Size of each page in pagination"
results:
type: array
description: "Array of all elements on that page. Size of pagination and size of array can be different and it can be interpreted like current page is the last one"
items:
type: object
paths:
/get:
get:
tags:
- "Read"
parameters:
- allOf:
- $ref: "#/components/parameters/KeyInQuery"
- required: true
responses:
"200":
description: "Element by key"
content:
"*/*":
schema:
$ref: "#/components/schemas/Value"
"204":
description: "No value by id"
/values:
get:
tags:
- "Read"
parameters:
- $ref: "#/components/parameters/PaginationInQueryPage"
- $ref: "#/components/parameters/PaginationInQuerySize"
- $ref: "#/components/parameters/ReversedInQuery"
responses:
"200":
description: "Pagination with elements"
content:
"*/*":
schema:
allOf:
- $ref: "#/components/schemas/PaginationResult"
- properties:
results:
items:
$ref: "#/components/schemas/Value"
/keys:
get:
tags:
- "Read"
parameters:
- $ref: "#/components/parameters/PaginationInQueryPage"
- $ref: "#/components/parameters/PaginationInQuerySize"
- $ref: "#/components/parameters/ReversedInQuery"
- $ref: "#/components/parameters/ValueInQuery"
required: false
responses:
"200":
description: "Pagination with elements"
content:
"*/*":
schema:
allOf:
- $ref: "#/components/schemas/PaginationResult"
- properties:
results:
items:
$ref: "#/components/schemas/Key"
/contains:
get:
tags:
- "Read"
parameters:
- $ref: "#/components/parameters/KeyInQuery"
required: true
responses:
"200":
description: "Object with id availability in repo"
content:
"*/*":
schema:
type: boolean
/count:
get:
tags:
- "Read"
responses:
"200":
description: "Amount of objects in repo"
content:
"*/*":
schema:
type: integer
/set:
post:
tags:
- "Write"
requestBody:
content:
"*/*":
schema:
type: object
additionalProperties:
$ref: "#/components/schemas/Value"
description: "Map with new elements to set. Use keys as a keys of this map"
responses:
"200":
description: "Will return 200 if everything has been completed ok"
/unset:
post:
tags:
- "Write"
requestBody:
content:
"*/*":
schema:
type: array
items:
$ref: "#/components/schemas/Key"
responses:
"200":
description: "Objects with keys from body has been unset"
/unsetWithValues:
post:
tags:
- "Write"
requestBody:
content:
"*/*":
schema:
type: array
items:
$ref: "#/components/schemas/Value"
responses:
"200":
description: "Objects with values from body has been unset"

222
repos/ktor/kvs.yml Normal file
View File

@@ -0,0 +1,222 @@
swagger: "2.0"
info:
description: "This is a template for the KeyValues repositories from [microutils](https://github.com/InsanusMokrassar/MicroUtils/tree/master/repos/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/repos/ktor/server/key/values)"
version: "0.11.0"
title: "KeyValues Repo"
contact:
email: "ovsyannikov.alexey95@gmail.com"
tags:
- name: "Read"
description: "Operations with `get` request in most cases"
- name: "Write"
description: "Operations with `post` request in most cases"
parameters:
KeyInQuery:
in: "query"
name: "key"
allOf:
- $ref: "#/definitions/Key"
KeyInBody:
in: "body"
name: "body"
allOf:
- $ref: "#/definitions/Key"
KeysInBody:
in: "body"
name: "body"
type: array
items:
$ref: "#/definitions/Key"
ValuesInBody:
in: "body"
name: "body"
type: array
items:
$ref: "#/definitions/Value"
PaginationInQueryPage:
in: "query"
type: integer
name: "ppage"
description: "Page of pagination"
required: false
PaginationInQuerySize:
in: "query"
type: integer
name: "psize"
description: "Size of each page in pagination"
required: false
ReversedInQuery:
in: "query"
type: boolean
name: "reversed"
description: "If passed, will tell to reverse the result pages"
required: false
ValueInQuery:
in: "query"
name: "value"
allOf:
- $ref: "#/definitions/Value"
ValueInBody:
in: "body"
name: "body"
allOf:
- $ref: "#/definitions/Value"
MapInBody:
in: "body"
name: "body"
allOf:
- $ref: "#/definitions/Map"
definitions:
Key:
type: integer
description: "REWRITE THIS TYPE AS KEY IN SWAGGER FILE"
Value:
type: integer
description: "REWRITE THIS TYPE AS VALUE IN SWAGGER FILE"
Map:
type: object
description: "Map of objects"
PaginationResult:
type: object
properties:
page:
type: integer
description: "Page of pagination"
pagesNumber:
type: integer
description: "Count of pages with the size from this pagination"
size:
type: integer
description: "Size of each page in pagination"
results:
type: array
description: "Array of all elements on that page. Size of pagination and size of array can be different and it can be interpreted like current page is the last one"
items:
type: object
paths:
/get:
get:
tags:
- "Read"
parameters:
- $ref: "#/parameters/KeyInQuery"
required: true
- $ref: "#/parameters/PaginationInQueryPage"
- $ref: "#/parameters/PaginationInQuerySize"
- $ref: "#/parameters/ReversedInQuery"
responses:
"200":
description: "Elements by query"
schema:
allOf:
- $ref: "#/definitions/PaginationResult"
- properties:
results:
items:
$ref: "#/definitions/Value"
/keys:
get:
tags:
- "Read"
parameters:
- $ref: "#/parameters/PaginationInQueryPage"
- $ref: "#/parameters/PaginationInQuerySize"
- $ref: "#/parameters/ReversedInQuery"
- $ref: "#/parameters/ValueInQuery"
required: false
responses:
"200":
description: "Pagination with elements"
schema:
allOf:
- $ref: "#/definitions/PaginationResult"
- properties:
results:
items:
$ref: "#/definitions/Key"
/contains:
get:
tags:
- "Read"
parameters:
- $ref: "#/parameters/KeyInQuery"
required: true
- $ref: "#/parameters/ValueInQuery"
required: false
responses:
"200":
description: "Object with key and optionally availability in repo"
schema:
type: boolean
/count:
get:
tags:
- "Read"
responses:
"200":
description: "Amount of objects in repo"
schema:
type: integer
/add:
post:
tags:
- "Write"
parameters:
- allOf:
- $ref: "#/parameters/MapInBody"
- additionalProperties:
$ref: "#/definitions/Value"
description: "Map with new elements to add. Use keys as a keys of this map. That values will be added to the end of current data by their keys"
responses:
"200":
description: "Will return 200 if everything has been completed ok"
/set:
post:
tags:
- "Write"
parameters:
- allOf:
- $ref: "#/parameters/MapInBody"
- additionalProperties:
$ref: "#/definitions/Value"
description: "Map with new elements to set. Use keys as a keys of this map. That values will overwrite all exists data by their keys"
responses:
"200":
description: "Will return 200 if everything has been completed ok"
/remove:
post:
tags:
- "Write"
parameters:
- allOf:
- $ref: "#/parameters/MapInBody"
- additionalProperties:
$ref: "#/definitions/Value"
description: "Map with data to remove. Removing will be processed by each value for its key"
responses:
"200":
description: "Objects with keys and values from body has been unset"
/clear:
post:
tags:
- "Write"
parameters:
- $ref: "#/parameters/KeyInBody"
responses:
"200":
description: "Data with corresponding key has been removed"
/clearWithValues:
post:
tags:
- "Write"
parameters:
- $ref: "#/parameters/ValueInBody"
responses:
"200":
description: "Will remove value from all keys data"

Some files were not shown because too many files have changed in this diff Show More