diff --git a/CHANGELOG.md b/CHANGELOG.md index 8082415d18b..4002495a8a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ ## 0.2.7 +* `Versions`: + * `Coroutines`: `1.4.0` -> `1.4.1` +* `Repos`: + * `WriteStandardKeyValueRepo` got new methods `set` and `unset` with collections + * All standard realizations of repos got collections methods realizations + * `Ktor`: + * `Server`: + * Fixed incorrect answer for `keyvalue` + ## 0.2.6 * `Pagination` diff --git a/gradle.properties b/gradle.properties index f4717c4c706..84c55359729 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,7 +5,7 @@ kotlin.incremental=true kotlin.incremental.js=true kotlin_version=1.4.10 -kotlin_coroutines_version=1.4.0 +kotlin_coroutines_version=1.4.1 kotlin_serialisation_core_version=1.0.1 kotlin_exposed_version=0.28.1 diff --git a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/OneToManyKeyValueRepo.kt b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/OneToManyKeyValueRepo.kt index d21048b6db0..bbc25228592 100644 --- a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/OneToManyKeyValueRepo.kt +++ b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/OneToManyKeyValueRepo.kt @@ -43,21 +43,13 @@ interface WriteOneToManyKeyValueRepo : Repo { val onValueRemoved: Flow> val onDataCleared: Flow - suspend fun add(toAdd: Map>) = toAdd.forEach { (k, values) -> - values.forEach { v -> - add(k, v) - } - } + suspend fun add(toAdd: Map>) @Deprecated("Will be extracted as extension for other add method") - suspend fun add(k: Key, v: Value) + suspend fun add(k: Key, v: Value) = add(mapOf(k to listOf(v))) - suspend fun remove(toRemove: Map>) = toRemove.forEach { (k, values) -> - values.forEach { v -> - remove(k, v) - } - } + suspend fun remove(toRemove: Map>) @Deprecated("Will be extracted as extension for other remove method") - suspend fun remove(k: Key, v: Value) + suspend fun remove(k: Key, v: Value) = remove(mapOf(k to listOf(v))) suspend fun clear(k: Key) } diff --git a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/StandartKeyValueRepo.kt b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/StandartKeyValueRepo.kt index a2666571db6..62780db7012 100644 --- a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/StandartKeyValueRepo.kt +++ b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/StandartKeyValueRepo.kt @@ -16,8 +16,16 @@ interface WriteStandardKeyValueRepo : Repo { val onNewValue: Flow> val onValueRemoved: Flow + @Deprecated("Realize set with map instead") suspend fun set(k: Key, v: Value) + suspend fun set(toSet: Map) = toSet.forEach { (k, v) -> + set(k, v) + } + @Deprecated("Realize unset with list instead") suspend fun unset(k: Key) + suspend fun unset(toUnset: List) = toUnset.forEach { + unset(it) + } } interface StandardKeyValueRepo : ReadStandardKeyValueRepo, WriteStandardKeyValueRepo \ No newline at end of file diff --git a/repos/exposed/src/jvmMain/kotlin/dev/inmo/micro_utils/repos/exposed/keyvalue/ExposedKeyValueRepo.kt b/repos/exposed/src/jvmMain/kotlin/dev/inmo/micro_utils/repos/exposed/keyvalue/ExposedKeyValueRepo.kt index 4e4a9254894..3551e591e39 100644 --- a/repos/exposed/src/jvmMain/kotlin/dev/inmo/micro_utils/repos/exposed/keyvalue/ExposedKeyValueRepo.kt +++ b/repos/exposed/src/jvmMain/kotlin/dev/inmo/micro_utils/repos/exposed/keyvalue/ExposedKeyValueRepo.kt @@ -2,10 +2,8 @@ package dev.inmo.micro_utils.repos.exposed.keyvalue import dev.inmo.micro_utils.repos.StandardKeyValueRepo import dev.inmo.micro_utils.repos.exposed.ColumnAllocator -import kotlinx.coroutines.channels.BroadcastChannel import kotlinx.coroutines.channels.Channel -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.asFlow +import kotlinx.coroutines.flow.* import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.transactions.transaction @@ -20,11 +18,11 @@ open class ExposedKeyValueRepo( valueColumnAllocator, tableName ) { - private val onNewValueChannel = BroadcastChannel>(Channel.BUFFERED) - private val onValueRemovedChannel = BroadcastChannel(Channel.BUFFERED) + private val _onNewValue = MutableSharedFlow>(Channel.BUFFERED) + private val _onValueRemoved = MutableSharedFlow(Channel.BUFFERED) - override val onNewValue: Flow> = onNewValueChannel.asFlow() - override val onValueRemoved: Flow = onValueRemovedChannel.asFlow() + override val onNewValue: Flow> = _onNewValue.asSharedFlow() + override val onValueRemoved: Flow = _onValueRemoved.asSharedFlow() override suspend fun set(k: Key, v: Value) { transaction(database) { @@ -39,14 +37,50 @@ open class ExposedKeyValueRepo( } } } - onNewValueChannel.send(k to v) + _onNewValue.emit(k to v) + } + + override suspend fun set(toSet: Map) { + transaction(database) { + toSet.mapNotNull { (k, v) -> + if (update({ keyColumn.eq(k) }) { it[valueColumn] = v } > 0) { + k to v + } else { + val inserted = insert { + it[keyColumn] = k + it[valueColumn] = v + }.getOrNull(keyColumn) != null + if (inserted) { + k to v + } else { + null + } + } + } + }.forEach { + _onNewValue.emit(it) + } } override suspend fun unset(k: Key) { transaction(database) { deleteWhere { keyColumn.eq(k) } } - onValueRemovedChannel.send(k) + _onValueRemoved.emit(k) + } + + override suspend fun unset(toUnset: List) { + transaction(database) { + toUnset.mapNotNull { + if (deleteWhere { keyColumn.eq(it) } > 0) { + it + } else { + null + } + } + }.forEach { + _onValueRemoved.emit(it) + } } } diff --git a/repos/exposed/src/jvmMain/kotlin/dev/inmo/micro_utils/repos/exposed/onetomany/ExposedOneToManyKeyValueRepo.kt b/repos/exposed/src/jvmMain/kotlin/dev/inmo/micro_utils/repos/exposed/onetomany/ExposedOneToManyKeyValueRepo.kt index fec7fbdc940..f1b0aca4b1d 100644 --- a/repos/exposed/src/jvmMain/kotlin/dev/inmo/micro_utils/repos/exposed/onetomany/ExposedOneToManyKeyValueRepo.kt +++ b/repos/exposed/src/jvmMain/kotlin/dev/inmo/micro_utils/repos/exposed/onetomany/ExposedOneToManyKeyValueRepo.kt @@ -3,7 +3,7 @@ package dev.inmo.micro_utils.repos.exposed.onetomany import dev.inmo.micro_utils.coroutines.BroadcastFlow import dev.inmo.micro_utils.repos.OneToManyKeyValueRepo import dev.inmo.micro_utils.repos.exposed.ColumnAllocator -import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.* import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.transactions.transaction @@ -18,13 +18,13 @@ open class ExposedOneToManyKeyValueRepo( valueColumnAllocator, tableName ) { - protected val _onNewValue: BroadcastFlow> = BroadcastFlow() + protected val _onNewValue: MutableSharedFlow> = MutableSharedFlow() override val onNewValue: Flow> get() = _onNewValue - protected val _onValueRemoved: BroadcastFlow> = BroadcastFlow() + protected val _onValueRemoved: MutableSharedFlow> = MutableSharedFlow() override val onValueRemoved: Flow> get() = _onValueRemoved - protected val _onDataCleared: BroadcastFlow = BroadcastFlow() + protected val _onDataCleared: MutableSharedFlow = MutableSharedFlow() override val onDataCleared: Flow get() = _onDataCleared @@ -34,19 +34,42 @@ open class ExposedOneToManyKeyValueRepo( it[keyColumn] = k it[valueColumn] = v } - }.also { _onNewValue.send(k to v) } + }.also { _onNewValue.emit(k to v) } } - override suspend fun remove(k: Key, v: Value) { + override suspend fun add(toAdd: Map>) { transaction(database) { - deleteWhere { keyColumn.eq(k).and(valueColumn.eq(v)) } - }.also { _onValueRemoved.send(k to v) } + toAdd.keys.flatMap { k -> + toAdd[k] ?.mapNotNull { v -> + insertIgnore { + it[keyColumn] = k + it[valueColumn] = v + }.getOrNull(keyColumn) ?.let { k to v } + } ?: emptyList() + } + }.forEach { _onNewValue.emit(it) } + } + + override suspend fun remove(toRemove: Map>) { + transaction(database) { + toRemove.keys.flatMap { k -> + toRemove[k] ?.mapNotNull { v -> + if (deleteIgnoreWhere { keyColumn.eq(k).and(valueColumn.eq(v)) } > 0 ) { + k to v + } else { + null + } + } ?: emptyList() + } + }.forEach { + _onValueRemoved.emit(it) + } } override suspend fun clear(k: Key) { transaction(database) { deleteWhere { keyColumn.eq(k) } - }.also { _onDataCleared.send(k) } + }.also { _onDataCleared.emit(k) } } } diff --git a/repos/inmemory/src/commonMain/kotlin/dev/inmo/micro_utils/repos/MapKeyValueRepo.kt b/repos/inmemory/src/commonMain/kotlin/dev/inmo/micro_utils/repos/MapKeyValueRepo.kt index 221a1110441..2456e04d5b1 100644 --- a/repos/inmemory/src/commonMain/kotlin/dev/inmo/micro_utils/repos/MapKeyValueRepo.kt +++ b/repos/inmemory/src/commonMain/kotlin/dev/inmo/micro_utils/repos/MapKeyValueRepo.kt @@ -6,6 +6,7 @@ import dev.inmo.micro_utils.pagination.PaginationResult import dev.inmo.micro_utils.pagination.utils.paginate import dev.inmo.micro_utils.pagination.utils.reverse import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow class ReadMapKeyValueRepo( private val map: Map = emptyMap() @@ -47,20 +48,31 @@ class ReadMapKeyValueRepo( class WriteMapKeyValueRepo( private val map: MutableMap = mutableMapOf() ) : WriteStandardKeyValueRepo { - private val _onNewValue: BroadcastFlow> = BroadcastFlow() + private val _onNewValue: MutableSharedFlow> = MutableSharedFlow() override val onNewValue: Flow> get() = _onNewValue - private val _onValueRemoved: BroadcastFlow = BroadcastFlow() + private val _onValueRemoved: MutableSharedFlow = MutableSharedFlow() override val onValueRemoved: Flow get() = _onValueRemoved override suspend fun set(k: Key, v: Value) { map[k] = v - _onNewValue.send(k to v) + _onNewValue.emit(k to v) + } + + override suspend fun set(toSet: Map) { + map.putAll(toSet) + toSet.forEach { (k, v) -> _onNewValue.emit(k to v) } } override suspend fun unset(k: Key) { - map.remove(k) ?.also { _onValueRemoved.send(k) } + map.remove(k) ?.also { _onValueRemoved.emit(k) } + } + + override suspend fun unset(toUnset: List) { + toUnset.forEach { k -> + map.remove(k) ?.also { _ -> _onValueRemoved.emit(k) } + } } } diff --git a/repos/inmemory/src/commonMain/kotlin/dev/inmo/micro_utils/repos/MapOneToManyKeyValueRepo.kt b/repos/inmemory/src/commonMain/kotlin/dev/inmo/micro_utils/repos/MapOneToManyKeyValueRepo.kt index 47cdb32b4a5..f9b681b9f8d 100644 --- a/repos/inmemory/src/commonMain/kotlin/dev/inmo/micro_utils/repos/MapOneToManyKeyValueRepo.kt +++ b/repos/inmemory/src/commonMain/kotlin/dev/inmo/micro_utils/repos/MapOneToManyKeyValueRepo.kt @@ -55,13 +55,18 @@ class MapWriteOneToManyKeyValueRepo( override val onDataCleared: Flow get() = _onDataCleared - override suspend fun add(k: Key, v: Value) { - map.getOrPut(k) { mutableListOf() }.add(v) - _onNewValue.send(k to v) + override suspend fun add(toAdd: Map>) { + toAdd.keys.forEach { + map.getOrPut(it) { + mutableListOf() + }.addAll(toAdd[it] ?: return@forEach) + } } - override suspend fun remove(k: Key, v: Value) { - map[k] ?.remove(v) ?.also { _onValueRemoved.send(k to v) } + override suspend fun remove(toRemove: Map>) { + toRemove.keys.forEach { + map[it] ?.removeAll(toRemove[it] ?: return@forEach) + } } override suspend fun clear(k: Key) { diff --git a/repos/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/repos/ktor/client/key_value/KtorWriteStandardKeyValueRepo.kt b/repos/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/repos/ktor/client/key_value/KtorWriteStandardKeyValueRepo.kt index fd3fefeb48b..440c22e8350 100644 --- a/repos/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/repos/ktor/client/key_value/KtorWriteStandardKeyValueRepo.kt +++ b/repos/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/repos/ktor/client/key_value/KtorWriteStandardKeyValueRepo.kt @@ -7,8 +7,7 @@ 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.PairSerializer -import kotlinx.serialization.builtins.serializer +import kotlinx.serialization.builtins.* class KtorWriteStandardKeyValueRepo ( private var baseUrl: String, @@ -16,6 +15,8 @@ class KtorWriteStandardKeyValueRepo ( private var keySerializer: KSerializer, private var valueSerializer: KSerializer, ) : WriteStandardKeyValueRepo { + private val keyValueMapSerializer = MapSerializer(keySerializer, valueSerializer) + private val keysListSerializer = ListSerializer(keySerializer) override val onNewValue: Flow> = client.createStandardWebsocketFlow( buildStandardUrl(baseUrl, onNewValueRoute), deserializer = PairSerializer(keySerializer, valueSerializer) @@ -26,23 +27,25 @@ class KtorWriteStandardKeyValueRepo ( deserializer = keySerializer ) - override suspend fun set(k: K, v: V) = client.unipost( + override suspend fun set(toSet: Map) = client.unipost( buildStandardUrl( baseUrl, setRoute ), - BodyPair(KeyValuePostObject.serializer(keySerializer, valueSerializer), KeyValuePostObject(k, v)), + BodyPair(keyValueMapSerializer, toSet), Unit.serializer() ) + override suspend fun set(k: K, v: V) = set(mapOf(k to v)) - override suspend fun unset(k: K) = client.unipost( + override suspend fun unset(toUnset: List) = client.unipost( buildStandardUrl( baseUrl, unsetRoute, ), - BodyPair(keySerializer, k), + BodyPair(keysListSerializer, toUnset), Unit.serializer() ) + override suspend fun unset(k: K) = unset(listOf(k)) } @Deprecated("Renamed", ReplaceWith("KtorWriteStandardKeyValueRepo", "dev.inmo.micro_utils.repos.ktor.client.key_value.KtorWriteStandardKeyValueRepo")) diff --git a/repos/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/repos/ktor/client/one_to_many/KtorWriteOneToManyKeyValueRepo.kt b/repos/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/repos/ktor/client/one_to_many/KtorWriteOneToManyKeyValueRepo.kt index aecfc496702..bc7de0bcd88 100644 --- a/repos/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/repos/ktor/client/one_to_many/KtorWriteOneToManyKeyValueRepo.kt +++ b/repos/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/repos/ktor/client/one_to_many/KtorWriteOneToManyKeyValueRepo.kt @@ -7,8 +7,7 @@ import dev.inmo.micro_utils.repos.ktor.common.one_to_many.* import io.ktor.client.HttpClient import kotlinx.coroutines.flow.Flow import kotlinx.serialization.KSerializer -import kotlinx.serialization.builtins.PairSerializer -import kotlinx.serialization.builtins.serializer +import kotlinx.serialization.builtins.* class KtorWriteOneToManyKeyValueRepo ( private val baseUrl: String, @@ -17,6 +16,7 @@ class KtorWriteOneToManyKeyValueRepo ( private val valueSerializer: KSerializer ) : WriteOneToManyKeyValueRepo { private val keyValueSerializer = PairSerializer(keySerializer, valueSerializer) + private val keyValueMapSerializer = MapSerializer(keySerializer, ListSerializer(valueSerializer)) override val onNewValue: Flow> = client.createStandardWebsocketFlow( buildStandardUrl(baseUrl, onNewValueRoute), deserializer = keyValueSerializer @@ -39,15 +39,23 @@ class KtorWriteOneToManyKeyValueRepo ( Unit.serializer(), ) - override suspend fun remove(k: Key, v: Value) = client.unipost( + override suspend fun remove(toRemove: Map>) = client.unipost( buildStandardUrl( baseUrl, removeRoute, ), - BodyPair(keyValueSerializer, k to v), + BodyPair(keyValueMapSerializer, toRemove), Unit.serializer(), ) + override suspend fun add(toAdd: Map>) = client.unipost( + buildStandardUrl( + baseUrl, + clearRoute, + ), + BodyPair(keyValueMapSerializer, toAdd), + Unit.serializer(), + ) override suspend fun clear(k: Key) = client.unipost( buildStandardUrl( baseUrl, diff --git a/repos/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/repos/ktor/server/key_value/KtorStandartWriteKeyValueRepo.kt b/repos/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/repos/ktor/server/key_value/KtorStandartWriteKeyValueRepo.kt index 75d06732168..57a16f20cba 100644 --- a/repos/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/repos/ktor/server/key_value/KtorStandartWriteKeyValueRepo.kt +++ b/repos/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/repos/ktor/server/key_value/KtorStandartWriteKeyValueRepo.kt @@ -1,20 +1,21 @@ package dev.inmo.micro_utils.repos.ktor.server.key_value -import dev.inmo.micro_utils.ktor.server.includeWebsocketHandling -import dev.inmo.micro_utils.ktor.server.uniload +import dev.inmo.micro_utils.ktor.server.* import dev.inmo.micro_utils.repos.WriteStandardKeyValueRepo import dev.inmo.micro_utils.repos.ktor.common.key_value.* import io.ktor.application.call import io.ktor.routing.Route import io.ktor.routing.post import kotlinx.serialization.KSerializer -import kotlinx.serialization.builtins.PairSerializer +import kotlinx.serialization.builtins.* fun Route.configureWriteStandartKeyValueRepoRoutes ( originalRepo: WriteStandardKeyValueRepo, keySerializer: KSerializer, valueSerializer: KSerializer, ) { + val keyValueMapSerializer = MapSerializer(keySerializer, valueSerializer) + val keysListSerializer = ListSerializer(keySerializer) includeWebsocketHandling( onNewValueRoute, originalRepo.onNewValue, @@ -28,18 +29,16 @@ fun Route.configureWriteStandartKeyValueRepoRoutes ( ) post(setRoute) { - val (key, value) = call.uniload( - KeyValuePostObject.serializer(keySerializer, valueSerializer) + val toSet = call.uniload( + keyValueMapSerializer ) - originalRepo.set(key, value) + call.unianswer(Unit.serializer(), originalRepo.set(toSet)) } post(unsetRoute) { - val key = call.uniload( - keySerializer - ) + val toUnset = call.uniload(keysListSerializer) - originalRepo.unset(key) + call.unianswer(Unit.serializer(), originalRepo.unset(toUnset)) } } \ No newline at end of file diff --git a/repos/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/repos/ktor/server/one_to_many/ConfigureOneToManyWriteKeyValueRepoRoutes.kt b/repos/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/repos/ktor/server/one_to_many/ConfigureOneToManyWriteKeyValueRepoRoutes.kt index 788ea78a16a..fec8fa9d416 100644 --- a/repos/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/repos/ktor/server/one_to_many/ConfigureOneToManyWriteKeyValueRepoRoutes.kt +++ b/repos/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/repos/ktor/server/one_to_many/ConfigureOneToManyWriteKeyValueRepoRoutes.kt @@ -7,15 +7,15 @@ import io.ktor.application.call import io.ktor.routing.Route import io.ktor.routing.post import kotlinx.serialization.KSerializer -import kotlinx.serialization.builtins.PairSerializer -import kotlinx.serialization.builtins.serializer +import kotlinx.serialization.builtins.* fun Route.configureOneToManyWriteKeyValueRepoRoutes( originalRepo: WriteOneToManyKeyValueRepo, keySerializer: KSerializer, - valueSealizer: KSerializer, + valueSerializer: KSerializer, ) { - val keyValueSerializer = PairSerializer(keySerializer, valueSealizer) + val keyValueSerializer = PairSerializer(keySerializer, valueSerializer) + val keyValueMapSerializer = MapSerializer(keySerializer, ListSerializer(valueSerializer)) includeWebsocketHandling( onNewValueRoute, @@ -34,24 +34,22 @@ fun Route.configureOneToManyWriteKeyValueRepoRoutes( ) post(addRoute) { - val obj = call.uniload( - keyValueSerializer - ) + val obj = call.uniload(keyValueMapSerializer) call.unianswer( Unit.serializer(), - originalRepo.add(obj.first, obj.second) + originalRepo.add(obj) ) } post(removeRoute) { val obj = call.uniload( - keyValueSerializer + keyValueMapSerializer ) call.unianswer( Unit.serializer(), - originalRepo.remove(obj.first, obj.second), + originalRepo.remove(obj), ) }