mirror of
https://github.com/InsanusMokrassar/MicroUtils.git
synced 2025-09-17 14:29:24 +00:00
Compare commits
98 Commits
Author | SHA1 | Date | |
---|---|---|---|
62df81bb4e | |||
0a8e0f6178 | |||
f705020aaa | |||
78bd3b9853 | |||
992b283597 | |||
8fc1ff1d59 | |||
3bbde61f39 | |||
8d955c4b9d | |||
18593c530b | |||
78903cd4eb | |||
eaa143f7d7 | |||
bcb0e42fa2 | |||
8eed435302 | |||
0e4a63057f | |||
1af5faa440 | |||
7412217b0c | |||
3e749d75b7 | |||
846b5c87c9 | |||
7b5e84f80b | |||
27822a8a66 | |||
36f4e7ec37 | |||
187e84ad65 | |||
195fe221c4 | |||
6f9c19bbf6 | |||
65bdab4f7e | |||
c4d5fcfc22 | |||
43232afa62 | |||
18908a01d7 | |||
b22fbfb3bc | |||
57aaea88b6 | |||
140949c5ea | |||
639241e0d3 | |||
e0eb42bc2d | |||
7990b21cc5 | |||
2ba5c97709 | |||
33b7c85fc2 | |||
7f6c02ffdf | |||
f368616e6f | |||
dd632f4203 | |||
a8f3ae501b | |||
ea76963ac2 | |||
99dfa97958 | |||
f68270a5b3 | |||
542ed81034 | |||
404a11f5e7 | |||
411221070e | |||
25d35d0c76 | |||
c72904d61c | |||
b94c9acd26 | |||
bdb4988569 | |||
1dafd1352a | |||
31fbd3cad7 | |||
848bc5ec10 | |||
4d0ad826a0 | |||
6955820fcc | |||
ef530624b9 | |||
9b9e7dd88f | |||
a13cc9e961 | |||
0d2b923378 | |||
fba84c8ac8 | |||
db10fe1b2c | |||
175dd980f8 | |||
8364020671 | |||
eba44cd394 | |||
b3bac8015a | |||
0b48afd251 | |||
19857930a4 | |||
d0dbe3ed2f | |||
8b7e78b63a | |||
92a4ecb523 | |||
6a5ad4d728 | |||
be4aa8daac | |||
b5eac37782 | |||
b1ad3c5a39 | |||
ba16bad029 | |||
ca8ae4cd72 | |||
53d35d74b3 | |||
49c139e235 | |||
caf9c821f3 | |||
ca4c6db96f | |||
6b2298c752 | |||
a1bf43def9 | |||
15e9254e00 | |||
afe5a72c6f | |||
750a8b9ecf | |||
27fc3f93e0 | |||
8166d4b99b | |||
b61d2ae2eb | |||
4790fe0aea | |||
bc37b11cee | |||
223fed910f | |||
b85ab7b061 | |||
888dc299c9 | |||
e113dc28ed | |||
31e55d2307 | |||
e90645f248 | |||
4bb7ba2571 | |||
8d31c25bf8 |
121
CHANGELOG.md
121
CHANGELOG.md
@@ -1,5 +1,126 @@
|
||||
# Changelog
|
||||
|
||||
## 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`
|
||||
* `Ktor`: `2.0.1` -> `2.0.2`
|
||||
* `Common`
|
||||
* `JS`:
|
||||
* Add `ResizeObserver` functionality
|
||||
|
||||
## 0.10.5
|
||||
|
||||
* `Versions`
|
||||
* `Compose`: `1.2.0-alpha01-dev683` -> `1.2.0-alpha01-dev686`
|
||||
* `Repos`
|
||||
* `Android`:
|
||||
* New function `SharedPreferencesKeyValueRepo`
|
||||
* `FSM`
|
||||
* Add `StateHandlingErrorHandler` and opportunity to handle states handling errors
|
||||
|
||||
## 0.10.4
|
||||
|
||||
* `Versions`:
|
||||
* `Serialization`: `1.3.2` -> `1.3.3`
|
||||
|
||||
## 0.10.3
|
||||
|
||||
* `Versions`:
|
||||
* `Compose`: `1.2.0-alpha01-dev682` -> `1.2.0-alpha01-dev683`
|
||||
* `Coroutines`:
|
||||
* Fixes in `AccumulatorFlow`
|
||||
|
||||
## 0.10.2
|
||||
|
||||
* `Versions`:
|
||||
* `Compose`: `1.2.0-alpha01-dev675` -> `1.2.0-alpha01-dev682`
|
||||
|
||||
## 0.10.1
|
||||
|
||||
* `Versions`:
|
||||
* `Ktor`: `2.0.0` -> `2.0.1`
|
||||
* `Crypto`:
|
||||
* Add `hmacSha256`
|
||||
* Add `hex`
|
||||
|
||||
## 0.10.0
|
||||
|
||||
* `Versions`:
|
||||
* `Kotlin`: `1.6.10` -> `1.6.21`
|
||||
* `Compose`: `1.1.1` -> `1.2.0-alpha01-dev675`
|
||||
* `Exposed`: `0.37.3` -> `0.38.2`
|
||||
* `Ktor`: `1.6.8` -> `2.0.0`
|
||||
* `Dokka`: `1.6.10` -> `1.6.21`
|
||||
|
||||
## 0.9.24
|
||||
|
||||
* `Ktor`:
|
||||
* `Common`:
|
||||
* New extension fun `MPPFile#input`
|
||||
|
||||
## 0.9.23
|
||||
|
||||
* `Repos`:
|
||||
* `Exposed`:
|
||||
* New property `ExposedRepo#selectAll` to retrieve all the rows in the table
|
||||
|
||||
## 0.9.22
|
||||
|
||||
* `Ktor`:
|
||||
* `Server`:
|
||||
* Now `createKtorServer` fun is fully customizable
|
||||
|
||||
## 0.9.21
|
||||
|
||||
* `Repos`:
|
||||
* `Exposed`:
|
||||
* fixes in `AbstractExposedWriteCRUDRepo`
|
||||
|
||||
## 0.9.20
|
||||
|
||||
* `Repos`:
|
||||
* `Common`:
|
||||
* Fixes in `OneToManyAndroidRepo`
|
||||
* New `CursorIterator`
|
||||
|
||||
## 0.9.19
|
||||
|
||||
* `Versions`:
|
||||
* `Coroutines`: `1.6.0` -> `1.6.1`
|
||||
* `Repos`:
|
||||
* `Exposed`:
|
||||
* Fixes in `ExposedStandardVersionsRepoProxy`
|
||||
|
||||
## 0.9.18
|
||||
|
||||
* `Common`
|
||||
* New extensions for `Element`: `Element#onActionOutside` and `Element#onClickOutside`
|
||||
|
||||
## 0.9.17
|
||||
|
||||
* `Common`:
|
||||
|
@@ -21,6 +21,7 @@ allprojects {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
google()
|
||||
maven { url "https://maven.pkg.jetbrains.space/public/p/compose/dev" }
|
||||
}
|
||||
|
||||
// temporal crutch until legacy tests will be stabled or legacy target will be removed
|
||||
|
@@ -1,3 +1,5 @@
|
||||
@file:Suppress("OPT_IN_IS_NOT_ENABLED")
|
||||
|
||||
package dev.inmo.micro_utils.common
|
||||
|
||||
@RequiresOptIn(
|
||||
|
@@ -43,6 +43,7 @@ private inline fun <T> performChanges(
|
||||
if (oldOneEqualToNewObject || newOneEqualToOldObject) {
|
||||
changedList.addAll(
|
||||
potentialChanges.take(i).mapNotNull {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
if (it.first != null && it.second != null) it as Pair<IndexedValue<T>, IndexedValue<T>> else null
|
||||
}
|
||||
)
|
||||
@@ -121,7 +122,10 @@ fun <T> Iterable<T>.calculateDiff(
|
||||
|
||||
when {
|
||||
oldObject === newObject || (oldObject == newObject && !strictComparison) -> {
|
||||
changedObjects.addAll(potentiallyChangedObjects.map { it as Pair<IndexedValue<T>, IndexedValue<T>> })
|
||||
changedObjects.addAll(potentiallyChangedObjects.map {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
it as Pair<IndexedValue<T>, IndexedValue<T>>
|
||||
})
|
||||
potentiallyChangedObjects.clear()
|
||||
}
|
||||
else -> {
|
||||
|
@@ -27,20 +27,13 @@ sealed interface Either<T1, T2> {
|
||||
@Deprecated("Use optionalT2 instead", ReplaceWith("optionalT2"))
|
||||
val t2: T2?
|
||||
get() = optionalT2.dataOrNull()
|
||||
|
||||
companion object {
|
||||
fun <T1, T2> serializer(
|
||||
t1Serializer: KSerializer<T1>,
|
||||
t2Serializer: KSerializer<T2>,
|
||||
): KSerializer<Either<T1, T2>> = EitherSerializer(t1Serializer, t2Serializer)
|
||||
}
|
||||
}
|
||||
|
||||
class EitherSerializer<T1, T2>(
|
||||
t1Serializer: KSerializer<T1>,
|
||||
t2Serializer: KSerializer<T2>,
|
||||
) : KSerializer<Either<T1, T2>> {
|
||||
@OptIn(ExperimentalSerializationApi::class, InternalSerializationApi::class)
|
||||
@OptIn(InternalSerializationApi::class)
|
||||
override val descriptor: SerialDescriptor = buildSerialDescriptor(
|
||||
"TypedSerializer",
|
||||
SerialKind.CONTEXTUAL
|
||||
@@ -51,7 +44,6 @@ class EitherSerializer<T1, T2>(
|
||||
private val t1EitherSerializer = EitherFirst.serializer(t1Serializer, t2Serializer)
|
||||
private val t2EitherSerializer = EitherSecond.serializer(t1Serializer, t2Serializer)
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class, InternalSerializationApi::class)
|
||||
override fun deserialize(decoder: Decoder): Either<T1, T2> {
|
||||
return decoder.decodeStructure(descriptor) {
|
||||
var type: String? = null
|
||||
@@ -83,7 +75,6 @@ class EitherSerializer<T1, T2>(
|
||||
}
|
||||
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class, InternalSerializationApi::class)
|
||||
override fun serialize(encoder: Encoder, value: Either<T1, T2>) {
|
||||
encoder.encodeStructure(descriptor) {
|
||||
when (value) {
|
||||
|
@@ -32,7 +32,7 @@ class DiffUtilsTests {
|
||||
val withIndex = oldList.withIndex()
|
||||
|
||||
for (count in 1 .. (floor(oldList.size.toFloat() / 2).toInt())) {
|
||||
for ((i, v) in withIndex) {
|
||||
for ((i, _) in withIndex) {
|
||||
if (i + count > oldList.lastIndex) {
|
||||
continue
|
||||
}
|
||||
@@ -55,7 +55,7 @@ class DiffUtilsTests {
|
||||
val withIndex = oldList.withIndex()
|
||||
|
||||
for (step in oldList.indices) {
|
||||
for ((i, v) in withIndex) {
|
||||
for ((i, _) in withIndex) {
|
||||
val mutable = oldList.toMutableList()
|
||||
val changes = (
|
||||
if (step == 0) i until oldList.size else (i until oldList.size step step)
|
||||
@@ -104,7 +104,7 @@ class DiffUtilsTests {
|
||||
val withIndex = oldList.withIndex()
|
||||
|
||||
for (count in 1 .. (floor(oldList.size.toFloat() / 2).toInt())) {
|
||||
for ((i, v) in withIndex) {
|
||||
for ((i, _) in withIndex) {
|
||||
if (i + count > oldList.lastIndex) {
|
||||
continue
|
||||
}
|
||||
@@ -129,15 +129,20 @@ class DiffUtilsTests {
|
||||
val withIndex = oldList.withIndex()
|
||||
|
||||
for (step in oldList.indices) {
|
||||
for ((i, v) in withIndex) {
|
||||
for ((i, _) in withIndex) {
|
||||
val mutable = oldList.toMutableList()
|
||||
val changes = (
|
||||
if (step == 0) i until oldList.size else (i until oldList.size step step)
|
||||
).map { index ->
|
||||
|
||||
val newList = if (step == 0) {
|
||||
i until oldList.size
|
||||
} else {
|
||||
i until oldList.size step step
|
||||
}
|
||||
newList.forEach { index ->
|
||||
IndexedValue(index, mutable[index]) to IndexedValue(index, "changed$index").also {
|
||||
mutable[index] = it.value
|
||||
}
|
||||
}
|
||||
|
||||
val mutableOldList = oldList.toMutableList()
|
||||
mutableOldList.applyDiff(mutable)
|
||||
assertEquals(
|
||||
|
@@ -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
|
@@ -0,0 +1,38 @@
|
||||
package dev.inmo.micro_utils.common
|
||||
|
||||
import kotlinx.browser.document
|
||||
import org.w3c.dom.*
|
||||
import org.w3c.dom.events.Event
|
||||
import org.w3c.dom.events.EventListener
|
||||
|
||||
fun Element.onActionOutside(type: String, options: dynamic = null, callback: (Event) -> Unit): EventListener {
|
||||
lateinit var observer: MutationObserver
|
||||
val listener = EventListener {
|
||||
val elementsToCheck = mutableListOf<Element>(this@onActionOutside)
|
||||
while (it.target != this@onActionOutside && elementsToCheck.isNotEmpty()) {
|
||||
val childrenGettingElement = elementsToCheck.removeFirst()
|
||||
for (i in 0 until childrenGettingElement.childElementCount) {
|
||||
elementsToCheck.add(childrenGettingElement.children[i] ?: continue)
|
||||
}
|
||||
}
|
||||
if (elementsToCheck.isEmpty()) {
|
||||
callback(it)
|
||||
}
|
||||
}
|
||||
if (options == null) {
|
||||
document.addEventListener(type, listener)
|
||||
} else {
|
||||
document.addEventListener(type, listener, options)
|
||||
}
|
||||
observer = onRemoved {
|
||||
if (options == null) {
|
||||
document.removeEventListener(type, listener)
|
||||
} else {
|
||||
document.removeEventListener(type, listener, options)
|
||||
}
|
||||
observer.disconnect()
|
||||
}
|
||||
return listener
|
||||
}
|
||||
|
||||
fun Element.onClickOutside(options: dynamic = null, callback: (Event) -> Unit) = onActionOutside("click", options, callback)
|
@@ -0,0 +1,58 @@
|
||||
package dev.inmo.micro_utils.common
|
||||
|
||||
import org.w3c.dom.*
|
||||
import kotlin.js.Json
|
||||
import kotlin.js.json
|
||||
|
||||
external class ResizeObserver(
|
||||
callback: (Array<ResizeObserverEntry>, ResizeObserver) -> Unit
|
||||
) {
|
||||
fun observe(target: Element, options: Json = definedExternally)
|
||||
|
||||
fun unobserve(target: Element)
|
||||
|
||||
fun disconnect()
|
||||
}
|
||||
|
||||
external interface ResizeObserverSize {
|
||||
val blockSize: Float
|
||||
val inlineSize: Float
|
||||
}
|
||||
|
||||
external interface ResizeObserverEntry {
|
||||
val borderBoxSize: Array<ResizeObserverSize>
|
||||
val contentBoxSize: Array<ResizeObserverSize>
|
||||
val devicePixelContentBoxSize: Array<ResizeObserverSize>
|
||||
val contentRect: DOMRectReadOnly
|
||||
val target: Element
|
||||
}
|
||||
|
||||
fun ResizeObserver.observe(target: Element, options: ResizeObserverObserveOptions) = observe(
|
||||
target,
|
||||
json(
|
||||
"box" to options.box ?.name
|
||||
)
|
||||
)
|
||||
|
||||
class ResizeObserverObserveOptions(
|
||||
val box: Box? = null
|
||||
) {
|
||||
sealed interface Box {
|
||||
val name: String
|
||||
|
||||
object Content : Box {
|
||||
override val name: String
|
||||
get() = "content-box"
|
||||
}
|
||||
|
||||
object Border : Box {
|
||||
override val name: String
|
||||
get() = "border-box"
|
||||
}
|
||||
|
||||
object DevicePixelContent : Box {
|
||||
override val name: String
|
||||
get() = "device-pixel-content-box"
|
||||
}
|
||||
}
|
||||
}
|
@@ -16,6 +16,7 @@ fun <T> Flow<T>.toMutableState(
|
||||
return state
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun <T> StateFlow<T>.toMutableState(
|
||||
scope: CoroutineScope
|
||||
): MutableState<T> = toMutableState(value, scope)
|
||||
|
@@ -6,11 +6,12 @@ import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlin.coroutines.cancellation.CancellationException
|
||||
|
||||
private sealed interface AccumulatorFlowStep
|
||||
private data class DataRetrievedAccumulatorFlowStep(val data: Any) : AccumulatorFlowStep
|
||||
private data class SubscribeAccumulatorFlowStep(val channel: Channel<Any>) : AccumulatorFlowStep
|
||||
private data class UnsubscribeAccumulatorFlowStep(val channel: Channel<Any>) : AccumulatorFlowStep
|
||||
private sealed interface AccumulatorFlowStep<T>
|
||||
private data class DataRetrievedAccumulatorFlowStep<T>(val data: T) : AccumulatorFlowStep<T>
|
||||
private data class SubscribeAccumulatorFlowStep<T>(val channel: Channel<T>) : AccumulatorFlowStep<T>
|
||||
private data class UnsubscribeAccumulatorFlowStep<T>(val channel: Channel<T>) : AccumulatorFlowStep<T>
|
||||
|
||||
/**
|
||||
* This [Flow] will have behaviour very similar to [SharedFlow], but there are several differences:
|
||||
@@ -26,12 +27,12 @@ class AccumulatorFlow<T>(
|
||||
private val subscope = scope.LinkedSupervisorScope()
|
||||
private val activeData = ArrayDeque<T>()
|
||||
private val dataMutex = Mutex()
|
||||
private val channelsForBroadcast = mutableListOf<Channel<Any>>()
|
||||
private val channelsForBroadcast = mutableListOf<Channel<T>>()
|
||||
private val channelsMutex = Mutex()
|
||||
private val steps = subscope.actor<AccumulatorFlowStep> { step ->
|
||||
private val steps = subscope.actor<AccumulatorFlowStep<T>> { step ->
|
||||
when (step) {
|
||||
is DataRetrievedAccumulatorFlowStep -> {
|
||||
if (activeData.first() === step.data) {
|
||||
if (activeData.firstOrNull() === step.data) {
|
||||
dataMutex.withLock {
|
||||
activeData.removeFirst()
|
||||
}
|
||||
@@ -42,7 +43,7 @@ class AccumulatorFlow<T>(
|
||||
dataMutex.withLock {
|
||||
val dataToSend = activeData.toList()
|
||||
safelyWithoutExceptions {
|
||||
dataToSend.forEach { step.channel.send(it as Any) }
|
||||
dataToSend.forEach { step.channel.send(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,24 +59,29 @@ class AccumulatorFlow<T>(
|
||||
channelsMutex.withLock {
|
||||
channelsForBroadcast.forEach { channel ->
|
||||
safelyWithResult {
|
||||
channel.send(it as Any)
|
||||
channel.send(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun collectSafely(collector: FlowCollector<T>) {
|
||||
val channel = Channel<Any>(Channel.UNLIMITED, BufferOverflow.SUSPEND)
|
||||
val channel = Channel<T>(Channel.UNLIMITED, BufferOverflow.SUSPEND)
|
||||
steps.send(SubscribeAccumulatorFlowStep(channel))
|
||||
for (data in channel) {
|
||||
try {
|
||||
collector.emit(data as T)
|
||||
steps.send(DataRetrievedAccumulatorFlowStep(data))
|
||||
} finally {
|
||||
channel.cancel()
|
||||
steps.send(UnsubscribeAccumulatorFlowStep(channel))
|
||||
val result = runCatchingSafely {
|
||||
for (data in channel) {
|
||||
val emitResult = runCatchingSafely {
|
||||
collector.emit(data)
|
||||
}
|
||||
if (emitResult.isSuccess || emitResult.exceptionOrNull() is CancellationException) {
|
||||
steps.send(DataRetrievedAccumulatorFlowStep(data))
|
||||
}
|
||||
emitResult.getOrThrow()
|
||||
}
|
||||
}
|
||||
channel.cancel()
|
||||
steps.send(UnsubscribeAccumulatorFlowStep(channel))
|
||||
result.getOrThrow()
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,15 @@
|
||||
package dev.inmo.micro_utils.crypto
|
||||
|
||||
val HEX_ARRAY = "0123456789abcdef".toCharArray()
|
||||
|
||||
fun SourceBytes.hex(): String {
|
||||
val hexChars = CharArray(size * 2)
|
||||
for (j in indices) {
|
||||
val v: Int = this[j].toInt() and 0xFF
|
||||
hexChars[j * 2] = HEX_ARRAY[v ushr 4]
|
||||
hexChars[j * 2 + 1] = HEX_ARRAY[v and 0x0F]
|
||||
}
|
||||
return hexChars.concatToString()
|
||||
}
|
||||
|
||||
fun SourceString.hex(): String = encodeToByteArray().hex()
|
@@ -0,0 +1,3 @@
|
||||
package dev.inmo.micro_utils.crypto
|
||||
|
||||
expect fun SourceString.hmacSha256(key: String): String
|
@@ -0,0 +1,15 @@
|
||||
package dev.inmo.micro_utils.crypto
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
class Hex {
|
||||
@Test
|
||||
fun testSimpleHmacSHA256Message() {
|
||||
val text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
|
||||
val resultHex = text.hex()
|
||||
assertEquals(
|
||||
"4c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e6720656c69742c2073656420646f20656975736d6f642074656d706f7220696e6369646964756e74207574206c61626f726520657420646f6c6f7265206d61676e6120616c697175612e20557420656e696d206164206d696e696d2076656e69616d2c2071756973206e6f737472756420657865726369746174696f6e20756c6c616d636f206c61626f726973206e69736920757420616c697175697020657820656120636f6d6d6f646f20636f6e7365717561742e2044756973206175746520697275726520646f6c6f7220696e20726570726568656e646572697420696e20766f6c7570746174652076656c697420657373652063696c6c756d20646f6c6f726520657520667567696174206e756c6c612070617269617475722e204578636570746575722073696e74206f6363616563617420637570696461746174206e6f6e2070726f6964656e742c2073756e7420696e2063756c706120717569206f666669636961206465736572756e74206d6f6c6c697420616e696d20696420657374206c61626f72756d2e",
|
||||
resultHex
|
||||
)
|
||||
}
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
package dev.inmo.micro_utils.crypto
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
class HmacSHA256 {
|
||||
@Test
|
||||
fun testSimpleHmacSHA256Message() {
|
||||
val text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
|
||||
val resultSha = text.hmacSha256("Example")
|
||||
assertEquals("5a481d59329ef862b158eedc95392ebb22492ba3014661a3379d8201db992484", resultSha)
|
||||
}
|
||||
}
|
@@ -7,3 +7,7 @@ external interface CryptoJs {
|
||||
@JsModule("crypto-js")
|
||||
@JsNonModule
|
||||
external val CryptoJS: CryptoJs
|
||||
|
||||
actual fun SourceString.hmacSha256(key: String): String {
|
||||
return CryptoJS.asDynamic().HmacSHA256(this, key).toString().unsafeCast<String>()
|
||||
}
|
||||
|
@@ -0,0 +1,13 @@
|
||||
package dev.inmo.micro_utils.crypto
|
||||
|
||||
import javax.crypto.Mac
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
actual fun SourceString.hmacSha256(key: String): String {
|
||||
val mac = Mac.getInstance("HmacSHA256")
|
||||
|
||||
val secretKey = SecretKeySpec(key.toByteArray(), "HmacSHA256")
|
||||
mac.init(secretKey)
|
||||
|
||||
return mac.doFinal(toByteArray()).hex()
|
||||
}
|
@@ -3,6 +3,8 @@ package dev.inmo.micro_utils.fsm.common
|
||||
import dev.inmo.micro_utils.common.Optional
|
||||
import dev.inmo.micro_utils.common.onPresented
|
||||
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.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
@@ -13,13 +15,24 @@ import kotlinx.coroutines.sync.withLock
|
||||
* handling until [start] method will be called
|
||||
*/
|
||||
interface StatesMachine<T : State> : StatesHandler<T, T> {
|
||||
suspend fun launchStateHandling(
|
||||
state: T,
|
||||
handlers: List<CheckableHandlerHolder<in T, T>>,
|
||||
onStateHandlingErrorHandler: StateHandlingErrorHandler<T>
|
||||
): T? {
|
||||
return runCatchingSafely {
|
||||
handlers.firstOrNull { it.checkHandleable(state) } ?.run {
|
||||
handleState(state)
|
||||
}
|
||||
}.getOrElse {
|
||||
onStateHandlingErrorHandler(state, it)
|
||||
}
|
||||
}
|
||||
suspend fun launchStateHandling(
|
||||
state: T,
|
||||
handlers: List<CheckableHandlerHolder<in T, T>>
|
||||
): T? {
|
||||
return handlers.firstOrNull { it.checkHandleable(state) } ?.run {
|
||||
handleState(state)
|
||||
}
|
||||
return launchStateHandling(state, handlers, defaultStateHandlingErrorHandler())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -38,8 +51,9 @@ interface StatesMachine<T : State> : StatesHandler<T, T> {
|
||||
*/
|
||||
operator fun <T: State> invoke(
|
||||
statesManager: StatesManager<T>,
|
||||
handlers: List<CheckableHandlerHolder<in T, T>>
|
||||
) = DefaultStatesMachine(statesManager, handlers)
|
||||
handlers: List<CheckableHandlerHolder<in T, T>>,
|
||||
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler()
|
||||
) = DefaultStatesMachine(statesManager, handlers, onStateHandlingErrorHandler)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,12 +66,17 @@ interface StatesMachine<T : State> : StatesHandler<T, T> {
|
||||
open class DefaultStatesMachine <T: State>(
|
||||
protected val statesManager: StatesManager<T>,
|
||||
protected val handlers: List<CheckableHandlerHolder<in T, T>>,
|
||||
protected val onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler()
|
||||
) : StatesMachine<T> {
|
||||
/**
|
||||
* Will call [launchStateHandling] for state handling
|
||||
*/
|
||||
override suspend fun StatesMachine<in T>.handleState(state: T): T? = launchStateHandling(state, handlers)
|
||||
|
||||
override suspend fun launchStateHandling(state: T, handlers: List<CheckableHandlerHolder<in T, T>>): T? {
|
||||
return launchStateHandling(state, handlers, onStateHandlingErrorHandler)
|
||||
}
|
||||
|
||||
/**
|
||||
* This
|
||||
*/
|
||||
|
@@ -1,6 +1,8 @@
|
||||
package dev.inmo.micro_utils.fsm.common
|
||||
|
||||
import dev.inmo.micro_utils.common.*
|
||||
import dev.inmo.micro_utils.fsm.common.utils.StateHandlingErrorHandler
|
||||
import dev.inmo.micro_utils.fsm.common.utils.defaultStateHandlingErrorHandler
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
|
||||
@@ -20,9 +22,11 @@ interface UpdatableStatesMachine<T : State> : StatesMachine<T> {
|
||||
open class DefaultUpdatableStatesMachine<T : State>(
|
||||
statesManager: StatesManager<T>,
|
||||
handlers: List<CheckableHandlerHolder<in T, T>>,
|
||||
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler()
|
||||
) : DefaultStatesMachine<T>(
|
||||
statesManager,
|
||||
handlers
|
||||
handlers,
|
||||
onStateHandlingErrorHandler
|
||||
), UpdatableStatesMachine<T> {
|
||||
protected val jobsStates = mutableMapOf<Job, T>()
|
||||
|
||||
@@ -34,7 +38,7 @@ open class DefaultUpdatableStatesMachine<T : State>(
|
||||
*/
|
||||
override suspend fun performStateUpdate(previousState: Optional<T>, actualState: T, scope: CoroutineScope) {
|
||||
statesJobsMutex.withLock {
|
||||
if (compare(previousState, actualState)) {
|
||||
if (shouldReplaceJob(previousState, actualState)) {
|
||||
statesJobs[actualState] ?.cancel()
|
||||
}
|
||||
val job = previousState.mapOnPresented {
|
||||
|
@@ -0,0 +1,6 @@
|
||||
package dev.inmo.micro_utils.fsm.common.utils
|
||||
|
||||
typealias StateHandlingErrorHandler<T> = suspend (T, Throwable) -> T?
|
||||
val DefaultStateHandlingErrorHandler: StateHandlingErrorHandler<*> = { _, _ -> null }
|
||||
inline fun <T> defaultStateHandlingErrorHandler(): StateHandlingErrorHandler<T> = DefaultStateHandlingErrorHandler as StateHandlingErrorHandler<T>
|
||||
|
@@ -14,5 +14,5 @@ crypto_js_version=4.1.1
|
||||
# Project data
|
||||
|
||||
group=dev.inmo
|
||||
version=0.9.17
|
||||
android_code_version=107
|
||||
version=0.11.0
|
||||
android_code_version=124
|
||||
|
@@ -1,22 +1,22 @@
|
||||
[versions]
|
||||
|
||||
kt = "1.6.10"
|
||||
kt-serialization = "1.3.2"
|
||||
kt-coroutines = "1.6.0"
|
||||
kt = "1.6.21"
|
||||
kt-serialization = "1.3.3"
|
||||
kt-coroutines = "1.6.1"
|
||||
|
||||
jb-compose = "1.1.1"
|
||||
jb-exposed = "0.37.3"
|
||||
jb-dokka = "1.6.10"
|
||||
jb-compose = "1.2.0-alpha01-dev686"
|
||||
jb-exposed = "0.38.2"
|
||||
jb-dokka = "1.6.21"
|
||||
|
||||
klock = "2.7.0"
|
||||
uuid = "0.4.0"
|
||||
uuid = "0.4.1"
|
||||
|
||||
ktor = "1.6.8"
|
||||
ktor = "2.0.2"
|
||||
|
||||
gh-release = "2.2.12"
|
||||
gh-release = "2.3.7"
|
||||
|
||||
android-gradle = "7.0.4"
|
||||
dexcount = "3.0.1"
|
||||
dexcount = "3.1.0"
|
||||
|
||||
android-coreKtx = "1.7.0"
|
||||
android-recyclerView = "1.2.1"
|
||||
@@ -37,14 +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" }
|
||||
|
@@ -1,14 +1,18 @@
|
||||
package dev.inmo.micro_utils.ktor.client
|
||||
|
||||
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.features.websocket.ws
|
||||
import io.ktor.client.plugins.pluginOrNull
|
||||
import io.ktor.client.plugins.websocket.WebSockets
|
||||
import io.ktor.client.plugins.websocket.ws
|
||||
import io.ktor.client.request.HttpRequestBuilder
|
||||
import io.ktor.http.cio.websocket.Frame
|
||||
import io.ktor.http.cio.websocket.readBytes
|
||||
import io.ktor.websocket.Frame
|
||||
import io.ktor.websocket.readBytes
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.channelFlow
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.serialization.DeserializationStrategy
|
||||
|
||||
/**
|
||||
@@ -17,43 +21,41 @@ import kotlinx.serialization.DeserializationStrategy
|
||||
*/
|
||||
inline fun <T> HttpClient.createStandardWebsocketFlow(
|
||||
url: String,
|
||||
crossinline checkReconnection: (Throwable?) -> Boolean = { true },
|
||||
crossinline checkReconnection: suspend (Throwable?) -> Boolean = { true },
|
||||
noinline requestBuilder: HttpRequestBuilder.() -> Unit = {},
|
||||
crossinline conversation: suspend (StandardKtorSerialInputData) -> T
|
||||
): Flow<T> {
|
||||
pluginOrNull(WebSockets) ?: error("Plugin $WebSockets must be installed for using createStandardWebsocketFlow")
|
||||
|
||||
val correctedUrl = url.asCorrectWebSocketUrl
|
||||
|
||||
return channelFlow {
|
||||
val producerScope = this@channelFlow
|
||||
do {
|
||||
val reconnect = try {
|
||||
safely {
|
||||
ws(correctedUrl, requestBuilder) {
|
||||
for (received in incoming) {
|
||||
when (received) {
|
||||
is Frame.Binary -> producerScope.send(conversation(received.readBytes()))
|
||||
else -> {
|
||||
producerScope.close()
|
||||
return@ws
|
||||
}
|
||||
val reconnect = runCatchingSafely {
|
||||
ws(correctedUrl, requestBuilder) {
|
||||
for (received in incoming) {
|
||||
when (received) {
|
||||
is Frame.Binary -> send(conversation(received.data))
|
||||
else -> {
|
||||
close()
|
||||
return@ws
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
checkReconnection(null)
|
||||
} catch (e: Throwable) {
|
||||
}.getOrElse { e ->
|
||||
checkReconnection(e).also {
|
||||
if (!it) {
|
||||
producerScope.close(e)
|
||||
close(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (reconnect)
|
||||
if (!producerScope.isClosedForSend) {
|
||||
safely(
|
||||
{ it.printStackTrace() }
|
||||
) {
|
||||
producerScope.close()
|
||||
} while (reconnect && isActive)
|
||||
|
||||
if (isActive) {
|
||||
safely {
|
||||
close()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -65,8 +67,8 @@ inline fun <T> HttpClient.createStandardWebsocketFlow(
|
||||
*/
|
||||
inline fun <T> HttpClient.createStandardWebsocketFlow(
|
||||
url: String,
|
||||
crossinline checkReconnection: (Throwable?) -> Boolean = { true },
|
||||
deserializer: DeserializationStrategy<T>,
|
||||
crossinline checkReconnection: suspend (Throwable?) -> Boolean = { true },
|
||||
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat,
|
||||
noinline requestBuilder: HttpRequestBuilder.() -> Unit = {},
|
||||
) = createStandardWebsocketFlow(
|
||||
|
@@ -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())
|
||||
}
|
@@ -0,0 +1,55 @@
|
||||
package dev.inmo.micro_utils.ktor.client
|
||||
|
||||
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.websocket.Frame
|
||||
import io.ktor.websocket.readBytes
|
||||
import io.ktor.websocket.serialization.sendSerializedBase
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.channelFlow
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.serialization.DeserializationStrategy
|
||||
|
||||
/**
|
||||
* @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> HttpClient.createStandardWebsocketFlow(
|
||||
url: String,
|
||||
noinline checkReconnection: suspend (Throwable?) -> Boolean = { true },
|
||||
noinline requestBuilder: HttpRequestBuilder.() -> Unit = {}
|
||||
): Flow<T> {
|
||||
pluginOrNull(WebSockets) ?: error("Plugin $WebSockets must be installed for using createStandardWebsocketFlow")
|
||||
|
||||
val correctedUrl = url.asCorrectWebSocketUrl
|
||||
|
||||
return channelFlow {
|
||||
do {
|
||||
val reconnect = runCatchingSafely {
|
||||
ws(correctedUrl, requestBuilder) {
|
||||
for (received in incoming) {
|
||||
sendSerialized(received.data)
|
||||
}
|
||||
}
|
||||
checkReconnection(null)
|
||||
}.getOrElse { e ->
|
||||
checkReconnection(e).also {
|
||||
if (!it) {
|
||||
close(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (reconnect && isActive)
|
||||
|
||||
if (isActive) {
|
||||
safely {
|
||||
close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -4,8 +4,10 @@ import dev.inmo.micro_utils.common.MPPFile
|
||||
import dev.inmo.micro_utils.common.filename
|
||||
import dev.inmo.micro_utils.ktor.common.*
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.call.body
|
||||
import io.ktor.client.request.*
|
||||
import io.ktor.client.request.forms.*
|
||||
import io.ktor.client.statement.readBytes
|
||||
import io.ktor.http.*
|
||||
import io.ktor.utils.io.core.ByteReadPacket
|
||||
import kotlinx.serialization.*
|
||||
@@ -85,16 +87,16 @@ class UnifiedRequester(
|
||||
|
||||
fun <T> createStandardWebsocketFlow(
|
||||
url: String,
|
||||
checkReconnection: (Throwable?) -> Boolean,
|
||||
checkReconnection: suspend (Throwable?) -> Boolean,
|
||||
deserializer: DeserializationStrategy<T>,
|
||||
requestBuilder: HttpRequestBuilder.() -> Unit = {},
|
||||
) = client.createStandardWebsocketFlow(url, checkReconnection, deserializer, serialFormat, requestBuilder)
|
||||
) = client.createStandardWebsocketFlow(url, deserializer, checkReconnection, serialFormat, requestBuilder)
|
||||
|
||||
fun <T> createStandardWebsocketFlow(
|
||||
url: String,
|
||||
deserializer: DeserializationStrategy<T>,
|
||||
requestBuilder: HttpRequestBuilder.() -> Unit = {},
|
||||
) = createStandardWebsocketFlow(url, { true }, deserializer, requestBuilder)
|
||||
) = createStandardWebsocketFlow(url, { true }, deserializer, requestBuilder)
|
||||
}
|
||||
|
||||
val defaultRequester = UnifiedRequester()
|
||||
@@ -103,10 +105,8 @@ suspend fun <ResultType> HttpClient.uniget(
|
||||
url: String,
|
||||
resultDeserializer: DeserializationStrategy<ResultType>,
|
||||
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
|
||||
) = get<StandardKtorSerialInputData>(
|
||||
url
|
||||
).let {
|
||||
serialFormat.decodeDefault(resultDeserializer, it)
|
||||
) = get(url).let {
|
||||
serialFormat.decodeDefault(resultDeserializer, it.body<StandardKtorSerialInputData>())
|
||||
}
|
||||
|
||||
|
||||
@@ -123,10 +123,12 @@ suspend fun <BodyType, ResultType> HttpClient.unipost(
|
||||
bodyInfo: BodyPair<BodyType>,
|
||||
resultDeserializer: DeserializationStrategy<ResultType>,
|
||||
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
|
||||
) = post<StandardKtorSerialInputData>(url) {
|
||||
body = serialFormat.encodeDefault(bodyInfo.first, bodyInfo.second)
|
||||
) = post(url) {
|
||||
setBody(
|
||||
serialFormat.encodeDefault(bodyInfo.first, bodyInfo.second)
|
||||
)
|
||||
}.let {
|
||||
serialFormat.decodeDefault(resultDeserializer, it)
|
||||
serialFormat.decodeDefault(resultDeserializer, it.body<StandardKtorSerialInputData>())
|
||||
}
|
||||
|
||||
suspend fun <ResultType> HttpClient.unimultipart(
|
||||
@@ -139,7 +141,7 @@ suspend fun <ResultType> HttpClient.unimultipart(
|
||||
dataHeadersBuilder: HeadersBuilder.() -> Unit = {},
|
||||
requestBuilder: HttpRequestBuilder.() -> Unit = {},
|
||||
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
|
||||
): ResultType = submitFormWithBinaryData<StandardKtorSerialInputData>(
|
||||
): ResultType = submitFormWithBinaryData(
|
||||
url,
|
||||
formData = formData {
|
||||
append(
|
||||
@@ -155,7 +157,7 @@ suspend fun <ResultType> HttpClient.unimultipart(
|
||||
}
|
||||
) {
|
||||
requestBuilder()
|
||||
}.let { serialFormat.decodeDefault(resultDeserializer, it) }
|
||||
}.let { serialFormat.decodeDefault(resultDeserializer, it.body<StandardKtorSerialInputData>()) }
|
||||
|
||||
suspend fun <BodyType, ResultType> HttpClient.unimultipart(
|
||||
url: String,
|
||||
|
@@ -4,9 +4,10 @@ import dev.inmo.micro_utils.common.MPPFile
|
||||
import dev.inmo.micro_utils.common.filename
|
||||
import dev.inmo.micro_utils.ktor.common.TemporalFileId
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.features.onUpload
|
||||
import io.ktor.client.plugins.onUpload
|
||||
import io.ktor.client.request.forms.formData
|
||||
import io.ktor.client.request.forms.submitFormWithBinaryData
|
||||
import io.ktor.client.statement.bodyAsText
|
||||
import io.ktor.http.Headers
|
||||
import io.ktor.http.HttpHeaders
|
||||
import java.net.URLConnection
|
||||
@@ -20,7 +21,7 @@ actual suspend fun HttpClient.tempUpload(
|
||||
onUpload: (Long, Long) -> Unit
|
||||
): TemporalFileId {
|
||||
val inputProvider = file.inputProvider()
|
||||
val fileId = submitFormWithBinaryData<String>(
|
||||
val fileId = submitFormWithBinaryData(
|
||||
fullTempUploadDraftPath,
|
||||
formData = formData {
|
||||
append(
|
||||
@@ -34,6 +35,6 @@ actual suspend fun HttpClient.tempUpload(
|
||||
}
|
||||
) {
|
||||
onUpload(onUpload)
|
||||
}
|
||||
}.bodyAsText()
|
||||
return TemporalFileId(fileId)
|
||||
}
|
||||
|
@@ -14,6 +14,7 @@ kotlin {
|
||||
api libs.kt.serialization.cbor
|
||||
api libs.klock
|
||||
api libs.uuid
|
||||
api libs.ktor.io
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,6 @@
|
||||
package dev.inmo.micro_utils.ktor.common
|
||||
|
||||
import dev.inmo.micro_utils.common.MPPFile
|
||||
import io.ktor.utils.io.core.Input
|
||||
|
||||
expect fun MPPFile.input(): Input
|
@@ -1,3 +1,5 @@
|
||||
@file:Suppress("NOTHING_TO_INLINE")
|
||||
|
||||
package dev.inmo.micro_utils.ktor.common
|
||||
|
||||
import kotlinx.serialization.*
|
||||
|
@@ -0,0 +1,7 @@
|
||||
package dev.inmo.micro_utils.ktor.common
|
||||
|
||||
import dev.inmo.micro_utils.common.*
|
||||
import io.ktor.utils.io.core.ByteReadPacket
|
||||
import io.ktor.utils.io.core.Input
|
||||
|
||||
actual fun MPPFile.input(): Input = ByteReadPacket(readBytes())
|
@@ -0,0 +1,7 @@
|
||||
package dev.inmo.micro_utils.ktor.common
|
||||
|
||||
import dev.inmo.micro_utils.common.MPPFile
|
||||
import io.ktor.utils.io.core.Input
|
||||
import io.ktor.utils.io.streams.asInput
|
||||
|
||||
actual fun MPPFile.input(): Input = inputStream().asInput()
|
@@ -19,7 +19,8 @@ kotlin {
|
||||
api libs.ktor.server
|
||||
api libs.ktor.server.cio
|
||||
api libs.ktor.server.host.common
|
||||
api libs.ktor.websockets
|
||||
api libs.ktor.server.websockets
|
||||
api libs.ktor.server.statusPages
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
@@ -2,26 +2,26 @@ package dev.inmo.micro_utils.ktor.server
|
||||
|
||||
import dev.inmo.micro_utils.coroutines.safely
|
||||
import dev.inmo.micro_utils.ktor.common.*
|
||||
import io.ktor.application.featureOrNull
|
||||
import io.ktor.application.install
|
||||
import io.ktor.http.URLProtocol
|
||||
import io.ktor.http.cio.websocket.*
|
||||
import io.ktor.routing.Route
|
||||
import io.ktor.routing.application
|
||||
import io.ktor.websocket.*
|
||||
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
|
||||
|
||||
fun <T> Route.includeWebsocketHandling(
|
||||
suburl: String,
|
||||
flow: Flow<T>,
|
||||
protocol: URLProtocol = URLProtocol.WS,
|
||||
protocol: URLProtocol? = null,
|
||||
converter: suspend WebSocketServerSession.(T) -> StandardKtorSerialInputData?
|
||||
) {
|
||||
application.apply {
|
||||
featureOrNull(io.ktor.websocket.WebSockets) ?: install(io.ktor.websocket.WebSockets)
|
||||
pluginOrNull(WebSockets) ?: install(WebSockets)
|
||||
}
|
||||
webSocket(suburl, protocol.name) {
|
||||
webSocket(suburl, protocol ?.name) {
|
||||
safely {
|
||||
flow.collect {
|
||||
converter(it) ?.let { data ->
|
||||
@@ -37,7 +37,7 @@ fun <T> Route.includeWebsocketHandling(
|
||||
flow: Flow<T>,
|
||||
serializer: SerializationStrategy<T>,
|
||||
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat,
|
||||
protocol: URLProtocol = URLProtocol.WS,
|
||||
protocol: URLProtocol? = null,
|
||||
filter: (suspend WebSocketServerSession.(T) -> Boolean)? = null
|
||||
) = includeWebsocketHandling(
|
||||
suburl,
|
||||
|
@@ -0,0 +1,30 @@
|
||||
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
|
||||
) {
|
||||
application.apply {
|
||||
pluginOrNull(WebSockets) ?: install(WebSockets)
|
||||
}
|
||||
webSocket(suburl, protocol ?.name) {
|
||||
safely {
|
||||
flow.collect {
|
||||
sendSerialized(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -3,25 +3,21 @@ package dev.inmo.micro_utils.ktor.server
|
||||
import dev.inmo.micro_utils.common.*
|
||||
import dev.inmo.micro_utils.coroutines.safely
|
||||
import dev.inmo.micro_utils.ktor.common.*
|
||||
import io.ktor.application.ApplicationCall
|
||||
import io.ktor.application.call
|
||||
import io.ktor.http.*
|
||||
import io.ktor.http.content.PartData
|
||||
import io.ktor.http.content.forEachPart
|
||||
import io.ktor.request.receive
|
||||
import io.ktor.request.receiveMultipart
|
||||
import io.ktor.response.respond
|
||||
import io.ktor.response.respondBytes
|
||||
import io.ktor.routing.Route
|
||||
import io.ktor.util.asStream
|
||||
import io.ktor.util.cio.writeChannel
|
||||
import io.ktor.http.content.*
|
||||
import io.ktor.server.application.ApplicationCall
|
||||
import io.ktor.server.application.call
|
||||
import io.ktor.server.request.receive
|
||||
import io.ktor.server.request.receiveMultipart
|
||||
import io.ktor.server.response.respond
|
||||
import io.ktor.server.response.respondBytes
|
||||
import io.ktor.server.routing.Route
|
||||
import io.ktor.server.websocket.WebSocketServerSession
|
||||
import io.ktor.util.pipeline.PipelineContext
|
||||
import io.ktor.utils.io.core.*
|
||||
import io.ktor.websocket.WebSocketServerSession
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.serialization.*
|
||||
import java.io.File
|
||||
import java.io.File.createTempFile
|
||||
import kotlinx.serialization.DeserializationStrategy
|
||||
import kotlinx.serialization.SerializationStrategy
|
||||
|
||||
class UnifiedRouter(
|
||||
val serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat,
|
||||
@@ -31,7 +27,7 @@ class UnifiedRouter(
|
||||
suburl: String,
|
||||
flow: Flow<T>,
|
||||
serializer: SerializationStrategy<T>,
|
||||
protocol: URLProtocol = URLProtocol.WS,
|
||||
protocol: URLProtocol? = null,
|
||||
filter: (suspend WebSocketServerSession.(T) -> Boolean)? = null
|
||||
) = includeWebsocketHandling(suburl, flow, serializer, serialFormat, protocol, filter)
|
||||
|
||||
@@ -197,7 +193,9 @@ suspend fun <T> ApplicationCall.uniloadMultipartFile(
|
||||
".${name.extension}"
|
||||
).apply {
|
||||
outputStream().use { fileStream ->
|
||||
it.provider().asStream().copyTo(fileStream)
|
||||
it.streamProvider().use {
|
||||
it.copyTo(fileStream)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -239,7 +237,9 @@ suspend fun ApplicationCall.uniloadMultipartFile(
|
||||
".${name.extension}"
|
||||
).apply {
|
||||
outputStream().use { fileStream ->
|
||||
it.provider().asStream().copyTo(fileStream)
|
||||
it.streamProvider().use {
|
||||
it.copyTo(fileStream)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@@ -1,8 +1,9 @@
|
||||
package dev.inmo.micro_utils.ktor.server
|
||||
|
||||
import dev.inmo.micro_utils.ktor.server.configurators.KtorApplicationConfigurator
|
||||
import io.ktor.application.Application
|
||||
import io.ktor.server.application.Application
|
||||
import io.ktor.server.cio.CIO
|
||||
import io.ktor.server.cio.CIOApplicationEngine
|
||||
import io.ktor.server.engine.*
|
||||
import kotlin.random.Random
|
||||
|
||||
@@ -10,17 +11,21 @@ fun <TEngine : ApplicationEngine, TConfiguration : ApplicationEngine.Configurati
|
||||
engine: ApplicationEngineFactory<TEngine, TConfiguration>,
|
||||
host: String = "localhost",
|
||||
port: Int = Random.nextInt(1024, 65535),
|
||||
additionalEngineEnvironmentConfigurator: ApplicationEngineEnvironmentBuilder.() -> Unit = {},
|
||||
additionalConfigurationConfigurator: TConfiguration.() -> Unit = {},
|
||||
block: Application.() -> Unit
|
||||
): TEngine {
|
||||
val env = applicationEngineEnvironment {
|
||||
): TEngine = embeddedServer(
|
||||
engine,
|
||||
applicationEngineEnvironment {
|
||||
module(block)
|
||||
connector {
|
||||
this@connector.host = host
|
||||
this@connector.port = port
|
||||
this.host = host
|
||||
this.port = port
|
||||
}
|
||||
}
|
||||
return embeddedServer(engine, env)
|
||||
}
|
||||
additionalEngineEnvironmentConfigurator()
|
||||
},
|
||||
additionalConfigurationConfigurator
|
||||
)
|
||||
|
||||
/**
|
||||
* Create server with [CIO] server engine without starting of it
|
||||
@@ -30,18 +35,31 @@ fun <TEngine : ApplicationEngine, TConfiguration : ApplicationEngine.Configurati
|
||||
fun createKtorServer(
|
||||
host: String = "localhost",
|
||||
port: Int = Random.nextInt(1024, 65535),
|
||||
additionalEngineEnvironmentConfigurator: ApplicationEngineEnvironmentBuilder.() -> Unit = {},
|
||||
additionalConfigurationConfigurator: CIOApplicationEngine.Configuration.() -> Unit = {},
|
||||
block: Application.() -> Unit
|
||||
): ApplicationEngine = createKtorServer(CIO, host, port, block)
|
||||
): CIOApplicationEngine = createKtorServer(
|
||||
CIO,
|
||||
host,
|
||||
port,
|
||||
additionalEngineEnvironmentConfigurator,
|
||||
additionalConfigurationConfigurator,
|
||||
block
|
||||
)
|
||||
|
||||
fun <TEngine : ApplicationEngine, TConfiguration : ApplicationEngine.Configuration> createKtorServer(
|
||||
engine: ApplicationEngineFactory<TEngine, TConfiguration>,
|
||||
host: String = "localhost",
|
||||
port: Int = Random.nextInt(1024, 65535),
|
||||
additionalEngineEnvironmentConfigurator: ApplicationEngineEnvironmentBuilder.() -> Unit = {},
|
||||
additionalConfigurationConfigurator: TConfiguration.() -> Unit = {},
|
||||
configurators: List<KtorApplicationConfigurator>
|
||||
): TEngine = createKtorServer(
|
||||
engine,
|
||||
host,
|
||||
port
|
||||
port,
|
||||
additionalEngineEnvironmentConfigurator,
|
||||
additionalConfigurationConfigurator
|
||||
) {
|
||||
configurators.forEach { it.apply { configure() } }
|
||||
}
|
||||
@@ -54,5 +72,7 @@ fun <TEngine : ApplicationEngine, TConfiguration : ApplicationEngine.Configurati
|
||||
fun createKtorServer(
|
||||
host: String = "localhost",
|
||||
port: Int = Random.nextInt(1024, 65535),
|
||||
configurators: List<KtorApplicationConfigurator>
|
||||
): ApplicationEngine = createKtorServer(CIO, host, port, configurators)
|
||||
configurators: List<KtorApplicationConfigurator>,
|
||||
additionalEngineEnvironmentConfigurator: ApplicationEngineEnvironmentBuilder.() -> Unit = {},
|
||||
additionalConfigurationConfigurator: CIOApplicationEngine.Configuration.() -> Unit = {},
|
||||
): ApplicationEngine = createKtorServer(CIO, host, port, additionalEngineEnvironmentConfigurator, additionalConfigurationConfigurator, configurators)
|
||||
|
@@ -7,14 +7,14 @@ import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions
|
||||
import dev.inmo.micro_utils.ktor.common.DefaultTemporalFilesSubPath
|
||||
import dev.inmo.micro_utils.ktor.common.TemporalFileId
|
||||
import dev.inmo.micro_utils.ktor.server.configurators.ApplicationRoutingConfigurator
|
||||
import io.ktor.application.call
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.http.content.PartData
|
||||
import io.ktor.http.content.streamProvider
|
||||
import io.ktor.request.receiveMultipart
|
||||
import io.ktor.response.respond
|
||||
import io.ktor.routing.Route
|
||||
import io.ktor.routing.post
|
||||
import io.ktor.server.application.call
|
||||
import io.ktor.server.request.receiveMultipart
|
||||
import io.ktor.server.response.respond
|
||||
import io.ktor.server.routing.Route
|
||||
import io.ktor.server.routing.post
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
|
@@ -1,14 +1,15 @@
|
||||
package dev.inmo.micro_utils.ktor.server.configurators
|
||||
|
||||
import io.ktor.application.Application
|
||||
import io.ktor.application.install
|
||||
import io.ktor.features.CachingHeaders
|
||||
import io.ktor.server.application.Application
|
||||
import io.ktor.server.application.install
|
||||
import io.ktor.server.plugins.cachingheaders.CachingHeaders
|
||||
import io.ktor.server.plugins.cachingheaders.CachingHeadersConfig
|
||||
import kotlinx.serialization.Contextual
|
||||
|
||||
data class ApplicationCachingHeadersConfigurator(
|
||||
private val elements: List<@Contextual Element>
|
||||
) : KtorApplicationConfigurator {
|
||||
fun interface Element { operator fun CachingHeaders.Configuration.invoke() }
|
||||
fun interface Element { operator fun CachingHeadersConfig.invoke() }
|
||||
|
||||
override fun Application.configure() {
|
||||
install(CachingHeaders) {
|
||||
|
@@ -1,8 +1,9 @@
|
||||
package dev.inmo.micro_utils.ktor.server.configurators
|
||||
|
||||
import io.ktor.application.*
|
||||
import io.ktor.routing.Route
|
||||
import io.ktor.routing.Routing
|
||||
import dev.inmo.micro_utils.ktor.server.configurators.ApplicationRoutingConfigurator.Element
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.routing.Route
|
||||
import io.ktor.server.routing.Routing
|
||||
import kotlinx.serialization.Contextual
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@@ -18,7 +19,7 @@ class ApplicationRoutingConfigurator(
|
||||
}
|
||||
|
||||
override fun Application.configure() {
|
||||
featureOrNull(Routing) ?.apply {
|
||||
pluginOrNull(Routing) ?.apply {
|
||||
rootInstaller.apply { invoke() }
|
||||
} ?: install(Routing) {
|
||||
rootInstaller.apply { invoke() }
|
||||
|
@@ -1,14 +1,15 @@
|
||||
package dev.inmo.micro_utils.ktor.server.configurators
|
||||
|
||||
import io.ktor.application.Application
|
||||
import io.ktor.application.install
|
||||
import io.ktor.sessions.Sessions
|
||||
import io.ktor.server.application.Application
|
||||
import io.ktor.server.application.install
|
||||
import io.ktor.server.sessions.Sessions
|
||||
import io.ktor.server.sessions.SessionsConfig
|
||||
import kotlinx.serialization.Contextual
|
||||
|
||||
class ApplicationSessionsConfigurator(
|
||||
private val elements: List<@Contextual Element>
|
||||
) : KtorApplicationConfigurator {
|
||||
fun interface Element { operator fun Sessions.Configuration.invoke() }
|
||||
fun interface Element { operator fun SessionsConfig.invoke() }
|
||||
|
||||
override fun Application.configure() {
|
||||
install(Sessions) {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
package dev.inmo.micro_utils.ktor.server.configurators
|
||||
|
||||
import io.ktor.application.Application
|
||||
import io.ktor.server.application.Application
|
||||
|
||||
interface KtorApplicationConfigurator {
|
||||
fun Application.configure()
|
||||
|
@@ -1,14 +1,15 @@
|
||||
package dev.inmo.micro_utils.ktor.server.configurators
|
||||
|
||||
import io.ktor.application.Application
|
||||
import io.ktor.application.install
|
||||
import io.ktor.features.StatusPages
|
||||
import io.ktor.server.application.Application
|
||||
import io.ktor.server.application.install
|
||||
import io.ktor.server.plugins.statuspages.StatusPages
|
||||
import io.ktor.server.plugins.statuspages.StatusPagesConfig
|
||||
import kotlinx.serialization.Contextual
|
||||
|
||||
class StatusPagesConfigurator(
|
||||
private val elements: List<@Contextual Element>
|
||||
) : KtorApplicationConfigurator {
|
||||
fun interface Element { operator fun StatusPages.Configuration.invoke() }
|
||||
fun interface Element { operator fun StatusPagesConfig.invoke() }
|
||||
|
||||
override fun Application.configure() {
|
||||
install(StatusPages) {
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.client.statement.bodyAsText
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.builtins.ListSerializer
|
||||
@@ -164,7 +165,7 @@ suspend fun main(vararg args: String) {
|
||||
|
||||
val ietfLanguageCodes = json.decodeFromString(
|
||||
ListSerializer(LanguageCode.serializer()),
|
||||
client.get(ietfLanguageCodesLink)
|
||||
client.get(ietfLanguageCodesLink).bodyAsText()
|
||||
).map {
|
||||
it.copy(
|
||||
title = it.title
|
||||
@@ -175,7 +176,7 @@ suspend fun main(vararg args: String) {
|
||||
}
|
||||
val ietfLanguageCodesWithTagsMap = json.decodeFromString(
|
||||
ListSerializer(LanguageCodeWithTag.serializer()),
|
||||
client.get(ietfLanguageCodesAdditionalTagsLink)
|
||||
client.get(ietfLanguageCodesAdditionalTagsLink).bodyAsText()
|
||||
).filter { it.withSubtag != it.tag }.groupBy { it.tag }
|
||||
|
||||
val tags = ietfLanguageCodes.map {
|
||||
|
@@ -1,7 +1,6 @@
|
||||
package dev.inmo.micro_utils.mime_types
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.Serializer
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.descriptors.*
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
@@ -16,6 +15,7 @@ fun mimeType(raw: String) = mimesCache.getOrPut(raw) {
|
||||
|
||||
internal fun parseMimeType(raw: String): MimeType = CustomMimeType(raw)
|
||||
|
||||
@Suppress("OPT_IN_USAGE")
|
||||
@Serializer(MimeType::class)
|
||||
object MimeTypeSerializer : KSerializer<MimeType> {
|
||||
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("mimeType", PrimitiveKind.STRING)
|
||||
|
@@ -27,4 +27,5 @@ inline fun <T> PaginationResult<T>.thisPageIfNotEmpty(): PaginationResult<T>? =
|
||||
null
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun <T> PaginationResult<T>.currentPageIfNotEmpty() = thisPageIfNotEmpty()
|
||||
|
@@ -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)
|
||||
|
@@ -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) }
|
||||
|
@@ -6,7 +6,7 @@ import dev.inmo.micro_utils.pagination.*
|
||||
* Example:
|
||||
*
|
||||
* * `|__f__l_______________________|` will be transformed to `|_______________________f__l__|`
|
||||
* * `|__f__l_|` will be transformed to `|__f__l_|`
|
||||
* * `|__f__l_|` will be transformed to `|_f__l__|`
|
||||
*
|
||||
* @return Reversed version of this [Pagination]
|
||||
*/
|
||||
|
@@ -1,7 +1,7 @@
|
||||
package dev.inmo.micro_utils.pagination
|
||||
|
||||
import io.ktor.application.ApplicationCall
|
||||
import io.ktor.http.Parameters
|
||||
import io.ktor.server.application.ApplicationCall
|
||||
|
||||
val Parameters.extractPagination: Pagination
|
||||
get() = SimplePagination(
|
||||
|
@@ -4,8 +4,6 @@ import dev.inmo.micro_utils.repos.*
|
||||
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>,
|
||||
|
@@ -8,8 +8,6 @@ import dev.inmo.micro_utils.repos.*
|
||||
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>,
|
||||
|
@@ -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())
|
@@ -1,5 +1,6 @@
|
||||
package dev.inmo.micro_utils.repos
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
interface MapperRepo<FromKey, FromValue, ToKey, ToValue> {
|
||||
suspend fun FromKey.toOutKey() = this as ToKey
|
||||
suspend fun FromValue.toOutValue() = this as ToValue
|
||||
|
@@ -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
|
||||
|
@@ -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 {
|
||||
@@ -49,4 +49,11 @@ interface StandardKeyValueRepo<Key, Value> : ReadStandardKeyValueRepo<Key, Value
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
|
@@ -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(
|
||||
inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> ReadKeyValueRepo<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(
|
||||
): 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,39 +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(
|
||||
inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> WriteKeyValueRepo<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(
|
||||
): WriteKeyValueRepo<FromKey, FromValue> = withMapper(
|
||||
mapper(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom)
|
||||
)
|
||||
|
||||
open class MapperStandardKeyValueRepo<FromKey, FromValue, ToKey, ToValue>(
|
||||
private val to: StandardKeyValueRepo<ToKey, ToValue>,
|
||||
mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue>
|
||||
) : StandardKeyValueRepo<FromKey, FromValue>,
|
||||
@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 MapperKeyValueRepo<FromKey, FromValue, ToKey, ToValue>(
|
||||
private val to: KeyValueRepo<ToKey, ToValue>,
|
||||
private val mapper: MapperRepo<FromKey, FromValue, ToKey, ToValue>
|
||||
) : 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(
|
||||
inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> KeyValueRepo<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(
|
||||
): KeyValueRepo<FromKey, FromValue> = withMapper(
|
||||
mapper(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom)
|
||||
)
|
||||
|
@@ -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(
|
||||
inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> ReadKeyValuesRepo<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(
|
||||
): 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,39 +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(
|
||||
inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> WriteKeyValuesRepo<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(
|
||||
): WriteKeyValuesRepo<FromKey, FromValue> = withMapper(
|
||||
mapper(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom)
|
||||
)
|
||||
|
||||
open class MapperOneToManyKeyValueRepo<FromKey, FromValue, ToKey, ToValue>(
|
||||
private val to: OneToManyKeyValueRepo<ToKey, ToValue>,
|
||||
@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 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(
|
||||
inline fun <reified FromKey, reified FromValue, reified ToKey, reified ToValue> KeyValuesRepo<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(
|
||||
): KeyValuesRepo<FromKey, FromValue> = withMapper(
|
||||
mapper(keyFromToTo, valueFromToTo, keyToToFrom, valueToToFrom)
|
||||
)
|
||||
|
@@ -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 {
|
||||
|
@@ -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 {
|
||||
|
@@ -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 {
|
||||
|
@@ -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)
|
||||
|
@@ -13,12 +13,12 @@ interface VersionsRepo<T> : Repo {
|
||||
* By default, instance of this interface will check that version of table with name [tableName] is less than
|
||||
* [version] or is absent
|
||||
*
|
||||
* * In case if [tableName] didn't found, will be called [onCreate] and version of table will be set up to [version]
|
||||
* * In case if [tableName] have version less than parameter [version], it will increase version one-by-one
|
||||
* until database version will be equal to [version]
|
||||
* In case if [tableName] didn't found, will be called [onCreate]. Then in case if [tableName] have version less
|
||||
* than parameter [version] or null, it will increase version one-by-one until database version will be equal to
|
||||
* [version]
|
||||
*
|
||||
* @param version Current version of table
|
||||
* @param onCreate This callback will be called in case when table have no information about table
|
||||
* @param onCreate This callback will be called in case when repo have no information about table
|
||||
* @param onUpdate This callback will be called after **iterative** changing of version. It is expected that parameter
|
||||
* "to" will always be greater than "from"
|
||||
*/
|
||||
|
@@ -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()
|
||||
}
|
||||
@@ -79,14 +82,17 @@ 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>()
|
||||
@@ -174,10 +180,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")
|
||||
class FileStandardKeyValueRepo(
|
||||
@Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE")
|
||||
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) {
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
package dev.inmo.micro_utils.repos
|
||||
|
||||
import android.database.Cursor
|
||||
|
||||
class CursorIterator(
|
||||
private val c: Cursor
|
||||
) : Iterator<Cursor> {
|
||||
private var i = 0
|
||||
|
||||
init {
|
||||
c.moveToFirst()
|
||||
}
|
||||
override fun hasNext(): Boolean {
|
||||
return i < c.count
|
||||
}
|
||||
|
||||
override fun next(): Cursor {
|
||||
i++
|
||||
return if (c.moveToNext()) {
|
||||
c
|
||||
} else {
|
||||
throw NoSuchElementException()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
operator fun Cursor.iterator(): CursorIterator = CursorIterator(this)
|
@@ -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
|
||||
@@ -64,4 +64,4 @@ abstract class AbstractAndroidCRUDRepo<ObjectType, IdType>(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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)
|
||||
|
@@ -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,8 @@ 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)
|
||||
} as KeyValueStore<T>
|
||||
@@ -25,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) {
|
||||
@@ -62,6 +63,7 @@ class KeyValueStore<T : Any> internal constructor (
|
||||
}
|
||||
|
||||
override suspend fun get(k: String): T? {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return (cachedData ?. get(k) ?: sharedPreferences.all[k]) as? T
|
||||
}
|
||||
|
||||
@@ -73,7 +75,10 @@ class KeyValueStore<T : Any> internal constructor (
|
||||
PaginationResult(
|
||||
it.page,
|
||||
it.pagesNumber,
|
||||
it.results.map { it as T }.let { if (reversed) it.reversed() else it },
|
||||
it.results.map {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
it as T
|
||||
}.let { if (reversed) it.reversed() else it },
|
||||
it.size
|
||||
)
|
||||
}
|
||||
@@ -155,3 +160,9 @@ class KeyValueStore<T : Any> internal constructor (
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <T : Any> SharedPreferencesKeyValueRepo(
|
||||
context: Context,
|
||||
name: String = "default",
|
||||
cacheValues: Boolean = false
|
||||
) = context.keyValueStore<T>(name, cacheValues)
|
||||
|
@@ -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()
|
||||
@@ -143,7 +142,12 @@ class OneToManyAndroidRepo<Key, Value>(
|
||||
}.toLong()
|
||||
|
||||
override suspend fun count(k: Key): Long = helper.blockingReadableTransaction {
|
||||
selectDistinct(tableName, columns = valueColumnArray, selection = "$idColumnName=?", selectionArgs = arrayOf(k.keyAsString()), limit = FirstPagePagination(1).limitClause()).use {
|
||||
selectDistinct(
|
||||
tableName,
|
||||
columns = valueColumnArray,
|
||||
selection = "$idColumnName=?",
|
||||
selectionArgs = arrayOf(k.keyAsString())
|
||||
).use {
|
||||
it.count
|
||||
}
|
||||
}.toLong()
|
||||
|
@@ -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]
|
||||
*/
|
||||
|
@@ -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>
|
||||
|
@@ -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)
|
||||
{
|
||||
|
@@ -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,15 +15,21 @@ abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
|
||||
) :
|
||||
AbstractExposedReadCRUDRepo<ObjectType, IdType>(tableName),
|
||||
ExposedCRUDRepo<ObjectType, IdType>,
|
||||
WriteStandardCRUDRepo<ObjectType, IdType, InputValueType>
|
||||
WriteCRUDRepo<ObjectType, IdType, InputValueType>
|
||||
{
|
||||
protected val newObjectsChannel = MutableSharedFlow<ObjectType>(replyCacheInFlows, flowsChannelsSize)
|
||||
protected val updateObjectsChannel = MutableSharedFlow<ObjectType>(replyCacheInFlows, flowsChannelsSize)
|
||||
protected val deleteObjectsIdsChannel = MutableSharedFlow<IdType>(replyCacheInFlows, flowsChannelsSize)
|
||||
protected val _newObjectsFlow = MutableSharedFlow<ObjectType>(replyCacheInFlows, flowsChannelsSize)
|
||||
protected val _updatedObjectsFlow = MutableSharedFlow<ObjectType>(replyCacheInFlows, flowsChannelsSize)
|
||||
protected val _deletedObjectsIdsFlow = MutableSharedFlow<IdType>(replyCacheInFlows, flowsChannelsSize)
|
||||
@Deprecated("Renamed", ReplaceWith("_newObjectsFlow"))
|
||||
protected val newObjectsChannel = _newObjectsFlow
|
||||
@Deprecated("Renamed", ReplaceWith("_updatedObjectsFlow"))
|
||||
protected val updateObjectsChannel = _updatedObjectsFlow
|
||||
@Deprecated("Renamed", ReplaceWith("_deletedObjectsIdsFlow"))
|
||||
protected val deleteObjectsIdsChannel = _deletedObjectsIdsFlow
|
||||
|
||||
override val newObjectsFlow: Flow<ObjectType> = newObjectsChannel.asSharedFlow()
|
||||
override val updatedObjectsFlow: Flow<ObjectType> = updateObjectsChannel.asSharedFlow()
|
||||
override val deletedObjectsIdsFlow: Flow<IdType> = deleteObjectsIdsChannel.asSharedFlow()
|
||||
override val newObjectsFlow: Flow<ObjectType> = _newObjectsFlow.asSharedFlow()
|
||||
override val updatedObjectsFlow: Flow<ObjectType> = _updatedObjectsFlow.asSharedFlow()
|
||||
override val deletedObjectsIdsFlow: Flow<IdType> = _deletedObjectsIdsFlow.asSharedFlow()
|
||||
|
||||
protected abstract fun InsertStatement<Number>.asObject(value: InputValueType): ObjectType
|
||||
abstract val selectByIds: SqlExpressionBuilder.(List<IdType>) -> Op<Boolean>
|
||||
@@ -43,7 +49,7 @@ abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
|
||||
return transaction(db = database) {
|
||||
values.map { value -> createWithoutNotification(value) }
|
||||
}.onEach {
|
||||
newObjectsChannel.emit(it)
|
||||
_newObjectsFlow.emit(it)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +80,7 @@ abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
|
||||
onBeforeUpdate(listOf(id to value))
|
||||
return updateWithoutNotification(id, value).also {
|
||||
if (it != null) {
|
||||
updateObjectsChannel.emit(it)
|
||||
_updatedObjectsFlow.emit(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -85,16 +91,25 @@ abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
|
||||
values.map { (id, value) -> updateWithoutNotification(id, value) }
|
||||
}.filterNotNull()
|
||||
).onEach {
|
||||
updateObjectsChannel.emit(it)
|
||||
_updatedObjectsFlow.emit(it)
|
||||
}
|
||||
}
|
||||
protected open suspend fun onBeforeDelete(ids: List<IdType>) {}
|
||||
override suspend fun deleteById(ids: List<IdType>) {
|
||||
onBeforeDelete(ids)
|
||||
transaction(db = database) {
|
||||
deleteWhere(null, null) {
|
||||
val deleted = deleteWhere(null, null) {
|
||||
selectByIds(ids)
|
||||
}
|
||||
if (deleted == ids.size) {
|
||||
ids
|
||||
} else {
|
||||
ids.filter {
|
||||
select { selectById(it) }.limit(1).none()
|
||||
}
|
||||
}
|
||||
}.forEach {
|
||||
_deletedObjectsIdsFlow.emit(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,8 +1,10 @@
|
||||
package dev.inmo.micro_utils.repos.exposed
|
||||
|
||||
import dev.inmo.micro_utils.repos.Repo
|
||||
import org.jetbrains.exposed.sql.Database
|
||||
import org.jetbrains.exposed.sql.*
|
||||
|
||||
interface ExposedRepo : Repo {
|
||||
interface ExposedRepo : Repo, FieldSet {
|
||||
val database: Database
|
||||
}
|
||||
val selectAll: Transaction.() -> Query
|
||||
get() = { (this@ExposedRepo as FieldSet).selectAll() }
|
||||
}
|
||||
|
@@ -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,
|
||||
|
@@ -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)
|
||||
|
@@ -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,
|
@@ -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()
|
||||
|
@@ -19,7 +19,7 @@ class ExposedStandardVersionsRepoProxy(
|
||||
override val database: Database
|
||||
) : StandardVersionsRepoProxy<Database>, Table("ExposedVersionsProxy"), ExposedRepo {
|
||||
val tableNameColumn = text("tableName")
|
||||
val tableVersionColumn = integer("tableName")
|
||||
val tableVersionColumn = integer("tableVersion")
|
||||
|
||||
init {
|
||||
initTable()
|
||||
|
@@ -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)
|
||||
|
@@ -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)
|
||||
|
@@ -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()
|
@@ -0,0 +1,121 @@
|
||||
package dev.inmo.micro_utils.repos.ktor.client.crud
|
||||
|
||||
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.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,
|
||||
objectTypeInfo: TypeInfo,
|
||||
contentType: ContentType,
|
||||
noinline idSerializer: suspend (IdType) -> String
|
||||
) = KtorCRUDRepoClient(
|
||||
KtorReadCRUDRepoClient(
|
||||
baseUrl,
|
||||
httpClient,
|
||||
objectTypeInfo,
|
||||
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,
|
||||
objectTypeInfo: TypeInfo,
|
||||
contentType: ContentType,
|
||||
noinline idSerializer: suspend (IdType) -> String
|
||||
) = KtorCRUDRepoClient<ObjectType, IdType, InputValue>(
|
||||
buildStandardUrl(baseUrl, subpart),
|
||||
httpClient,
|
||||
objectTypeInfo,
|
||||
contentType,
|
||||
idSerializer
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline fun <reified ObjectType, reified IdType, reified InputValue> KtorCRUDRepoClient(
|
||||
baseUrl: String,
|
||||
httpClient: HttpClient,
|
||||
contentType: ContentType,
|
||||
noinline idSerializer: suspend (IdType) -> String
|
||||
) = KtorCRUDRepoClient<ObjectType, IdType, InputValue>(
|
||||
baseUrl,
|
||||
httpClient,
|
||||
typeInfo<ObjectType>(),
|
||||
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)
|
@@ -0,0 +1,95 @@
|
||||
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 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()
|
||||
|
||||
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>(),
|
||||
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)
|
||||
}
|
@@ -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()
|
||||
)
|
||||
|
@@ -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,
|
||||
|
@@ -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() }
|
||||
)
|
||||
}
|
||||
}
|
@@ -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)
|
||||
|
@@ -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)
|
||||
}
|
@@ -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)
|
||||
}
|
@@ -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>>()
|
||||
)
|
||||
}
|
||||
}
|
@@ -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)
|
||||
}
|
@@ -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)
|
||||
}
|
@@ -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>>>()
|
||||
)
|
||||
}
|
||||
}
|
@@ -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,
|
||||
|
@@ -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,
|
||||
@@ -38,4 +39,4 @@ class KtorStandartKeyValueRepo<K, V> (
|
||||
valueNullableSerializer: KSerializer<V?>,
|
||||
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
|
||||
) : this(baseUrl, baseSubpart, UnifiedRequester(client, serialFormat), keySerializer, valueSerializer, valueNullableSerializer)
|
||||
}
|
||||
}
|
||||
|
@@ -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)
|
||||
|
@@ -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,
|
||||
@@ -34,4 +35,4 @@ class KtorOneToManyKeyValueRepo<Key, Value>(
|
||||
valueSerializer: KSerializer<Value>,
|
||||
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
|
||||
) : this (baseUrl, baseSubpart, UnifiedRequester(client, serialFormat), keySerializer, valueSerializer)
|
||||
}
|
||||
}
|
||||
|
@@ -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)
|
||||
|
||||
@@ -104,4 +105,4 @@ class KtorReadOneToManyKeyValueRepo<Key, Value> (
|
||||
Long.serializer()
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user