mirror of
https://github.com/InsanusMokrassar/MicroUtils.git
synced 2025-09-18 06:49:20 +00:00
Compare commits
40 Commits
Author | SHA1 | Date | |
---|---|---|---|
eb959a3135 | |||
24033e0cac | |||
71f9a505e0 | |||
979b8f017b | |||
af78f01682 | |||
0b16d5c826 | |||
597e14bc7e | |||
04a95867e2 | |||
e0d5eb45b7 | |||
b90cab318e | |||
3252b61abe | |||
2a2da21ff3 | |||
04ef371337 | |||
623e0cd369 | |||
1f466747f0 | |||
2215462f99 | |||
ac4c0a2e4c | |||
f7496db5ac | |||
3028fe975d | |||
23a5034493 | |||
65e339f811 | |||
2020e48659 | |||
9566d6f81f | |||
a00d734712 | |||
27a3e8706a | |||
e601efcfc0 | |||
2bfad9f885 | |||
e78e984943 | |||
242f4b02d0 | |||
041be5a1d1 | |||
976ce056c1 | |||
00c23c73a8 | |||
9dd1848337 | |||
9b30efd9a2 | |||
5853f7cc49 | |||
7b00a06f3e | |||
9ef9be0f37 | |||
13ca419473 | |||
b80f1a0773 | |||
e85101c74e |
12
.github/workflows/build.yml
vendored
Normal file
12
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
name: Regular build
|
||||||
|
on: [push]
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-java@v1
|
||||||
|
with:
|
||||||
|
java-version: 1.8
|
||||||
|
- name: Build
|
||||||
|
run: ./gradlew build
|
57
CHANGELOG.md
57
CHANGELOG.md
@@ -1,5 +1,62 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.5.8
|
||||||
|
|
||||||
|
* `Common`:
|
||||||
|
* New extension `Iterable#firstNotNull`
|
||||||
|
* `Coroutines`
|
||||||
|
* New extension `Flow#firstNotNull`
|
||||||
|
* New extensions `CoroutineContext#LinkedSupervisorJob`, `CoroutineScope#LinkedSupervisorJob` and
|
||||||
|
`CoroutineScope#LinkedSupervisorScope`
|
||||||
|
|
||||||
|
## 0.5.7
|
||||||
|
|
||||||
|
* `Pagination`
|
||||||
|
* `Ktor`
|
||||||
|
* `Server`
|
||||||
|
* Fixes in extension `extractPagination`
|
||||||
|
* `Repos`
|
||||||
|
* `Cache`
|
||||||
|
* All standard cache repos have been separated to read and read/write repos
|
||||||
|
|
||||||
|
## 0.5.6
|
||||||
|
|
||||||
|
* `Versions`
|
||||||
|
* `Exposed`: `0.31.1` -> `0.32.1`
|
||||||
|
* `Coroutines`
|
||||||
|
* `JVM`
|
||||||
|
* `launchSynchronously` and subsequent functions got improved mechanism
|
||||||
|
* New method `safelyWithResult`
|
||||||
|
|
||||||
|
## 0.5.5
|
||||||
|
|
||||||
|
* `Versions`
|
||||||
|
* `Ktor`: `1.5.4` -> `1.6.0`
|
||||||
|
|
||||||
|
## 0.5.4
|
||||||
|
|
||||||
|
* `Versions`:
|
||||||
|
* `Klock`: `2.1.0` -> `2.1.2`
|
||||||
|
|
||||||
|
## 0.5.3
|
||||||
|
|
||||||
|
* `Versions`:
|
||||||
|
* `Kotlin`: `1.5.0` -> `1.5.10`
|
||||||
|
* `Coroutines`:
|
||||||
|
* Extensions `doInUI` and `doInDefault` were replaced in common and available on any supported platform
|
||||||
|
* Extension `doInIO` replaced into `jvm` and available on any `JVM` platform
|
||||||
|
* Old extension `safelyWithouException` without `onException` has been replaced by its copy with `onException` and
|
||||||
|
default value
|
||||||
|
* New value `defaultSafelyWithoutExceptionHandlerWithNull` which is used in all `*WithoutExceptions` by default
|
||||||
|
* Analogs of `launch` and `async` for `safely` and `safelyWithoutExceptions` were added
|
||||||
|
* Analogs of `runCatching` for `safely` and `safelyWithoutExceptions` were added
|
||||||
|
|
||||||
|
## 0.5.2
|
||||||
|
|
||||||
|
* `Ktor`:
|
||||||
|
* `Client`:
|
||||||
|
* Fixes in `UnifiedRequester`
|
||||||
|
|
||||||
## 0.5.1
|
## 0.5.1
|
||||||
|
|
||||||
* `Versions`:
|
* `Versions`:
|
||||||
|
@@ -0,0 +1,3 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
fun <T> Iterable<T?>.firstNotNull() = first { it != null }!!
|
@@ -19,4 +19,4 @@ kotlin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -38,7 +38,7 @@ inline fun CoroutineScope.createSafeActionsActor(
|
|||||||
suspend fun <T> Channel<suspend () -> Unit>.doWithSuspending(
|
suspend fun <T> Channel<suspend () -> Unit>.doWithSuspending(
|
||||||
action: ActorAction<T>
|
action: ActorAction<T>
|
||||||
) = suspendCoroutine<T> {
|
) = suspendCoroutine<T> {
|
||||||
offer {
|
trySend {
|
||||||
safely({ e -> it.resumeWithException(e) }) {
|
safely({ e -> it.resumeWithException(e) }) {
|
||||||
it.resume(action())
|
it.resume(action())
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,23 @@
|
|||||||
|
package dev.inmo.micro_utils.coroutines
|
||||||
|
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
|
inline val UI
|
||||||
|
get() = Dispatchers.Main
|
||||||
|
inline val Default
|
||||||
|
get() = Dispatchers.Default
|
||||||
|
|
||||||
|
suspend inline fun <T> doIn(context: CoroutineContext, noinline block: suspend CoroutineScope.() -> T) = withContext(
|
||||||
|
context,
|
||||||
|
block
|
||||||
|
)
|
||||||
|
|
||||||
|
suspend inline fun <T> doInUI(noinline block: suspend CoroutineScope.() -> T) = doIn(
|
||||||
|
UI,
|
||||||
|
block
|
||||||
|
)
|
||||||
|
suspend inline fun <T> doInDefault(noinline block: suspend CoroutineScope.() -> T) = doIn(
|
||||||
|
Default,
|
||||||
|
block
|
||||||
|
)
|
@@ -0,0 +1,6 @@
|
|||||||
|
package dev.inmo.micro_utils.coroutines
|
||||||
|
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
|
||||||
|
suspend fun <T> Flow<T?>.firstNotNull() = first { it != null }!!
|
@@ -86,6 +86,9 @@ suspend fun <T> safelyWithContextExceptionHandler(
|
|||||||
* * [CoroutineContext.get] with [SafelyExceptionHandlerKey] as key
|
* * [CoroutineContext.get] with [SafelyExceptionHandlerKey] as key
|
||||||
* * [defaultSafelyExceptionHandler]
|
* * [defaultSafelyExceptionHandler]
|
||||||
*
|
*
|
||||||
|
* Remember, that [ExceptionHandler] from [CoroutineContext.get] will be used anyway if it is available. After it will
|
||||||
|
* be called [onException]
|
||||||
|
*
|
||||||
* @param [onException] Will be called when happen exception inside of [block]. By default will throw exception - this
|
* @param [onException] Will be called when happen exception inside of [block]. By default will throw exception - this
|
||||||
* exception will be available for catching
|
* exception will be available for catching
|
||||||
*
|
*
|
||||||
@@ -105,24 +108,42 @@ suspend inline fun <T> safely(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend inline fun <T> runCatchingSafely(
|
||||||
|
noinline onException: ExceptionHandler<T> = defaultSafelyExceptionHandler,
|
||||||
|
noinline block: suspend CoroutineScope.() -> T
|
||||||
|
): Result<T> = runCatching {
|
||||||
|
safely(onException, block)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend inline fun <T> safelyWithResult(
|
||||||
|
noinline block: suspend CoroutineScope.() -> T
|
||||||
|
): Result<T> = runCatchingSafely(defaultSafelyExceptionHandler, block)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this handler in cases you wish to include handling of exceptions by [defaultSafelyWithoutExceptionHandler] and
|
||||||
|
* returning null at one time
|
||||||
|
*
|
||||||
|
* @see safelyWithoutExceptions
|
||||||
|
* @see launchSafelyWithoutExceptions
|
||||||
|
* @see asyncSafelyWithoutExceptions
|
||||||
|
*/
|
||||||
|
val defaultSafelyWithoutExceptionHandlerWithNull: ExceptionHandler<Nothing?> = {
|
||||||
|
defaultSafelyWithoutExceptionHandler.invoke(it)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shortcut for [safely] with exception handler, that as expected must return null in case of impossible creating of
|
* Shortcut for [safely] with exception handler, that as expected must return null in case of impossible creating of
|
||||||
* result from exception (instead of throwing it)
|
* result from exception (instead of throwing it, by default always returns null)
|
||||||
*/
|
*/
|
||||||
suspend inline fun <T> safelyWithoutExceptions(
|
suspend inline fun <T> safelyWithoutExceptions(
|
||||||
noinline onException: ExceptionHandler<T?>,
|
noinline onException: ExceptionHandler<T?> = defaultSafelyWithoutExceptionHandlerWithNull,
|
||||||
noinline block: suspend CoroutineScope.() -> T
|
noinline block: suspend CoroutineScope.() -> T
|
||||||
): T? = safely(onException, block)
|
): T? = safely(onException, block)
|
||||||
|
|
||||||
/**
|
suspend inline fun <T> runCatchingSafelyWithoutExceptions(
|
||||||
* Shortcut for [safely] without exception handler (instead of this you will always receive null as a result)
|
noinline onException: ExceptionHandler<T?> = defaultSafelyWithoutExceptionHandlerWithNull,
|
||||||
*/
|
|
||||||
suspend inline fun <T> safelyWithoutExceptions(
|
|
||||||
noinline block: suspend CoroutineScope.() -> T
|
noinline block: suspend CoroutineScope.() -> T
|
||||||
): T? = safelyWithoutExceptions(
|
): Result<T?> = runCatching {
|
||||||
{
|
safelyWithoutExceptions(onException, block)
|
||||||
defaultSafelyWithoutExceptionHandler.invoke(it)
|
}
|
||||||
null
|
|
||||||
},
|
|
||||||
block
|
|
||||||
)
|
|
||||||
|
@@ -0,0 +1,41 @@
|
|||||||
|
package dev.inmo.micro_utils.coroutines
|
||||||
|
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
import kotlin.coroutines.EmptyCoroutineContext
|
||||||
|
|
||||||
|
inline fun CoroutineScope.launchSafely(
|
||||||
|
context: CoroutineContext = EmptyCoroutineContext,
|
||||||
|
start: CoroutineStart = CoroutineStart.DEFAULT,
|
||||||
|
noinline onException: ExceptionHandler<Unit> = defaultSafelyExceptionHandler,
|
||||||
|
noinline block: suspend CoroutineScope.() -> Unit
|
||||||
|
) = launch(context, start) {
|
||||||
|
safely(onException, block)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun CoroutineScope.launchSafelyWithoutExceptions(
|
||||||
|
context: CoroutineContext = EmptyCoroutineContext,
|
||||||
|
start: CoroutineStart = CoroutineStart.DEFAULT,
|
||||||
|
noinline onException: ExceptionHandler<Unit?> = defaultSafelyWithoutExceptionHandlerWithNull,
|
||||||
|
noinline block: suspend CoroutineScope.() -> Unit
|
||||||
|
) = launch(context, start) {
|
||||||
|
safelyWithoutExceptions(onException, block)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <T> CoroutineScope.asyncSafely(
|
||||||
|
context: CoroutineContext = EmptyCoroutineContext,
|
||||||
|
start: CoroutineStart = CoroutineStart.DEFAULT,
|
||||||
|
noinline onException: ExceptionHandler<T> = defaultSafelyExceptionHandler,
|
||||||
|
noinline block: suspend CoroutineScope.() -> T
|
||||||
|
) = async(context, start) {
|
||||||
|
safely(onException, block)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <T> CoroutineScope.asyncSafelyWithoutExceptions(
|
||||||
|
context: CoroutineContext = EmptyCoroutineContext,
|
||||||
|
start: CoroutineStart = CoroutineStart.DEFAULT,
|
||||||
|
noinline onException: ExceptionHandler<T?> = defaultSafelyWithoutExceptionHandlerWithNull,
|
||||||
|
noinline block: suspend CoroutineScope.() -> T
|
||||||
|
) = async(context, start) {
|
||||||
|
safelyWithoutExceptions(onException, block)
|
||||||
|
}
|
@@ -0,0 +1,17 @@
|
|||||||
|
package dev.inmo.micro_utils.coroutines
|
||||||
|
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
|
fun CoroutineContext.LinkedSupervisorJob(
|
||||||
|
additionalContext: CoroutineContext? = null
|
||||||
|
) = SupervisorJob(job).let { if (additionalContext != null) it + additionalContext else it }
|
||||||
|
fun CoroutineScope.LinkedSupervisorJob(
|
||||||
|
additionalContext: CoroutineContext? = null
|
||||||
|
) = coroutineContext.LinkedSupervisorJob(additionalContext)
|
||||||
|
|
||||||
|
fun CoroutineScope.LinkedSupervisorScope(
|
||||||
|
additionalContext: CoroutineContext? = null
|
||||||
|
) = CoroutineScope(
|
||||||
|
coroutineContext + LinkedSupervisorJob(additionalContext)
|
||||||
|
)
|
@@ -0,0 +1,11 @@
|
|||||||
|
package dev.inmo.micro_utils.coroutines
|
||||||
|
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
|
||||||
|
val IO
|
||||||
|
get() = Dispatchers.IO
|
||||||
|
|
||||||
|
suspend inline fun <T> doInIO(noinline block: suspend CoroutineScope.() -> T) = doIn(
|
||||||
|
IO,
|
||||||
|
block
|
||||||
|
)
|
@@ -3,27 +3,21 @@ package dev.inmo.micro_utils.coroutines
|
|||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
|
|
||||||
fun <T> CoroutineScope.launchSynchronously(block: suspend CoroutineScope.() -> T): T {
|
fun <T> CoroutineScope.launchSynchronously(block: suspend CoroutineScope.() -> T): T {
|
||||||
val deferred = CompletableDeferred<T>()
|
var result: Result<T>? = null
|
||||||
val objectToSynchronize = java.lang.Object()
|
val objectToSynchronize = Object()
|
||||||
val launchCallback = {
|
synchronized(objectToSynchronize) {
|
||||||
launch {
|
launch {
|
||||||
safely(
|
result = safelyWithResult(block)
|
||||||
{
|
}.invokeOnCompletion {
|
||||||
deferred.completeExceptionally(it)
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
deferred.complete(block())
|
|
||||||
}
|
|
||||||
synchronized(objectToSynchronize) {
|
synchronized(objectToSynchronize) {
|
||||||
objectToSynchronize.notifyAll()
|
objectToSynchronize.notifyAll()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
while (result == null) {
|
||||||
|
objectToSynchronize.wait()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
synchronized(objectToSynchronize) {
|
return result!!.getOrThrow()
|
||||||
launchCallback()
|
|
||||||
objectToSynchronize.wait()
|
|
||||||
}
|
|
||||||
return deferred.getCompleted()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> launchSynchronously(block: suspend CoroutineScope.() -> T): T = CoroutineScope(Dispatchers.Default).launchSynchronously(block)
|
fun <T> launchSynchronously(block: suspend CoroutineScope.() -> T): T = CoroutineScope(Dispatchers.Default).launchSynchronously(block)
|
||||||
|
@@ -1,9 +1,8 @@
|
|||||||
package dev.inmo.micro_utils.coroutines
|
package dev.inmo.micro_utils.coroutines
|
||||||
|
|
||||||
import dev.inmo.micro_utils.coroutines.asDeferred
|
|
||||||
import dev.inmo.micro_utils.coroutines.launchSynchronously
|
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlin.test.*
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
class DoWithFirstTests {
|
class DoWithFirstTests {
|
||||||
@Test
|
@Test
|
||||||
|
@@ -1,18 +0,0 @@
|
|||||||
package dev.inmo.micro_utils.coroutines
|
|
||||||
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
|
|
||||||
suspend inline fun <T> doInUI(noinline block: suspend CoroutineScope.() -> T) = withContext(
|
|
||||||
Dispatchers.Main,
|
|
||||||
block
|
|
||||||
)
|
|
||||||
suspend inline fun <T> doInDefault(noinline block: suspend CoroutineScope.() -> T) = withContext(
|
|
||||||
Dispatchers.Default,
|
|
||||||
block
|
|
||||||
)
|
|
||||||
suspend inline fun <T> doInIO(noinline block: suspend CoroutineScope.() -> T) = withContext(
|
|
||||||
Dispatchers.IO,
|
|
||||||
block
|
|
||||||
)
|
|
@@ -7,14 +7,14 @@ android.useAndroidX=true
|
|||||||
android.enableJetifier=true
|
android.enableJetifier=true
|
||||||
org.gradle.jvmargs=-Xmx2g
|
org.gradle.jvmargs=-Xmx2g
|
||||||
|
|
||||||
kotlin_version=1.5.0
|
kotlin_version=1.5.10
|
||||||
kotlin_coroutines_version=1.5.0
|
kotlin_coroutines_version=1.5.0
|
||||||
kotlin_serialisation_core_version=1.2.1
|
kotlin_serialisation_core_version=1.2.1
|
||||||
kotlin_exposed_version=0.31.1
|
kotlin_exposed_version=0.32.1
|
||||||
|
|
||||||
ktor_version=1.5.4
|
ktor_version=1.6.0
|
||||||
|
|
||||||
klockVersion=2.1.0
|
klockVersion=2.1.2
|
||||||
|
|
||||||
github_release_plugin_version=2.2.12
|
github_release_plugin_version=2.2.12
|
||||||
|
|
||||||
@@ -45,5 +45,5 @@ dokka_version=1.4.32
|
|||||||
# Project data
|
# Project data
|
||||||
|
|
||||||
group=dev.inmo
|
group=dev.inmo
|
||||||
version=0.5.1
|
version=0.5.8
|
||||||
android_code_version=42
|
android_code_version=49
|
||||||
|
@@ -15,30 +15,21 @@ class UnifiedRequester(
|
|||||||
suspend fun <ResultType> uniget(
|
suspend fun <ResultType> uniget(
|
||||||
url: String,
|
url: String,
|
||||||
resultDeserializer: DeserializationStrategy<ResultType>
|
resultDeserializer: DeserializationStrategy<ResultType>
|
||||||
): ResultType = client.get<StandardKtorSerialInputData>(
|
): ResultType = client.uniget(url, resultDeserializer, serialFormat)
|
||||||
url
|
|
||||||
).let {
|
|
||||||
serialFormat.decodeDefault(resultDeserializer, it)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fun <T> encodeUrlQueryValue(
|
fun <T> encodeUrlQueryValue(
|
||||||
serializationStrategy: SerializationStrategy<T>,
|
serializationStrategy: SerializationStrategy<T>,
|
||||||
value: T
|
value: T
|
||||||
) = serialFormat.encodeHex(
|
) = serializationStrategy.encodeUrlQueryValue(
|
||||||
serializationStrategy,
|
value,
|
||||||
value
|
serialFormat
|
||||||
)
|
)
|
||||||
|
|
||||||
suspend fun <BodyType, ResultType> unipost(
|
suspend fun <BodyType, ResultType> unipost(
|
||||||
url: String,
|
url: String,
|
||||||
bodyInfo: BodyPair<BodyType>,
|
bodyInfo: BodyPair<BodyType>,
|
||||||
resultDeserializer: DeserializationStrategy<ResultType>
|
resultDeserializer: DeserializationStrategy<ResultType>
|
||||||
) = client.post<StandardKtorSerialInputData>(url) {
|
) = client.unipost(url, bodyInfo, resultDeserializer, serialFormat)
|
||||||
body = serialFormat.encodeDefault(bodyInfo.first, bodyInfo.second)
|
|
||||||
}.let {
|
|
||||||
serialFormat.decodeDefault(resultDeserializer, it)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T> createStandardWebsocketFlow(
|
fun <T> createStandardWebsocketFlow(
|
||||||
url: String,
|
url: String,
|
||||||
@@ -51,14 +42,30 @@ val defaultRequester = UnifiedRequester()
|
|||||||
|
|
||||||
suspend fun <ResultType> HttpClient.uniget(
|
suspend fun <ResultType> HttpClient.uniget(
|
||||||
url: String,
|
url: String,
|
||||||
resultDeserializer: DeserializationStrategy<ResultType>
|
resultDeserializer: DeserializationStrategy<ResultType>,
|
||||||
) = defaultRequester.uniget(url, resultDeserializer)
|
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
|
||||||
|
) = get<StandardKtorSerialInputData>(
|
||||||
|
url
|
||||||
|
).let {
|
||||||
|
serialFormat.decodeDefault(resultDeserializer, it)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fun <T> SerializationStrategy<T>.encodeUrlQueryValue(value: T) = defaultRequester.encodeUrlQueryValue(this, value)
|
fun <T> SerializationStrategy<T>.encodeUrlQueryValue(
|
||||||
|
value: T,
|
||||||
|
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
|
||||||
|
) = serialFormat.encodeHex(
|
||||||
|
this,
|
||||||
|
value
|
||||||
|
)
|
||||||
|
|
||||||
suspend fun <BodyType, ResultType> HttpClient.unipost(
|
suspend fun <BodyType, ResultType> HttpClient.unipost(
|
||||||
url: String,
|
url: String,
|
||||||
bodyInfo: BodyPair<BodyType>,
|
bodyInfo: BodyPair<BodyType>,
|
||||||
resultDeserializer: DeserializationStrategy<ResultType>
|
resultDeserializer: DeserializationStrategy<ResultType>,
|
||||||
) = defaultRequester.unipost(url, bodyInfo, resultDeserializer)
|
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
|
||||||
|
) = post<StandardKtorSerialInputData>(url) {
|
||||||
|
body = serialFormat.encodeDefault(bodyInfo.first, bodyInfo.second)
|
||||||
|
}.let {
|
||||||
|
serialFormat.decodeDefault(resultDeserializer, it)
|
||||||
|
}
|
||||||
|
@@ -10,7 +10,7 @@ import kotlinx.coroutines.flow.collect
|
|||||||
import kotlinx.serialization.SerializationStrategy
|
import kotlinx.serialization.SerializationStrategy
|
||||||
|
|
||||||
private suspend fun DefaultWebSocketSession.checkReceivedAndCloseIfExists() {
|
private suspend fun DefaultWebSocketSession.checkReceivedAndCloseIfExists() {
|
||||||
if (incoming.poll() != null) {
|
if (incoming.tryReceive() != null) {
|
||||||
close()
|
close()
|
||||||
throw CorrectCloseException
|
throw CorrectCloseException
|
||||||
}
|
}
|
||||||
|
@@ -5,8 +5,8 @@ import io.ktor.http.Parameters
|
|||||||
|
|
||||||
val Parameters.extractPagination: Pagination
|
val Parameters.extractPagination: Pagination
|
||||||
get() = SimplePagination(
|
get() = SimplePagination(
|
||||||
get("page") ?.toIntOrNull() ?: 0,
|
get(paginationPageKey) ?.toIntOrNull() ?: 0,
|
||||||
get("size") ?.toIntOrNull() ?: defaultPaginationPageSize
|
get(paginationSizeKey) ?.toIntOrNull() ?: defaultPaginationPageSize
|
||||||
)
|
)
|
||||||
|
|
||||||
val ApplicationCall.extractPagination: Pagination
|
val ApplicationCall.extractPagination: Pagination
|
||||||
|
@@ -6,19 +6,29 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
|
||||||
open class CRUDCacheRepo<ObjectType, IdType, InputValueType>(
|
open class ReadCRUDCacheRepo<ObjectType, IdType>(
|
||||||
protected val parentRepo: CRUDRepo<ObjectType, IdType, InputValueType>,
|
protected val parentRepo: ReadCRUDRepo<ObjectType, IdType>,
|
||||||
protected val kvCache: KVCache<IdType, ObjectType>,
|
protected val kvCache: KVCache<IdType, ObjectType>,
|
||||||
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
|
|
||||||
protected val idGetter: (ObjectType) -> IdType
|
protected val idGetter: (ObjectType) -> IdType
|
||||||
) : CRUDRepo<ObjectType, IdType, InputValueType> by parentRepo {
|
) : ReadCRUDRepo<ObjectType, IdType> by parentRepo {
|
||||||
protected val onNewJob = parentRepo.newObjectsFlow.onEach { kvCache.set(idGetter(it), it) }.launchIn(scope)
|
|
||||||
protected val onUpdatedJob = parentRepo.updatedObjectsFlow.onEach { kvCache.set(idGetter(it), it) }.launchIn(scope)
|
|
||||||
protected val onRemoveJob = parentRepo.deletedObjectsIdsFlow.onEach { kvCache.unset(it) }.launchIn(scope)
|
|
||||||
|
|
||||||
override suspend fun getById(id: IdType): ObjectType? = kvCache.get(id) ?: (parentRepo.getById(id) ?.also {
|
override suspend fun getById(id: IdType): ObjectType? = kvCache.get(id) ?: (parentRepo.getById(id) ?.also {
|
||||||
kvCache.set(id, it)
|
kvCache.set(id, it)
|
||||||
})
|
})
|
||||||
|
|
||||||
override suspend fun contains(id: IdType): Boolean = kvCache.contains(id) || parentRepo.contains(id)
|
override suspend fun contains(id: IdType): Boolean = kvCache.contains(id) || parentRepo.contains(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open class CRUDCacheRepo<ObjectType, IdType, InputValueType>(
|
||||||
|
parentRepo: CRUDRepo<ObjectType, IdType, InputValueType>,
|
||||||
|
kvCache: KVCache<IdType, ObjectType>,
|
||||||
|
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
|
||||||
|
idGetter: (ObjectType) -> IdType
|
||||||
|
) : ReadCRUDCacheRepo<ObjectType, IdType>(
|
||||||
|
parentRepo,
|
||||||
|
kvCache,
|
||||||
|
idGetter
|
||||||
|
), CRUDRepo<ObjectType, IdType, InputValueType>, WriteCRUDRepo<ObjectType, IdType, InputValueType> by parentRepo {
|
||||||
|
protected val onNewJob = parentRepo.newObjectsFlow.onEach { kvCache.set(idGetter(it), it) }.launchIn(scope)
|
||||||
|
protected val onUpdatedJob = parentRepo.updatedObjectsFlow.onEach { kvCache.set(idGetter(it), it) }.launchIn(scope)
|
||||||
|
protected val onRemoveJob = parentRepo.deletedObjectsIdsFlow.onEach { kvCache.unset(it) }.launchIn(scope)
|
||||||
|
}
|
||||||
|
@@ -7,14 +7,19 @@ import kotlinx.coroutines.flow.*
|
|||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
|
||||||
open class KeyValueCacheRepo<Key,Value>(
|
open class ReadKeyValueCacheRepo<Key,Value>(
|
||||||
protected val parentRepo: KeyValueRepo<Key, Value>,
|
protected val parentRepo: ReadKeyValueRepo<Key, Value>,
|
||||||
protected val kvCache: KVCache<Key, Value>,
|
protected val kvCache: KVCache<Key, Value>,
|
||||||
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
|
) : ReadKeyValueRepo<Key,Value> by parentRepo {
|
||||||
) : KeyValueRepo<Key,Value> by parentRepo {
|
|
||||||
protected val onNewJob = parentRepo.onNewValue.onEach { kvCache.set(it.first, it.second) }.launchIn(scope)
|
|
||||||
protected val onRemoveJob = parentRepo.onValueRemoved.onEach { kvCache.unset(it) }.launchIn(scope)
|
|
||||||
|
|
||||||
override suspend fun get(k: Key): Value? = kvCache.get(k) ?: parentRepo.get(k) ?.also { kvCache.set(k, it) }
|
override suspend fun get(k: Key): Value? = kvCache.get(k) ?: parentRepo.get(k) ?.also { kvCache.set(k, it) }
|
||||||
override suspend fun contains(key: Key): Boolean = kvCache.contains(key) || parentRepo.contains(key)
|
override suspend fun contains(key: Key): Boolean = kvCache.contains(key) || parentRepo.contains(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open class KeyValueCacheRepo<Key,Value>(
|
||||||
|
parentRepo: KeyValueRepo<Key, Value>,
|
||||||
|
kvCache: KVCache<Key, Value>,
|
||||||
|
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
|
||||||
|
) : ReadKeyValueCacheRepo<Key,Value>(parentRepo, kvCache), KeyValueRepo<Key,Value>, WriteKeyValueRepo<Key, Value> by parentRepo {
|
||||||
|
protected val onNewJob = parentRepo.onNewValue.onEach { kvCache.set(it.first, it.second) }.launchIn(scope)
|
||||||
|
protected val onRemoveJob = parentRepo.onValueRemoved.onEach { kvCache.unset(it) }.launchIn(scope)
|
||||||
|
}
|
||||||
|
@@ -11,15 +11,10 @@ import kotlinx.coroutines.flow.*
|
|||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
|
||||||
open class KeyValuesCacheRepo<Key,Value>(
|
open class ReadKeyValuesCacheRepo<Key,Value>(
|
||||||
protected val parentRepo: KeyValuesRepo<Key, Value>,
|
protected val parentRepo: ReadKeyValuesRepo<Key, Value>,
|
||||||
protected val kvCache: KVCache<Key, List<Value>>,
|
protected val kvCache: KVCache<Key, List<Value>>
|
||||||
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
|
) : ReadKeyValuesRepo<Key,Value> by parentRepo {
|
||||||
) : KeyValuesRepo<Key,Value> by parentRepo {
|
|
||||||
protected val onNewJob = parentRepo.onNewValue.onEach { kvCache.set(it.first, kvCache.get(it.first) ?.plus(it.second) ?: listOf(it.second)) }.launchIn(scope)
|
|
||||||
protected val onRemoveJob = parentRepo.onValueRemoved.onEach { kvCache.set(it.first, kvCache.get(it.first) ?.minus(it.second) ?: return@onEach) }.launchIn(scope)
|
|
||||||
protected val onDataClearedJob = parentRepo.onDataCleared.onEach { kvCache.unset(it) }.launchIn(scope)
|
|
||||||
|
|
||||||
override suspend fun get(k: Key, pagination: Pagination, reversed: Boolean): PaginationResult<Value> {
|
override suspend fun get(k: Key, pagination: Pagination, reversed: Boolean): PaginationResult<Value> {
|
||||||
return kvCache.get(k) ?.paginate(
|
return kvCache.get(k) ?.paginate(
|
||||||
pagination.let { if (reversed) it.reverse(count(k)) else it }
|
pagination.let { if (reversed) it.reverse(count(k)) else it }
|
||||||
@@ -35,3 +30,13 @@ open class KeyValuesCacheRepo<Key,Value>(
|
|||||||
override suspend fun contains(k: Key, v: Value): Boolean = kvCache.get(k) ?.contains(v) ?: parentRepo.contains(k, v)
|
override suspend fun contains(k: Key, v: Value): Boolean = kvCache.get(k) ?.contains(v) ?: parentRepo.contains(k, v)
|
||||||
override suspend fun contains(k: Key): Boolean = kvCache.contains(k) || parentRepo.contains(k)
|
override suspend fun contains(k: Key): Boolean = kvCache.contains(k) || parentRepo.contains(k)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open class KeyValuesCacheRepo<Key,Value>(
|
||||||
|
parentRepo: KeyValuesRepo<Key, Value>,
|
||||||
|
kvCache: KVCache<Key, List<Value>>,
|
||||||
|
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
|
||||||
|
) : ReadKeyValuesCacheRepo<Key,Value>(parentRepo, kvCache), KeyValuesRepo<Key,Value>, WriteKeyValuesRepo<Key,Value> by parentRepo {
|
||||||
|
protected val onNewJob = parentRepo.onNewValue.onEach { kvCache.set(it.first, kvCache.get(it.first) ?.plus(it.second) ?: listOf(it.second)) }.launchIn(scope)
|
||||||
|
protected val onRemoveJob = parentRepo.onValueRemoved.onEach { kvCache.set(it.first, kvCache.get(it.first) ?.minus(it.second) ?: return@onEach) }.launchIn(scope)
|
||||||
|
protected val onDataClearedJob = parentRepo.onDataCleared.onEach { kvCache.unset(it) }.launchIn(scope)
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user