Compare commits

...

51 Commits
0.4.0 ... 0.4.8

Author SHA1 Message Date
e38094df58 change order in iterable to skip changing of pagination on no left elements error 2020-12-01 15:48:37 +06:00
c25e3f5867 update versions 2020-12-01 15:38:44 +06:00
f78e81d175 PaginatedIterable 2020-12-01 15:34:12 +06:00
3837ae237d start 0.4.8 2020-12-01 15:11:18 +06:00
2b6ef8b4ff Merge pull request #24 from InsanusMokrassar/0.4.7
0.4.7
2020-11-27 19:30:40 +06:00
6cc0eefb3e upfix of default in includeWebsocketHandling 2020-11-27 17:53:29 +06:00
ab11e28bf7 UnifiedRouter 2020-11-27 14:35:00 +06:00
26d5f5a5f5 UnifiedRequester 2020-11-27 13:30:02 +06:00
74ae91cba6 start 0.4.7 2020-11-27 11:57:40 +06:00
70509c7edd update wrapper 2020-11-26 22:50:44 +06:00
5a69bd6c63 Merge pull request #23 from InsanusMokrassar/0.4.6
0.4.6
2020-11-26 22:50:08 +06:00
091bb1394f Warning annotation 2020-11-26 18:01:12 +06:00
b82c3864a0 typealiases for repos 2020-11-26 15:32:00 +06:00
49ee38a936 FileStandardKeyValueRepo 2020-11-26 15:03:40 +06:00
c201866c51 FlowCollector#invoke 2020-11-26 15:02:30 +06:00
023f841fb5 Pagination#isFirstPage 2020-11-26 15:01:45 +06:00
76102e9ab3 start 0.4.6 2020-11-26 15:00:32 +06:00
b2fc5e2a4d add query params docs 2020-11-25 11:09:54 +06:00
55aacb8753 update publish script 2020-11-23 22:46:40 +06:00
8702846216 Merge pull request #22 from InsanusMokrassar/0.4.5
0.4.5
2020-11-23 18:48:57 +06:00
3347a6d189 Update build.gradle 2020-11-23 18:46:52 +06:00
47b3b42949 replaces 2020-11-23 18:32:14 +06:00
e985631621 add recyclerview.alerts project 2020-11-23 18:10:53 +06:00
e15034bfa4 add tools to android 2020-11-23 17:39:17 +06:00
e5dbcd25dd remove kotlin android extensions 2020-11-23 17:27:52 +06:00
3714c02c12 add Android.Alerts project 2020-11-23 17:24:02 +06:00
fa636b4146 start 0.4.5 2020-11-23 17:15:21 +06:00
3d825aebc3 Update LeftItems.kt 2020-11-23 01:18:30 +06:00
629884a396 update left items 2020-11-23 00:36:37 +06:00
e7a0fa4e8f Merge pull request #21 from InsanusMokrassar/0.4.4
0.4.4
2020-11-22 23:47:04 +06:00
1a41f37a9d update changelog 2020-11-22 23:42:01 +06:00
07a65e0bb5 fixes 2020-11-22 23:19:16 +06:00
615f7f99c3 one more time update mechanism of sql transactions in android 2020-11-22 22:00:50 +06:00
3de5558ed4 new mechanism of transactions 2020-11-22 21:54:51 +06:00
6bbe3a271f update defaults in VersionsRepo 2020-11-22 19:27:53 +06:00
8bee29f683 update version of klock and remove gradle bintray plugin 2020-11-22 19:12:10 +06:00
c40f0fdcb9 VersionsRepo 2020-11-22 19:07:37 +06:00
30d1453a12 start 0.4.4 2020-11-22 17:49:39 +06:00
75fa88b00d Merge pull request #20 from InsanusMokrassar/0.4.3
0.4.3
2020-11-20 13:28:44 +06:00
0f817ad212 update kotlin 2020-11-20 13:10:40 +06:00
d7b46ae0d4 getSp and getDp 2020-11-20 10:23:40 +06:00
b6be14ecca start 0.4.3 2020-11-20 10:20:35 +06:00
33dbfc6f69 Merge pull request #19 from InsanusMokrassar/0.4.2
0.4.2
2020-11-18 20:32:42 +06:00
fcacdcd544 set method in realizations 2020-11-18 20:27:34 +06:00
dd2fc5a86f start 0.4.2 2020-11-18 20:26:08 +06:00
0f8a6f6bde update publishing files 2020-11-18 18:22:19 +06:00
1efd94181d Merge pull request #18 from InsanusMokrassar/0.4.1
0.4.1
2020-11-17 00:51:25 +06:00
71ff0232aa fix keyvalue exposed repo initiation 2020-11-16 20:05:22 +06:00
63921cd984 fix of shared flow size in ExposedKeyValueRepo 2020-11-16 19:48:28 +06:00
051e03bed3 start 0.4.1 2020-11-16 19:46:50 +06:00
a051394f4f Merge pull request #17 from InsanusMokrassar/0.4.0
0.4.0
2020-11-14 18:04:59 +06:00
81 changed files with 2040 additions and 533 deletions

View File

@@ -22,5 +22,6 @@ jobs:
include: include:
- stage: build - stage: build
script: ./gradlew build -s -x jvmTest -x jsIrTest -x jsIrBrowserTest -x jsIrNodeTest -x jsLegacyTest -x jsLegacyBrowserTest -x jsLegacyNodeTest script: ./gradlew build -s -x jvmTest -x jsIrTest -x jsIrBrowserTest -x jsIrNodeTest -x jsLegacyTest -x jsLegacyBrowserTest -x jsLegacyNodeTest
- state: test # Tests are temporarily disabled on public travis due to the problems of launching
script: ./gradlew allTests # - state: test
# script: ./gradlew allTests

View File

@@ -1,5 +1,93 @@
# Changelog # Changelog
## 0.4.8
* `Versions`:
* `Coroutines`: `1.4.1` -> `1.4.2`
* `UUID`: `0.2.2` -> `0.2.3`
* `Pagination`
* Add `PaginatedIterable` and `PaginatedIterator`
## 0.4.7
* `Ktor`
* `Client`
* New class `UnifiedRequester`
* `Server`
* New class `UnifiedRouter`
* `Repos`
* `Ktor`
* `Client`
* Rewriting of all clients on new `UnifiedRequester`
* `Server`
* Rewriting of all clients on new `UnifiedRouter`
## 0.4.6
* `Common`
* New annotation `Warning` has been added
* `Pagination`
* `Common`
* `Pagination` got new extension: `Pagination#isFirstPage`
* `Coroutines`:
* New extension `FlowCollector#invoke` has been added
* `Repos`
* `Common`
* `JVM` (and `Android` since `Android API 26`):
* `FileStandardKeyValueRepo` has been added
* Add several `typealias`es for each type of repos
## 0.4.5
* `Android`
* `Alerts`
* `Common`
* Project has been created
* `RecyclerView`
* Project has been created
* `Common`
* Annotation `PreviewFeature` has been added
* `Android`
* Added tools to work with visibility in more comfortable way
* Added tools to work with disabled/enabled state in more comfortable way
* Added tools to work with expanded/collapsed state in more comfortable way (in preview mode)
## 0.4.4
* `Versions`:
* `Klock`: `1.12.1` -> `2.0.0`
* `Commons`:
* Update left items functionality to include work with `GridLayoutManager`
* `Repos`:
* Add interface `VersionsRepo`
* Add default realization of `VersionsRepo` named `StandardVersionsRepo` which use `StandardVersionsRepoProxy`
to get access to some end-store
* Add default realization of `StandardVersionsRepoProxy` based on `KeyValue` repos
* Add realizations of `StandardVersionsRepoProxy` for exposed and android (`SQL` and `SharedPreferences`)
* `Commons`:
* In Android fully reworked transactions functions
* Now `DatabaseCoroutineContext` is a shortcut for `Dispatchers.IO`
## 0.4.3
* `Versions`:
* `Kotlin`: `1.4.10` -> `1.4.20`
* `Common`:
* Two new extensions for Android:
* `Resources#getSp`
* `Resources#getDp`
## 0.4.2
* `Repos`:
* Add `WriteOneToManyKeyValueRepo#set` function and extensions
## 0.4.1
* `Repos`:
* Fixed error in `ExposedKeyValueRepo` related to negative size of shared flow
* Fixed error in `ExposedKeyValueRepo` related to avoiding of table initiation
## 0.4.0 ## 0.4.0
* `Repos`: * `Repos`:
@@ -224,4 +312,4 @@ All deprecations has been removed
## 0.1.0 ## 0.1.0
Inited :) Inited :)

View File

@@ -0,0 +1,17 @@
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library"
}
apply from: "$mppAndroidProjectPresetPath"
kotlin {
sourceSets {
androidMain {
dependencies {
api "androidx.appcompat:appcompat-resources:$appcompat_version"
}
}
}
}

View File

@@ -0,0 +1 @@
<manifest package="dev.inmo.micro_utils.android.alerts.common"/>

View File

@@ -0,0 +1,55 @@
@file:Suppress("NOTHING_TO_INLINE", "unused")
package dev.inmo.micro_utils.android.alerts.common
import android.app.AlertDialog
import android.content.Context
import android.content.DialogInterface
typealias AlertDialogCallback = (DialogInterface) -> Unit
inline fun Context.createAlertDialogTemplate(
title: String? = null,
positivePair: Pair<String, AlertDialogCallback?>? = null,
neutralPair: Pair<String, AlertDialogCallback?>? = null,
negativePair: Pair<String, AlertDialogCallback?>? = null
): AlertDialog.Builder {
val builder = AlertDialog.Builder(this)
title ?.let {
builder.setTitle(title)
}
positivePair ?. let {
builder.setPositiveButton(it.first) { di, _ -> it.second ?. invoke(di) }
}
negativePair ?. let {
builder.setNegativeButton(it.first) { di, _ -> it.second ?. invoke(di) }
}
neutralPair ?. let {
builder.setNeutralButton(it.first) { di, _ -> it.second ?. invoke(di) }
}
return builder
}
inline fun Context.createAlertDialogTemplateWithResources(
title: Int? = null,
positivePair: Pair<Int, AlertDialogCallback?>? = null,
neutralPair: Pair<Int, AlertDialogCallback?>? = null,
negativePair: Pair<Int, AlertDialogCallback?>? = null
): AlertDialog.Builder = createAlertDialogTemplate(
title ?.let { getString(it) },
positivePair ?.let { getString(it.first) to it.second },
neutralPair ?.let { getString(it.first) to it.second },
negativePair ?.let { getString(it.first) to it.second }
)
inline fun AlertDialog.setDismissChecker(noinline checker: () -> Boolean) : AlertDialog {
setOnDismissListener {
if (!checker()) {
show()
}
}
return this
}

View File

@@ -0,0 +1,38 @@
@file:Suppress("NOTHING_TO_INLINE", "unused")
package dev.inmo.micro_utils.android.alerts.common
import android.app.AlertDialog
import android.content.Context
import android.view.View
inline fun <T: View> Context.createCustomViewAlertDialog(
title: String? = null,
positivePair: Pair<String, AlertDialogCallback?>? = null,
neutralPair: Pair<String, AlertDialogCallback?>? = null,
negativePair: Pair<String, AlertDialogCallback?>? = null,
show: Boolean = true,
viewCreator: (Context) -> T
): AlertDialog = createAlertDialogTemplate(
title, positivePair, neutralPair, negativePair
).apply {
setView(viewCreator(this@createCustomViewAlertDialog))
}.create().apply {
if (show) show()
}
inline fun <T: View> Context.createCustomViewAlertDialogWithResources(
title: Int? = null,
positivePair: Pair<Int, AlertDialogCallback?>? = null,
neutralPair: Pair<Int, AlertDialogCallback?>? = null,
negativePair: Pair<Int, AlertDialogCallback?>? = null,
show: Boolean = true,
viewCreator: (Context) -> T
): AlertDialog = createCustomViewAlertDialog(
title ?.let { getString(it) },
positivePair ?.let { getString(it.first) to it.second },
neutralPair ?.let { getString(it.first) to it.second },
negativePair ?.let { getString(it.first) to it.second },
show,
viewCreator
)

View File

@@ -0,0 +1,45 @@
@file:Suppress("NOTHING_TO_INLINE", "unused")
package dev.inmo.micro_utils.android.alerts.common
import android.app.AlertDialog
import android.content.Context
import androidx.annotation.StringRes
inline fun Context.createSimpleTextAlertDialog(
text: String,
title: String? = null,
positivePair: Pair<String, AlertDialogCallback?>? = null,
neutralPair: Pair<String, AlertDialogCallback?>? = null,
negativePair: Pair<String, AlertDialogCallback?>? = null,
show: Boolean = true
): AlertDialog = createAlertDialogTemplate(
title,
positivePair,
neutralPair,
negativePair
).apply {
setMessage(text)
}.create().apply {
if (show) {
show()
}
}
inline fun Context.createSimpleTextAlertDialog(
@StringRes
text: Int,
@StringRes
title: Int? = null,
positivePair: Pair<Int, AlertDialogCallback?>? = null,
neutralPair: Pair<Int, AlertDialogCallback?>? = null,
negativePair: Pair<Int, AlertDialogCallback?>? = null,
show: Boolean = true
): AlertDialog = createSimpleTextAlertDialog(
getString(text),
title ?.let { getString(it) },
positivePair ?.let { getString(it.first) to it.second },
neutralPair ?.let { getString(it.first) to it.second },
negativePair ?.let { getString(it.first) to it.second },
show
)

View File

@@ -0,0 +1,18 @@
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library"
}
apply from: "$mppAndroidProjectPresetPath"
kotlin {
sourceSets {
commonMain {
dependencies {
api internalProject("micro_utils.android.alerts.common")
api internalProject("micro_utils.android.recyclerview")
}
}
}
}

View File

@@ -0,0 +1 @@
<manifest package="dev.inmo.micro_utils.android.alerts.recyclerview"/>

View File

@@ -0,0 +1,65 @@
@file:Suppress("unused")
package dev.inmo.micro_utils.android.alerts.recyclerview
import android.app.AlertDialog
import android.content.Context
import android.content.DialogInterface
import android.view.ViewGroup
import android.widget.TextView
import dev.inmo.micro_utils.android.alerts.common.AlertDialogCallback
import dev.inmo.micro_utils.android.recyclerview.*
data class AlertAction(
val title: String,
val callback: (DialogInterface) -> Unit
)
private class ActionViewHolder(
container: ViewGroup, dialogInterfaceGetter: () -> DialogInterface
) : AbstractStandardViewHolder<AlertAction>(container, android.R.layout.simple_list_item_1) {
private lateinit var action: AlertAction
private val textView: TextView
get() = itemView.findViewById(android.R.id.text1)
init {
itemView.setOnClickListener {
action.callback(dialogInterfaceGetter())
}
}
override fun onBind(item: AlertAction) {
action = item
textView.text = item.title
}
}
private class ActionsRecyclerViewAdapter(
data: List<AlertAction>,
private val dialogInterfaceGetter: () -> DialogInterface
) : RecyclerViewAdapter<AlertAction>(data) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AbstractViewHolder<AlertAction> = ActionViewHolder(
parent, dialogInterfaceGetter
)
}
fun Context.createActionsAlertDialog(
actions: List<AlertAction>,
title: Int? = null,
positivePair: Pair<Int, AlertDialogCallback?>? = null,
neutralPair: Pair<Int, AlertDialogCallback?>? = null,
negativePair: Pair<Int, AlertDialogCallback?>? = null,
show: Boolean = true
): AlertDialog {
lateinit var dialogInterface: DialogInterface
return createRecyclerViewDialog(
title, positivePair, neutralPair, negativePair, show
) {
ActionsRecyclerViewAdapter(
actions
) {
dialogInterface
}
}.also { dialogInterface = it }
}

View File

@@ -0,0 +1,43 @@
package dev.inmo.micro_utils.android.alerts.recyclerview
import android.app.AlertDialog
import android.content.Context
import android.widget.LinearLayout
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import dev.inmo.micro_utils.android.alerts.common.AlertDialogCallback
import dev.inmo.micro_utils.android.alerts.common.createCustomViewAlertDialogWithResources
fun Context.createRecyclerViewDialog(
title: Int? = null,
positivePair: Pair<Int, AlertDialogCallback?>? = null,
neutralPair: Pair<Int, AlertDialogCallback?>? = null,
negativePair: Pair<Int, AlertDialogCallback?>? = null,
show: Boolean = true,
layoutManager: RecyclerView.LayoutManager = LinearLayoutManager(this),
marginOfRecyclerView: Int = 8, // dp
recyclerViewSetUp: RecyclerView.() -> Unit = {},
adapterFactory: () -> RecyclerView.Adapter<*>
): AlertDialog {
val recyclerView = RecyclerView(this).apply {
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
).apply {
setMargins(marginOfRecyclerView, marginOfRecyclerView, marginOfRecyclerView, marginOfRecyclerView)
}
this.layoutManager = layoutManager
adapter = adapterFactory()
recyclerViewSetUp()
}
return createCustomViewAlertDialogWithResources(
title,
positivePair,
neutralPair,
negativePair,
show
) {
recyclerView
}
}

View File

@@ -2,7 +2,6 @@ plugins {
id "org.jetbrains.kotlin.multiplatform" id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization" id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library" id "com.android.library"
id "kotlin-android-extensions"
} }
apply from: "$mppAndroidProjectPresetPath" apply from: "$mppAndroidProjectPresetPath"

View File

@@ -1,14 +1,20 @@
package dev.inmo.micro_utils.android.recyclerview package dev.inmo.micro_utils.android.recyclerview
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.*
import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
@Suppress("NOTHING_TO_INLINE")
private inline fun RecyclerView.LayoutManager.findLastVisibleItemPositionGetter(): (() -> Int)? = when (this) {
is LinearLayoutManager -> ::findLastVisibleItemPosition
is GridLayoutManager -> ::findLastVisibleItemPosition
else -> null
}
fun RecyclerView.lastVisibleItemFlow( fun RecyclerView.lastVisibleItemFlow(
completingScope: CoroutineScope completingScope: CoroutineScope
): Flow<Int> { ): Flow<Int> {
val lastVisibleElementFun: () -> Int = (layoutManager as? LinearLayoutManager) ?.let { it::findLastVisibleItemPosition } ?: error("Currently supported only linear layout manager") val lastVisibleElementFun: () -> Int = layoutManager ?.findLastVisibleItemPositionGetter() ?: error("Currently supported only linear and grid layout manager")
val lastVisibleFlow = MutableStateFlow(lastVisibleElementFun()) val lastVisibleFlow = MutableStateFlow(lastVisibleElementFun())
addOnScrollListener( addOnScrollListener(
object : RecyclerView.OnScrollListener() { object : RecyclerView.OnScrollListener() {

View File

@@ -8,10 +8,9 @@ buildscript {
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:4.0.0' classpath 'com.android.tools.build:gradle:4.0.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:$gradle_bintray_plugin_version"
classpath "com.getkeepsafe.dexcount:dexcount-gradle-plugin:$dexcount_version" classpath "com.getkeepsafe.dexcount:dexcount-gradle-plugin:$dexcount_version"
classpath "com.github.breadmoirai:github-release:$github_release_plugin_version" classpath "com.github.breadmoirai:github-release:$github_release_plugin_version"
classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokka_version" classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokka_version"

View File

@@ -2,7 +2,6 @@ plugins {
id "org.jetbrains.kotlin.multiplatform" id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization" id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library" id "com.android.library"
id "kotlin-android-extensions"
} }
apply from: "$mppProjectWithSerializationPresetPath" apply from: "$mppProjectWithSerializationPresetPath"

View File

@@ -0,0 +1,37 @@
package dev.inmo.micro_utils.common
@RequiresOptIn(
"It is possible, that behaviour of this thing will be changed or removed in future releases",
RequiresOptIn.Level.WARNING
)
@Target(
AnnotationTarget.CLASS,
AnnotationTarget.CONSTRUCTOR,
AnnotationTarget.FIELD,
AnnotationTarget.PROPERTY,
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER,
AnnotationTarget.FUNCTION,
AnnotationTarget.TYPE,
AnnotationTarget.TYPEALIAS,
AnnotationTarget.TYPE_PARAMETER
)
annotation class PreviewFeature
@RequiresOptIn(
"This thing is marked as warned. See message of warn to get more info",
RequiresOptIn.Level.WARNING
)
@Target(
AnnotationTarget.CLASS,
AnnotationTarget.CONSTRUCTOR,
AnnotationTarget.FIELD,
AnnotationTarget.PROPERTY,
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER,
AnnotationTarget.FUNCTION,
AnnotationTarget.TYPE,
AnnotationTarget.TYPEALIAS,
AnnotationTarget.TYPE_PARAMETER
)
annotation class Warning(val message: String)

View File

@@ -0,0 +1,13 @@
@file:Suppress("NOTHING_TO_INLINE")
package dev.inmo.micro_utils.common
import android.content.res.Resources
inline fun Resources.getSp(
resId: Int
) = getDimension(resId) / displayMetrics.scaledDensity
inline fun Resources.getDp(
resId: Int
) = getDimension(resId) * displayMetrics.density

View File

@@ -0,0 +1,76 @@
package dev.inmo.micro_utils.common
import android.view.View
import android.view.ViewGroup
import android.view.animation.Animation
import android.view.animation.Transformation
@PreviewFeature
fun View.expand(
duration: Long = 500,
targetWidth: Int = ViewGroup.LayoutParams.MATCH_PARENT,
targetHeight: Int = ViewGroup.LayoutParams.WRAP_CONTENT
) {
measure(targetWidth, targetHeight)
val measuredHeight: Int = measuredHeight
layoutParams.height = 0
visibility = View.VISIBLE
val a: Animation = object : Animation() {
override fun applyTransformation(interpolatedTime: Float, t: Transformation?) {
super.applyTransformation(interpolatedTime, t)
layoutParams.height = if (interpolatedTime == 1f) targetHeight else (measuredHeight * interpolatedTime).toInt()
requestLayout()
}
override fun willChangeBounds(): Boolean {
return true
}
}
a.duration = duration
startAnimation(a)
}
@PreviewFeature
fun View.collapse(duration: Long = 500) {
val initialHeight: Int = measuredHeight
val a: Animation = object : Animation() {
override fun applyTransformation(interpolatedTime: Float, t: Transformation?) {
if (interpolatedTime == 1f) {
visibility = View.GONE
} else {
layoutParams.height = initialHeight - (initialHeight * interpolatedTime).toInt()
requestLayout()
}
}
override fun willChangeBounds(): Boolean {
return true
}
}
a.duration = duration
startAnimation(a)
}
@PreviewFeature
inline val View.isCollapsed
get() = visibility == View.GONE
@PreviewFeature
inline val View.isExpanded
get() = !isCollapsed
/**
* @return true in case of expanding
*/
@PreviewFeature
fun View.toggleExpandState(duration: Long = 500): Boolean = if (isCollapsed) {
expand(duration)
true
} else {
collapse(duration)
false
}

View File

@@ -0,0 +1,34 @@
@file:Suppress("NOTHING_TO_INLINE", "unused")
package dev.inmo.micro_utils.common
import android.view.View
import android.view.ViewGroup
inline val View.enabled
get() = isEnabled
inline val View.disabled
get() = !enabled
fun View.disable() {
if (this is ViewGroup) {
(0 until childCount).forEach { getChildAt(it).disable() }
}
isEnabled = false
}
fun View.enable() {
if (this is ViewGroup) {
(0 until childCount).forEach { getChildAt(it).enable() }
}
isEnabled = true
}
fun View.toggleEnabledState(enabled: Boolean) {
if (enabled) {
enable()
} else {
disable()
}
}

View File

@@ -0,0 +1,35 @@
@file:Suppress("NOTHING_TO_INLINE", "unused")
package dev.inmo.micro_utils.common
import android.view.View
inline val View.gone
get() = visibility == View.GONE
inline fun View.gone() {
visibility = View.GONE
}
inline val View.hidden
get() = visibility == View.INVISIBLE
inline fun View.hide() {
visibility = View.INVISIBLE
}
inline val View.shown
get() = visibility == View.VISIBLE
inline fun View.show() {
visibility = View.VISIBLE
}
fun View.toggleVisibility(goneOnHide: Boolean = true) {
if (isShown) {
if (goneOnHide) {
gone()
} else {
hide()
}
} else {
show()
}
}

View File

@@ -2,7 +2,6 @@ plugins {
id "org.jetbrains.kotlin.multiplatform" id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization" id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library" id "com.android.library"
id "kotlin-android-extensions"
} }
apply from: "$mppProjectWithSerializationPresetPath" apply from: "$mppProjectWithSerializationPresetPath"

View File

@@ -0,0 +1,5 @@
package dev.inmo.micro_utils.coroutines
import kotlinx.coroutines.flow.FlowCollector
suspend inline operator fun <T> FlowCollector<T>.invoke(value: T) = emit(value)

View File

@@ -2,7 +2,6 @@ plugins {
id "org.jetbrains.kotlin.multiplatform" id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization" id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library" id "com.android.library"
id "kotlin-android-extensions"
id "org.jetbrains.dokka" id "org.jetbrains.dokka"
} }

View File

@@ -5,26 +5,25 @@ kotlin.incremental=true
kotlin.incremental.js=true kotlin.incremental.js=true
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true
org.gradle.jvmargs=-Xmx3072m
kotlin_version=1.4.10 kotlin_version=1.4.20
kotlin_coroutines_version=1.4.1 kotlin_coroutines_version=1.4.2
kotlin_serialisation_core_version=1.0.1 kotlin_serialisation_core_version=1.0.1
kotlin_exposed_version=0.28.1 kotlin_exposed_version=0.28.1
ktor_version=1.4.2 ktor_version=1.4.2
klockVersion=1.12.1 klockVersion=2.0.0
gradle_bintray_plugin_version=1.8.5
github_release_plugin_version=2.2.12 github_release_plugin_version=2.2.12
uuidVersion=0.2.2 uuidVersion=0.2.3
# ANDROID # ANDROID
core_ktx_version=1.3.2 core_ktx_version=1.3.2
androidx_recycler_version=1.1.0 androidx_recycler_version=1.1.0
appcompat_version=1.2.0
android_minSdkVersion=19 android_minSdkVersion=19
android_compileSdkVersion=30 android_compileSdkVersion=30
@@ -36,10 +35,10 @@ espresso_core=3.3.0
# Dokka # Dokka
dokka_version=1.4.0 dokka_version=1.4.10.2
# Project data # Project data
group=dev.inmo group=dev.inmo
version=0.4.0 version=0.4.8
android_code_version=4 android_code_version=12

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View File

@@ -2,7 +2,6 @@ plugins {
id "org.jetbrains.kotlin.multiplatform" id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization" id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library" id "com.android.library"
id "kotlin-android-extensions"
} }
apply from: "$mppProjectWithSerializationPresetPath" apply from: "$mppProjectWithSerializationPresetPath"

View File

@@ -64,10 +64,11 @@ inline fun <T> HttpClient.createStandardWebsocketFlow(
inline fun <T> HttpClient.createStandardWebsocketFlow( inline fun <T> HttpClient.createStandardWebsocketFlow(
url: String, url: String,
crossinline checkReconnection: (Throwable?) -> Boolean = { true }, crossinline checkReconnection: (Throwable?) -> Boolean = { true },
deserializer: DeserializationStrategy<T> deserializer: DeserializationStrategy<T>,
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
) = createStandardWebsocketFlow( ) = createStandardWebsocketFlow(
url, url,
checkReconnection checkReconnection
) { ) {
standardKtorSerialFormat.decodeDefault(deserializer, it) serialFormat.decodeDefault(deserializer, it)
} }

View File

@@ -4,32 +4,61 @@ import dev.inmo.micro_utils.ktor.common.*
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
import io.ktor.client.request.get import io.ktor.client.request.get
import io.ktor.client.request.post import io.ktor.client.request.post
import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.*
import kotlinx.serialization.SerializationStrategy
typealias BodyPair<T> = Pair<SerializationStrategy<T>, T> typealias BodyPair<T> = Pair<SerializationStrategy<T>, T>
class UnifiedRequester(
private val client: HttpClient = HttpClient(),
private val serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
) {
suspend fun <ResultType> uniget(
url: String,
resultDeserializer: DeserializationStrategy<ResultType>
): ResultType = client.get<StandardKtorSerialInputData>(
url
).let {
serialFormat.decodeDefault(resultDeserializer, it)
}
fun <T> encodeUrlQueryValue(
serializationStrategy: SerializationStrategy<T>,
value: T
) = serialFormat.encodeHex(
serializationStrategy,
value
)
suspend fun <BodyType, ResultType> unipost(
url: String,
bodyInfo: BodyPair<BodyType>,
resultDeserializer: DeserializationStrategy<ResultType>
) = client.post<StandardKtorSerialInputData>(url) {
body = serialFormat.encodeDefault(bodyInfo.first, bodyInfo.second)
}.let {
serialFormat.decodeDefault(resultDeserializer, it)
}
fun <T> createStandardWebsocketFlow(
url: String,
checkReconnection: (Throwable?) -> Boolean = { true },
deserializer: DeserializationStrategy<T>
) = client.createStandardWebsocketFlow(url, checkReconnection, deserializer, serialFormat)
}
val defaultRequester = UnifiedRequester()
suspend fun <ResultType> HttpClient.uniget( suspend fun <ResultType> HttpClient.uniget(
url: String, url: String,
resultDeserializer: DeserializationStrategy<ResultType> resultDeserializer: DeserializationStrategy<ResultType>
) = get<StandardKtorSerialInputData>( ) = defaultRequester.uniget(url, resultDeserializer)
url
).let {
standardKtorSerialFormat.decodeDefault(resultDeserializer, it)
}
fun <T> SerializationStrategy<T>.encodeUrlQueryValue(value: T) = standardKtorSerialFormat.encodeHex( fun <T> SerializationStrategy<T>.encodeUrlQueryValue(value: T) = defaultRequester.encodeUrlQueryValue(this, value)
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>
) = post<StandardKtorSerialInputData>(url) { ) = defaultRequester.unipost(url, bodyInfo, resultDeserializer)
body = standardKtorSerialFormat.encodeDefault(bodyInfo.first, bodyInfo.second)
}.let {
standardKtorSerialFormat.decodeDefault(resultDeserializer, it)
}

View File

@@ -2,7 +2,6 @@ plugins {
id "org.jetbrains.kotlin.multiplatform" id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization" id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library" id "com.android.library"
id "kotlin-android-extensions"
} }
apply from: "$mppProjectWithSerializationPresetPath" apply from: "$mppProjectWithSerializationPresetPath"

View File

@@ -3,16 +3,28 @@ package dev.inmo.micro_utils.ktor.common
typealias QueryParam = Pair<String, String?> typealias QueryParam = Pair<String, String?>
typealias QueryParams = Map<String, String?> typealias QueryParams = Map<String, String?>
/**
* Create query part which includes key=value pairs separated with &
*/
val QueryParams.asUrlQuery: String val QueryParams.asUrlQuery: String
get() = keys.joinToString("&") { "${it}${get(it) ?.let { value -> "=$value" }}" } get() = keys.joinToString("&") { "${it}${get(it) ?.let { value -> "=$value" }}" }
/**
* Create query part which includes key=value pairs separated with &
*/
val List<QueryParam>.asUrlQuery: String val List<QueryParam>.asUrlQuery: String
get() = joinToString("&") { (key, value) -> "${key}${value ?.let { _ -> "=$value" }}" } get() = joinToString("&") { (key, value) -> "${key}${value ?.let { _ -> "=$value" }}" }
/**
* Create query part which includes key=value pairs separated with & and attach to receiver
*/
fun String.includeQueryParams( fun String.includeQueryParams(
queryParams: QueryParams queryParams: QueryParams
): String = "$this${if(queryParams.isNotEmpty()) "${if (contains("?")) "&" else "?"}${queryParams.asUrlQuery}" else ""}" ): String = "$this${if(queryParams.isNotEmpty()) "${if (contains("?")) "&" else "?"}${queryParams.asUrlQuery}" else ""}"
/**
* Create query part which includes key=value pairs separated with & and attach to receiver
*/
fun String.includeQueryParams( fun String.includeQueryParams(
queryParams: List<QueryParam> queryParams: List<QueryParam>
): String = "$this${if (contains("?")) "&" else "?"}${queryParams.asUrlQuery}" ): String = "$this${if (contains("?")) "&" else "?"}${queryParams.asUrlQuery}"

View File

@@ -33,10 +33,11 @@ fun <T> Route.includeWebsocketHandling(
fun <T> Route.includeWebsocketHandling( fun <T> Route.includeWebsocketHandling(
suburl: String, suburl: String,
flow: Flow<T>, flow: Flow<T>,
serializer: SerializationStrategy<T> serializer: SerializationStrategy<T>,
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
) = includeWebsocketHandling( ) = includeWebsocketHandling(
suburl, suburl,
flow flow
) { ) {
standardKtorSerialFormat.encodeDefault(serializer, it) serialFormat.encodeDefault(serializer, it)
} }

View File

@@ -3,12 +3,87 @@ package dev.inmo.micro_utils.ktor.server
import dev.inmo.micro_utils.coroutines.safely import dev.inmo.micro_utils.coroutines.safely
import dev.inmo.micro_utils.ktor.common.* import dev.inmo.micro_utils.ktor.common.*
import io.ktor.application.ApplicationCall import io.ktor.application.ApplicationCall
import io.ktor.application.call
import io.ktor.http.ContentType
import io.ktor.http.HttpStatusCode import io.ktor.http.HttpStatusCode
import io.ktor.request.receive import io.ktor.request.receive
import io.ktor.response.respond import io.ktor.response.respond
import io.ktor.response.respondBytes import io.ktor.response.respondBytes
import kotlinx.serialization.DeserializationStrategy import io.ktor.routing.Route
import kotlinx.serialization.SerializationStrategy import io.ktor.util.pipeline.PipelineContext
import kotlinx.coroutines.flow.Flow
import kotlinx.serialization.*
class UnifiedRouter(
private val serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat,
private val serialFormatContentType: ContentType = standardKtorSerialFormatContentType
) {
fun <T> Route.includeWebsocketHandling(
suburl: String,
flow: Flow<T>,
serializer: SerializationStrategy<T>
) = includeWebsocketHandling(suburl, flow, serializer, serialFormat)
suspend fun <T> PipelineContext<*, ApplicationCall>.unianswer(
answerSerializer: SerializationStrategy<T>,
answer: T
) {
call.respondBytes (
serialFormat.encodeDefault(answerSerializer, answer),
serialFormatContentType
)
}
suspend fun <T> PipelineContext<*, ApplicationCall>.uniload(
deserializer: DeserializationStrategy<T>
) = safely {
serialFormat.decodeDefault(
deserializer,
call.receive()
)
}
suspend fun PipelineContext<*, ApplicationCall>.getParameterOrSendError(
field: String
) = call.parameters[field].also {
if (it == null) {
call.respond(HttpStatusCode.BadRequest, "Request must contains $field")
}
}
fun PipelineContext<*, ApplicationCall>.getQueryParameter(
field: String
) = call.request.queryParameters[field]
suspend fun PipelineContext<*, ApplicationCall>.getQueryParameterOrSendError(
field: String
) = getQueryParameter(field).also {
if (it == null) {
call.respond(HttpStatusCode.BadRequest, "Request query parameters must contains $field")
}
}
fun <T> PipelineContext<*, ApplicationCall>.decodeUrlQueryValue(
field: String,
deserializer: DeserializationStrategy<T>
) = getQueryParameter(field) ?.let {
serialFormat.decodeHex(
deserializer,
it
)
}
suspend fun <T> PipelineContext<*, ApplicationCall>.decodeUrlQueryValueOrSendError(
field: String,
deserializer: DeserializationStrategy<T>
) = decodeUrlQueryValue(field, deserializer).also {
if (it == null) {
call.respond(HttpStatusCode.BadRequest, "Request query parameters must contains $field")
}
}
}
val defaultUnifiedRouter = UnifiedRouter()
suspend fun <T> ApplicationCall.unianswer( suspend fun <T> ApplicationCall.unianswer(
answerSerializer: SerializationStrategy<T>, answerSerializer: SerializationStrategy<T>,

View File

@@ -1,60 +0,0 @@
apply plugin: 'maven-publish'
task javadocsJar(type: Jar) {
classifier = 'javadoc'
}
afterEvaluate {
project.publishing.publications.all {
// rename artifacts
groupId "${project.group}"
if (it.name.contains('kotlinMultiplatform')) {
artifactId = "${project.name}"
} else {
artifactId = "${project.name}-$name"
}
}
}
publishing {
publications.all {
artifact javadocsJar
pom {
description = ""
name = "${project.name}"
url = "https://git.inmo.dev/InsanusMokrassar/MicroUtils_mirror"
scm {
developerConnection = "scm:git:[fetch=]ssh://git@git.inmo.dev:8322/InsanusMokrassar/MicroUtils_mirror.git[push=]ssh://git@git.inmo.dev:8322/InsanusMokrassar/MicroUtils_mirror.git"
url = "ssh://git@git.inmo.dev:8322/InsanusMokrassar/MicroUtils_mirror.git"
}
developers {
developer {
id = "InsanusMokrassar"
name = "Aleksei Ovsiannikov"
email = "ovsyannikov.alexey95@gmail.com"
}
developer {
id = "000Sanya"
name = "Syrov Aleksandr"
email = "000sanya.000sanya@gmail.com"
}
}
licenses {
license {
name = "Apache Software License 2.0"
url = "https://git.inmo.dev/InsanusMokrassar/MicroUtils_mirror/src/master/LICENSE"
}
}
}
}
}

View File

@@ -2,7 +2,6 @@ plugins {
id "org.jetbrains.kotlin.multiplatform" id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization" id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library" id "com.android.library"
id "kotlin-android-extensions"
} }
apply from: "$mppProjectWithSerializationPresetPath" apply from: "$mppProjectWithSerializationPresetPath"

View File

@@ -2,7 +2,6 @@ plugins {
id "org.jetbrains.kotlin.multiplatform" id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization" id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library" id "com.android.library"
id "kotlin-android-extensions"
} }
apply from: "$mppProjectWithSerializationPresetPath" apply from: "$mppProjectWithSerializationPresetPath"

View File

@@ -22,6 +22,12 @@ interface Pagination {
val size: Int val size: Int
} }
/**
* Logical shortcut for comparison that page is 0
*/
inline val Pagination.isFirstPage
get() = page == 0
/** /**
* First number in index of objects. It can be used as offset for databases or other data sources * First number in index of objects. It can be used as offset for databases or other data sources
*/ */

View File

@@ -0,0 +1,41 @@
package dev.inmo.micro_utils.pagination.utils
import dev.inmo.micro_utils.pagination.*
class PaginatedIterator<T>(
pageSize: Int,
private val countGetter: () -> Long,
private val paginationResultGetter: Pagination.() -> PaginationResult<T>
) : Iterator<T> {
private var pagination = FirstPagePagination(pageSize)
private val currentStack = mutableListOf<T>()
override fun hasNext(): Boolean = currentStack.isNotEmpty() || (countGetter() < pagination.lastIndexExclusive)
override fun next(): T {
if (currentStack.isEmpty()) {
val resultPagination = paginationResultGetter.invoke(pagination)
currentStack.addAll(resultPagination.results)
require(currentStack.isNotEmpty()) { "There is no elements left" }
pagination = resultPagination.nextPage()
}
return currentStack.removeFirst()
}
}
class PaginatedIterable<T>(
private val pageSize: Int,
private val countGetter: () -> Long,
private val paginationResultGetter: Pagination.() -> PaginationResult<T>
) : Iterable<T> {
override fun iterator(): Iterator<T> = PaginatedIterator(pageSize, countGetter, paginationResultGetter)
}
/**
* Will make iterable using incoming [countGetter] and [paginationResultGetter]
*/
@Suppress("NOTHING_TO_INLINE")
inline fun <T> makeIterable(
noinline countGetter: () -> Long,
pageSize: Int = defaultMediumPageSize,
noinline paginationResultGetter: Pagination.() -> PaginationResult<T>
): Iterable<T> = PaginatedIterable(pageSize, countGetter, paginationResultGetter)

View File

@@ -2,7 +2,6 @@ plugins {
id "org.jetbrains.kotlin.multiplatform" id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization" id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library" id "com.android.library"
id "kotlin-android-extensions"
} }
apply from: "$mppProjectWithSerializationPresetPath" apply from: "$mppProjectWithSerializationPresetPath"

View File

@@ -1 +1 @@
{"bintrayConfig":{"repo":"MicroUtils","packageName":"${project.name}","packageVcs":"https://github.com/InsanusMokrassar/MicroUtils","autoPublish":true,"overridePublish":true},"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://git.inmo.dev/InsanusMokrassar/MicroUtils_mirror/src/master/LICENSE"}],"mavenConfig":{"name":"${project.name}","description":"","url":"https://git.inmo.dev/InsanusMokrassar/MicroUtils_mirror","vcsUrl":"ssh://git@git.inmo.dev:8322/InsanusMokrassar/MicroUtils_mirror.git","developers":[{"id":"InsanusMokrassar","name":"Aleksei Ovsiannikov","eMail":"ovsyannikov.alexey95@gmail.com"},{"id":"000Sanya","name":"Syrov Aleksandr","eMail":"000sanya.000sanya@gmail.com"}]},"type":"Multiplatform"} {"bintrayConfig":{"repo":"MicroUtils","packageName":"${project.name}","packageVcs":"https://github.com/InsanusMokrassar/MicroUtils","autoPublish":true,"overridePublish":true},"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://git.inmo.dev/InsanusMokrassar/MicroUtils_mirror/src/master/LICENSE"}],"mavenConfig":{"name":"${project.name}","description":"","url":"https://git.inmo.dev/InsanusMokrassar/MicroUtils_mirror","vcsUrl":"ssh://git@git.inmo.dev:8322/InsanusMokrassar/MicroUtils_mirror.git","developers":[{"id":"InsanusMokrassar","name":"Aleksei Ovsiannikov","eMail":"ovsyannikov.alexey95@gmail.com"},{"id":"000Sanya","name":"Syrov Aleksandr","eMail":"000sanya.000sanya@gmail.com"}]}}

View File

@@ -1,69 +1,76 @@
apply plugin: 'com.jfrog.bintray' apply plugin: 'maven-publish'
apply from: "$publishMavenPath" task javadocsJar(type: Jar) {
classifier = 'javadoc'
bintray { }
user = project.hasProperty('BINTRAY_USER') ? project.property('BINTRAY_USER') : System.getenv('BINTRAY_USER') task sourceJar (type : Jar) {
key = project.hasProperty('BINTRAY_KEY') ? project.property('BINTRAY_KEY') : System.getenv('BINTRAY_KEY') classifier = 'sources'
filesSpec {
from "${buildDir}/publications/"
eachFile {
String directorySubname = it.getFile().parentFile.name
if (it.getName() == "module.json") {
if (directorySubname == "kotlinMultiplatform") {
it.setPath("${project.name}/${project.version}/${project.name}-${project.version}.module")
} else {
switch (directorySubname) {
case "androidRelease":
it.setPath("${project.name}-android/${project.version}/${project.name}-android-${project.version}.module")
break
case "androidDebug":
it.setPath("${project.name}-android-debug/${project.version}/${project.name}-android-debug-${project.version}.module")
break
default:
it.setPath("${project.name}-${directorySubname}/${project.version}/${project.name}-${directorySubname}-${project.version}.module")
break
}
}
} else {
if (directorySubname == "kotlinMultiplatform" && it.getName() == "pom-default.xml") {
it.setPath("${project.name}/${project.version}/${project.name}-${project.version}.pom")
} else {
it.exclude()
}
}
}
into "${project.group}".replace(".", "/")
}
publish = true
override = true
pkg {
repo = "MicroUtils"
name = "${project.name}"
vcsUrl = "https://github.com/InsanusMokrassar/MicroUtils"
licenses = ["Apache-2.0"]
version {
name = "${project.version}"
released = new Date()
vcsTag = "${project.version}"
gpg {
sign = true
passphrase = project.hasProperty('signing.gnupg.passphrase') ? project.property('signing.gnupg.passphrase') : System.getenv('signing.gnupg.passphrase')
}
}
}
} }
bintrayUpload.doFirst { afterEvaluate {
publications = publishing.publications.collect { project.publishing.publications.all {
// rename artifacts
groupId "${project.group}"
if (it.name.contains('kotlinMultiplatform')) { if (it.name.contains('kotlinMultiplatform')) {
null artifactId = "${project.name}"
artifact sourceJar
} else { } else {
it.name artifactId = "${project.name}-$name"
} }
} - null }
} }
bintrayUpload.dependsOn publishToMavenLocal publishing {
publications.all {
artifact javadocsJar
pom {
description = ""
name = "${project.name}"
url = "https://git.inmo.dev/InsanusMokrassar/MicroUtils_mirror"
scm {
developerConnection = "scm:git:[fetch=]ssh://git@git.inmo.dev:8322/InsanusMokrassar/MicroUtils_mirror.git[push=]ssh://git@git.inmo.dev:8322/InsanusMokrassar/MicroUtils_mirror.git"
url = "ssh://git@git.inmo.dev:8322/InsanusMokrassar/MicroUtils_mirror.git"
}
developers {
developer {
id = "InsanusMokrassar"
name = "Aleksei Ovsiannikov"
email = "ovsyannikov.alexey95@gmail.com"
}
developer {
id = "000Sanya"
name = "Syrov Aleksandr"
email = "000sanya.000sanya@gmail.com"
}
}
licenses {
license {
name = "Apache Software License 2.0"
url = "https://git.inmo.dev/InsanusMokrassar/MicroUtils_mirror/src/master/LICENSE"
}
}
}
repositories {
maven {
name = "bintray"
url = uri("https://api.bintray.com/maven/${project.hasProperty('BINTRAY_USER') ? project.property('BINTRAY_USER') : System.getenv('BINTRAY_USER')}/MicroUtils/${project.name}/;publish=1;override=1")
credentials {
username = project.hasProperty('BINTRAY_USER') ? project.property('BINTRAY_USER') : System.getenv('BINTRAY_USER')
password = project.hasProperty('BINTRAY_KEY') ? project.property('BINTRAY_KEY') : System.getenv('BINTRAY_KEY')
}
}
}
}
}

View File

@@ -2,7 +2,6 @@ plugins {
id "org.jetbrains.kotlin.multiplatform" id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization" id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library" id "com.android.library"
id "kotlin-android-extensions"
} }
apply from: "$mppProjectWithSerializationPresetPath" apply from: "$mppProjectWithSerializationPresetPath"
@@ -18,6 +17,11 @@ kotlin {
} }
} }
jvmMain {
dependencies {
api internalProject("micro_utils.common")
}
}
androidMain { androidMain {
dependencies { dependencies {
api "androidx.core:core-ktx:$core_ktx_version" api "androidx.core:core-ktx:$core_ktx_version"

View File

@@ -36,6 +36,7 @@ interface ReadOneToManyKeyValueRepo<Key, Value> : Repo {
} }
} }
} }
typealias ReadKeyValuesRepo<Key,Value> = ReadOneToManyKeyValueRepo<Key, Value>
interface WriteOneToManyKeyValueRepo<Key, Value> : Repo { interface WriteOneToManyKeyValueRepo<Key, Value> : Repo {
val onNewValue: Flow<Pair<Key, Value>> val onNewValue: Flow<Pair<Key, Value>>
@@ -47,7 +48,13 @@ interface WriteOneToManyKeyValueRepo<Key, Value> : Repo {
suspend fun remove(toRemove: Map<Key, List<Value>>) suspend fun remove(toRemove: Map<Key, List<Value>>)
suspend fun clear(k: Key) suspend fun clear(k: Key)
suspend fun set(toSet: Map<Key, List<Value>>) {
toSet.keys.forEach { key -> clear(key) }
add(toSet)
}
} }
typealias WriteKeyValuesRepo<Key,Value> = WriteOneToManyKeyValueRepo<Key, Value>
suspend inline fun <Key, Value, REPO : WriteOneToManyKeyValueRepo<Key, Value>> REPO.add( suspend inline fun <Key, Value, REPO : WriteOneToManyKeyValueRepo<Key, Value>> REPO.add(
keysAndValues: List<Pair<Key, List<Value>>> keysAndValues: List<Pair<Key, List<Value>>>
@@ -65,7 +72,24 @@ suspend inline fun <Key, Value> WriteOneToManyKeyValueRepo<Key, Value>.add(
k: Key, vararg v: Value k: Key, vararg v: Value
) = add(k, v.toList()) ) = add(k, v.toList())
suspend inline fun <Key, Value, REPO : WriteOneToManyKeyValueRepo<Key, Value>> REPO.set(
keysAndValues: List<Pair<Key, List<Value>>>
) = set(keysAndValues.toMap())
suspend inline fun <Key, Value, REPO : WriteOneToManyKeyValueRepo<Key, Value>> REPO.set(
vararg keysAndValues: Pair<Key, List<Value>>
) = set(keysAndValues.toMap())
suspend inline fun <Key, Value> WriteOneToManyKeyValueRepo<Key, Value>.set(
k: Key, v: List<Value>
) = set(mapOf(k to v))
suspend inline fun <Key, Value> WriteOneToManyKeyValueRepo<Key, Value>.set(
k: Key, vararg v: Value
) = set(k, v.toList())
interface OneToManyKeyValueRepo<Key, Value> : ReadOneToManyKeyValueRepo<Key, Value>, WriteOneToManyKeyValueRepo<Key, Value> interface OneToManyKeyValueRepo<Key, Value> : ReadOneToManyKeyValueRepo<Key, Value>, WriteOneToManyKeyValueRepo<Key, Value>
typealias KeyValuesRepo<Key,Value> = OneToManyKeyValueRepo<Key, Value>
suspend inline fun <Key, Value> WriteOneToManyKeyValueRepo<Key, Value>.remove( suspend inline fun <Key, Value> WriteOneToManyKeyValueRepo<Key, Value>.remove(
keysAndValues: List<Pair<Key, List<Value>>> keysAndValues: List<Pair<Key, List<Value>>>

View File

@@ -10,6 +10,7 @@ interface ReadStandardCRUDRepo<ObjectType, IdType> : Repo {
suspend fun contains(id: IdType): Boolean suspend fun contains(id: IdType): Boolean
suspend fun count(): Long suspend fun count(): Long
} }
typealias ReadCRUDRepo<ObjectType, IdType> = ReadStandardCRUDRepo<ObjectType, IdType>
typealias UpdatedValuePair<IdType, ValueType> = Pair<IdType, ValueType> typealias UpdatedValuePair<IdType, ValueType> = Pair<IdType, ValueType>
val <IdType> UpdatedValuePair<IdType, *>.id val <IdType> UpdatedValuePair<IdType, *>.id
@@ -27,6 +28,7 @@ interface WriteStandardCRUDRepo<ObjectType, IdType, InputValueType> : Repo {
suspend fun update(values: List<UpdatedValuePair<IdType, InputValueType>>): List<ObjectType> suspend fun update(values: List<UpdatedValuePair<IdType, InputValueType>>): List<ObjectType>
suspend fun deleteById(ids: List<IdType>) suspend fun deleteById(ids: List<IdType>)
} }
typealias WriteCRUDRepo<ObjectType, IdType, InputValueType> = WriteStandardCRUDRepo<ObjectType, IdType, InputValueType>
suspend fun <ObjectType, IdType, InputValueType> WriteStandardCRUDRepo<ObjectType, IdType, InputValueType>.create( suspend fun <ObjectType, IdType, InputValueType> WriteStandardCRUDRepo<ObjectType, IdType, InputValueType>.create(
vararg values: InputValueType vararg values: InputValueType
@@ -39,4 +41,5 @@ suspend fun <ObjectType, IdType, InputValueType> WriteStandardCRUDRepo<ObjectTyp
) = deleteById(ids.toList()) ) = deleteById(ids.toList())
interface StandardCRUDRepo<ObjectType, IdType, InputValueType> : ReadStandardCRUDRepo<ObjectType, IdType>, interface StandardCRUDRepo<ObjectType, IdType, InputValueType> : ReadStandardCRUDRepo<ObjectType, IdType>,
WriteStandardCRUDRepo<ObjectType, IdType, InputValueType> WriteStandardCRUDRepo<ObjectType, IdType, InputValueType>
typealias CRUDRepo<ObjectType, IdType, InputValueType> = StandardCRUDRepo<ObjectType, IdType, InputValueType>

View File

@@ -12,6 +12,7 @@ interface ReadStandardKeyValueRepo<Key, Value> : Repo {
suspend fun contains(key: Key): Boolean suspend fun contains(key: Key): Boolean
suspend fun count(): Long suspend fun count(): Long
} }
typealias ReadKeyValueRepo<Key,Value> = ReadStandardKeyValueRepo<Key, Value>
interface WriteStandardKeyValueRepo<Key, Value> : Repo { interface WriteStandardKeyValueRepo<Key, Value> : Repo {
val onNewValue: Flow<Pair<Key, Value>> val onNewValue: Flow<Pair<Key, Value>>
@@ -20,6 +21,7 @@ interface WriteStandardKeyValueRepo<Key, Value> : Repo {
suspend fun set(toSet: Map<Key, Value>) suspend fun set(toSet: Map<Key, Value>)
suspend fun unset(toUnset: List<Key>) suspend fun unset(toUnset: List<Key>)
} }
typealias WriteKeyValueRepo<Key,Value> = WriteStandardKeyValueRepo<Key, Value>
suspend inline fun <Key, Value> WriteStandardKeyValueRepo<Key, Value>.set( suspend inline fun <Key, Value> WriteStandardKeyValueRepo<Key, Value>.set(
vararg toSet: Pair<Key, Value> vararg toSet: Pair<Key, Value>
@@ -33,4 +35,5 @@ suspend inline fun <Key, Value> WriteStandardKeyValueRepo<Key, Value>.unset(
vararg k: Key vararg k: Key
) = unset(k.toList()) ) = unset(k.toList())
interface StandardKeyValueRepo<Key, Value> : ReadStandardKeyValueRepo<Key, Value>, WriteStandardKeyValueRepo<Key, Value> interface StandardKeyValueRepo<Key, Value> : ReadStandardKeyValueRepo<Key, Value>, WriteStandardKeyValueRepo<Key, Value>
typealias KeyValueRepo<Key,Value> = StandardKeyValueRepo<Key, Value>

View File

@@ -107,6 +107,12 @@ open class MapperWriteOneToManyKeyValueRepo<FromKey, FromValue, ToKey, ToValue>(
}.toMap() }.toMap()
) )
override suspend fun set(toSet: Map<FromKey, List<FromValue>>) {
to.set(
toSet.map { (k, vs) -> k.toOutKey() to vs.map { v -> v.toOutValue() } }.toMap()
)
}
override suspend fun clear(k: FromKey) = to.clear(k.toOutKey()) override suspend fun clear(k: FromKey) = to.clear(k.toOutKey())
} }

View File

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

View File

@@ -0,0 +1,36 @@
package dev.inmo.micro_utils.repos.versions
import dev.inmo.micro_utils.repos.Repo
interface StandardVersionsRepoProxy<T> : Repo {
val database: T
suspend fun getTableVersion(tableName: String): Int?
suspend fun updateTableVersion(tableName: String, version: Int)
}
class StandardVersionsRepo<T>(
private val proxy: StandardVersionsRepoProxy<T>
) : VersionsRepo<T> {
override suspend fun setTableVersion(
tableName: String,
version: Int,
onCreate: suspend T.() -> Unit,
onUpdate: suspend T.(from: Int, to: Int) -> Unit
) {
var savedVersion = proxy.getTableVersion(tableName)
if (savedVersion == null) {
proxy.database.onCreate()
proxy.updateTableVersion(tableName, version)
} else {
while (savedVersion != null && savedVersion < version) {
val newVersion = savedVersion + 1
proxy.database.onUpdate(savedVersion, newVersion)
proxy.updateTableVersion(tableName, newVersion)
savedVersion = newVersion
}
}
}
}

View File

@@ -0,0 +1,31 @@
package dev.inmo.micro_utils.repos.versions
import dev.inmo.micro_utils.repos.Repo
/**
* This interface has been created due to requirement to work with different versions of databases and make some
* migrations between versions
*
* @param T It is a type of database, which will be used by this repo to retrieve current table version and update it
*/
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]
*
* @param version Current version of table
* @param onCreate This callback will be called in case when table 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"
*/
suspend fun setTableVersion(
tableName: String,
version: Int,
onCreate: suspend T.() -> Unit = {},
onUpdate: suspend T.(from: Int, to: Int) -> Unit = { _, _ ->}
)
}

View File

@@ -0,0 +1,171 @@
package dev.inmo.micro_utils.repos
import dev.inmo.micro_utils.common.Warning
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.pagination.utils.reverse
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import java.io.File
import java.nio.file.FileSystems
import java.nio.file.Path
import java.nio.file.StandardWatchEventKinds.*
private inline val String.isAbsolute
get() = startsWith(File.separator)
class FileReadStandardKeyValueRepo(
private val folder: File
) : ReadStandardKeyValueRepo<String, File> {
init {
folder.mkdirs()
}
override suspend fun get(k: String): File? {
val file = File(folder, k)
if (file.exists()) {
return file
}
return null
}
override suspend fun values(pagination: Pagination, reversed: Boolean): PaginationResult<File> {
val count = count()
val resultPagination = if (reversed) pagination.reverse(count) else pagination
val filesPaths = folder.list() ?.copyOfRange(resultPagination.firstIndex, resultPagination.lastIndex) ?: return emptyPaginationResult()
if (reversed) {
filesPaths.reverse()
}
return filesPaths.map { File(folder, it) }.createPaginationResult(
resultPagination,
count
)
}
override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<String> {
val count = count()
val resultPagination = if (reversed) pagination.reverse(count) else pagination
val filesPaths = folder.list() ?.copyOfRange(resultPagination.firstIndex, resultPagination.lastIndex) ?: return emptyPaginationResult()
if (reversed) {
filesPaths.reverse()
}
return filesPaths.toList().createPaginationResult(
resultPagination,
count
)
}
override suspend fun keys(
v: File,
pagination: Pagination,
reversed: Boolean
): PaginationResult<String> {
val resultPagination = if (reversed) pagination.reverse(1L) else pagination
return if (resultPagination.isFirstPage) {
val fileSubpath = v.absolutePath.removePrefix(folder.absolutePath)
if (fileSubpath == v.absolutePath) {
emptyList()
} else {
listOf(fileSubpath)
}
} else {
emptyList()
}.createPaginationResult(resultPagination, 1L)
}
override suspend fun contains(key: String): Boolean {
return File(folder, key).exists()
}
override suspend fun count(): Long = folder.list() ?.size ?.toLong() ?: 0L
}
/**
* 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(
private val folder: File,
filesChangedProcessingScope: CoroutineScope? = null
) : WriteStandardKeyValueRepo<String, File> {
private val _onNewValue = MutableSharedFlow<Pair<String, File>>()
override val onNewValue: Flow<Pair<String, File>> = _onNewValue.asSharedFlow()
private val _onValueRemoved = MutableSharedFlow<String>()
override val onValueRemoved: Flow<String> = _onValueRemoved.asSharedFlow()
init {
folder.mkdirs()
filesChangedProcessingScope ?.let {
it.launch {
try {
val folderPath = folder.toPath()
while (isActive) {
val key = try {
folderPath.register(FileSystems.getDefault().newWatchService(), ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE)
} catch (e: Exception) {
// add verbose way to show that file watching is not working
return@launch
}
for (event in key.pollEvents()) {
val relativeFilePath = (event.context() as? Path) ?: continue
val file = relativeFilePath.toFile()
val relativePath = file.toRelativeString(folder)
when (event.kind()) {
ENTRY_CREATE, ENTRY_MODIFY -> {
launch {
_onNewValue.emit(relativePath to file)
}
}
ENTRY_DELETE -> {
launch {
_onValueRemoved.emit(relativePath)
}
}
}
}
if (key.isValid || folder.exists()) {
continue
}
break
}
} catch (e: Throwable) {
// add verbose way to notify that this functionality is disabled
}
}
}
}
override suspend fun set(toSet: Map<String, File>) {
supervisorScope {
toSet.map { (filename, fileSource) ->
launch {
val file = File(folder, filename)
file.delete()
fileSource.copyTo(file, overwrite = true)
_onNewValue.emit(filename to file)
}
}
}.joinAll()
}
override suspend fun unset(toUnset: List<String>) {
toUnset.forEach {
val file = File(folder, it)
if (file.exists()) {
file.delete()
_onValueRemoved.emit(it)
}
}
}
}
@Warning("Files watching will not correctly works on Android Platform with version of API lower than API 26")
class FileStandardKeyValueRepo(
folder: File,
filesChangedProcessingScope: CoroutineScope? = null
) : StandardKeyValueRepo<String, File>,
WriteStandardKeyValueRepo<String, File> by FileWriteStandardKeyValueRepo(folder, filesChangedProcessingScope),
ReadStandardKeyValueRepo<String, File> by FileReadStandardKeyValueRepo(folder)

View File

@@ -1,6 +1,6 @@
package dev.inmo.micro_utils.repos package dev.inmo.micro_utils.repos
import kotlinx.coroutines.newSingleThreadContext import kotlinx.coroutines.Dispatchers
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
val DatabaseCoroutineContext: CoroutineContext = newSingleThreadContext("db-context") val DatabaseCoroutineContext: CoroutineContext = Dispatchers.IO

View File

@@ -2,26 +2,68 @@ package dev.inmo.micro_utils.repos
import android.database.sqlite.SQLiteDatabase import android.database.sqlite.SQLiteDatabase
import dev.inmo.micro_utils.coroutines.safely import dev.inmo.micro_utils.coroutines.safely
import kotlinx.coroutines.withContext import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import java.util.concurrent.Executors
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.coroutineContext
private object ContextsPool {
private val contexts = mutableListOf<CoroutineContext>()
private val mutex = Mutex(locked = false)
private val freeContexts = mutableListOf<CoroutineContext>()
suspend fun acquireContext(): CoroutineContext {
return mutex.withLock {
freeContexts.removeFirstOrNull() ?: Executors.newSingleThreadExecutor().asCoroutineDispatcher().also {
contexts.add(it)
}
}
}
suspend fun freeContext(context: CoroutineContext) {
return mutex.withLock {
if (context in contexts && context !in freeContexts) {
freeContexts.add(context)
}
}
}
suspend fun <T> use(block: suspend (CoroutineContext) -> T): T = acquireContext().let {
try {
block(it)
} finally {
freeContext(it)
}
}
}
class TransactionContext(
val databaseContext: CoroutineContext
): CoroutineContext.Element {
override val key: CoroutineContext.Key<TransactionContext> = TransactionContext
companion object : CoroutineContext.Key<TransactionContext>
}
suspend fun <T> SQLiteDatabase.transaction(block: suspend SQLiteDatabase.() -> T): T { suspend fun <T> SQLiteDatabase.transaction(block: suspend SQLiteDatabase.() -> T): T {
return withContext(DatabaseCoroutineContext) { return coroutineContext[TransactionContext] ?.let {
when { withContext(it.databaseContext) {
inTransaction() -> { block()
block() }
} } ?: ContextsPool.use { context ->
else -> { withContext(TransactionContext(context) + context) {
beginTransaction() beginTransaction()
safely( safely(
{ {
endTransaction() endTransaction()
throw it throw it
} }
) { ) {
block().also { block().also {
setTransactionSuccessful() setTransactionSuccessful()
endTransaction() endTransaction()
}
} }
} }
} }

View File

@@ -5,6 +5,7 @@ import android.database.DatabaseErrorHandler
import android.database.sqlite.SQLiteDatabase import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper import android.database.sqlite.SQLiteOpenHelper
import dev.inmo.micro_utils.coroutines.safely import dev.inmo.micro_utils.coroutines.safely
import dev.inmo.micro_utils.repos.versions.*
import kotlin.coroutines.Continuation import kotlin.coroutines.Continuation
import kotlin.coroutines.resume import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException import kotlin.coroutines.resumeWithException
@@ -36,6 +37,9 @@ class StandardSQLHelper(
override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {} override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {}
} }
val versionsRepo: VersionsRepo<SQLiteOpenHelper> by lazy {
StandardVersionsRepo(AndroidSQLStandardVersionsRepoProxy(sqlOpenHelper))
}
suspend fun <T> writableTransaction(block: suspend SQLiteDatabase.() -> T): T = sqlOpenHelper.writableTransaction(block) suspend fun <T> writableTransaction(block: suspend SQLiteDatabase.() -> T): T = sqlOpenHelper.writableTransaction(block)

View File

@@ -87,6 +87,29 @@ class OneToManyAndroidRepo<Key, Value>(
} }
} }
override suspend fun set(toSet: Map<Key, List<Value>>) {
val (clearedKeys, inserted) = helper.writableTransaction {
toSet.mapNotNull { (k, _) ->
if (delete(tableName, "$idColumnName=?", arrayOf(k.asId())) > 0) {
k
} else {
null
}
} to toSet.flatMap { (k, values) ->
values.map { v ->
insert(
tableName,
null,
contentValuesOf(idColumnName to k.asId(), valueColumnName to v.asValue())
)
k to v
}
}
}
clearedKeys.forEach { _onDataCleared.emit(it) }
inserted.forEach { newPair -> _onNewValue.emit(newPair) }
}
override suspend fun contains(k: Key): Boolean = helper.readableTransaction { override suspend fun contains(k: Key): Boolean = helper.readableTransaction {
select(tableName, selection = "$idColumnName=?", selectionArgs = arrayOf(k.asId()), limit = FirstPagePagination(1).limitClause()).use { select(tableName, selection = "$idColumnName=?", selectionArgs = arrayOf(k.asId()), limit = FirstPagePagination(1).limitClause()).use {
it.count > 0 it.count > 0

View File

@@ -0,0 +1,63 @@
package dev.inmo.micro_utils.repos.versions
import android.database.sqlite.SQLiteOpenHelper
import androidx.core.content.contentValuesOf
import dev.inmo.micro_utils.repos.*
import kotlinx.coroutines.runBlocking
/**
* Will create [VersionsRepo] based on [SQLiteOpenHelper] with table inside of [database]
*/
@Suppress("NOTHING_TO_INLINE")
inline fun versionsRepo(database: SQLiteOpenHelper): VersionsRepo<SQLiteOpenHelper> = StandardVersionsRepo(
AndroidSQLStandardVersionsRepoProxy(database)
)
class AndroidSQLStandardVersionsRepoProxy(
override val database: SQLiteOpenHelper
) : StandardVersionsRepoProxy<SQLiteOpenHelper> {
private val tableName: String = "AndroidSQLStandardVersionsRepo"
private val tableNameColumnName = "tableName"
private val tableVersionColumnName = "version"
init {
runBlocking(DatabaseCoroutineContext) {
database.writableTransaction {
createTable(
tableName,
tableNameColumnName to ColumnType.Text.NOT_NULLABLE,
tableVersionColumnName to ColumnType.Numeric.INTEGER()
)
}
}
}
override suspend fun getTableVersion(table: String): Int? = database.writableTransaction {
select(
tableName,
selection = "$tableNameColumnName=?",
selectionArgs = arrayOf(table),
limit = limitClause(1)
).use {
if (it.moveToFirst()) {
it.getInt(tableVersionColumnName)
} else {
null
}
}
}
override suspend fun updateTableVersion(table: String, version: Int) {
database.writableTransaction {
val updated = update(
tableName,
contentValuesOf(tableVersionColumnName to version),
"$tableNameColumnName=?",
arrayOf(table)
) > 0
if (!updated) {
insert(tableName, null, contentValuesOf(tableNameColumnName to table, tableVersionColumnName to version))
}
}
}
}

View File

@@ -0,0 +1,49 @@
@file:Suppress("NOTHING_TO_INLINE")
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]
*
* @receiver Will be used to create [KeyValueBasedVersionsRepoProxy] via [keyValueStore] and pass it to [StandardVersionsRepo]
*
* @see [KeyValueBasedVersionsRepoProxy]
* @see [keyValueStore]
*/
inline fun <T> Context.versionsKeyValueRepo(
database: T
): VersionsRepo<T> = StandardVersionsRepo(
KeyValueBasedVersionsRepoProxy(
keyValueStore("SPVersionsRepo"),
database
)
)
/**
* Will create [VersionsRepo] based on [SQLiteOpenHelper], but versions will be stored in [StandardKeyValueRepo]
*
* @receiver Will be used to create [StandardKeyValueRepo] via [keyValueStore] and pass it to [StandardVersionsRepo]
*
* @see [keyValueStore]
*/
inline fun Context.versionsKeyValueRepoForSQL(
database: SQLiteOpenHelper
) = versionsKeyValueRepo(database)
/**
* Will create [VersionsRepo] based on [SQLiteOpenHelper], but versions will be stored in [StandardKeyValueRepo]
*
* @param context Will be used to create [StandardKeyValueRepo] via [keyValueStore] and pass it to [StandardVersionsRepo]
*
* @see [keyValueStore]
*/
inline fun versionsRepo(
context: Context,
database: SQLiteOpenHelper
) = context.versionsKeyValueRepoForSQL(database)

View File

@@ -2,6 +2,7 @@ package dev.inmo.micro_utils.repos.exposed.keyvalue
import dev.inmo.micro_utils.repos.StandardKeyValueRepo import dev.inmo.micro_utils.repos.StandardKeyValueRepo
import dev.inmo.micro_utils.repos.exposed.ColumnAllocator import dev.inmo.micro_utils.repos.exposed.ColumnAllocator
import dev.inmo.micro_utils.repos.exposed.initTable
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.*
@@ -18,8 +19,8 @@ open class ExposedKeyValueRepo<Key, Value>(
valueColumnAllocator, valueColumnAllocator,
tableName tableName
) { ) {
private val _onNewValue = MutableSharedFlow<Pair<Key, Value>>(Channel.BUFFERED) private val _onNewValue = MutableSharedFlow<Pair<Key, Value>>()
private val _onValueRemoved = MutableSharedFlow<Key>(Channel.BUFFERED) private val _onValueRemoved = MutableSharedFlow<Key>()
override val onNewValue: Flow<Pair<Key, Value>> = _onNewValue.asSharedFlow() override val onNewValue: Flow<Pair<Key, Value>> = _onNewValue.asSharedFlow()
override val onValueRemoved: Flow<Key> = _onValueRemoved.asSharedFlow() override val onValueRemoved: Flow<Key> = _onValueRemoved.asSharedFlow()

View File

@@ -2,8 +2,7 @@ package dev.inmo.micro_utils.repos.exposed.keyvalue
import dev.inmo.micro_utils.pagination.* import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.ReadStandardKeyValueRepo import dev.inmo.micro_utils.repos.ReadStandardKeyValueRepo
import dev.inmo.micro_utils.repos.exposed.ColumnAllocator import dev.inmo.micro_utils.repos.exposed.*
import dev.inmo.micro_utils.repos.exposed.ExposedRepo
import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
@@ -17,6 +16,8 @@ open class ExposedReadKeyValueRepo<Key, Value>(
protected val valueColumn: Column<Value> = valueColumnAllocator() protected val valueColumn: Column<Value> = valueColumnAllocator()
override val primaryKey: PrimaryKey = PrimaryKey(keyColumn, valueColumn) override val primaryKey: PrimaryKey = PrimaryKey(keyColumn, valueColumn)
init { initTable() }
override suspend fun get(k: Key): Value? = transaction(database) { override suspend fun get(k: Key): Value? = transaction(database) {
select { keyColumn.eq(k) }.limit(1).firstOrNull() ?.getOrNull(valueColumn) select { keyColumn.eq(k) }.limit(1).firstOrNull() ?.getOrNull(valueColumn)
} }
@@ -33,8 +34,8 @@ open class ExposedReadKeyValueRepo<Key, Value>(
} }
}.createPaginationResult(pagination, count()) }.createPaginationResult(pagination, count())
override suspend fun keys(value: Value, pagination: Pagination, reversed: Boolean): PaginationResult<Key> = transaction(database) { override suspend fun keys(v: Value, pagination: Pagination, reversed: Boolean): PaginationResult<Key> = transaction(database) {
select { valueColumn.eq(value) }.let { select { valueColumn.eq(v) }.let {
it.count() to it.paginate(pagination, keyColumn to if (reversed) SortOrder.DESC else SortOrder.ASC).map { it.count() to it.paginate(pagination, keyColumn to if (reversed) SortOrder.DESC else SortOrder.ASC).map {
it[keyColumn] it[keyColumn]
} }

View File

@@ -0,0 +1,48 @@
package dev.inmo.micro_utils.repos.exposed.versions
import dev.inmo.micro_utils.repos.exposed.ExposedRepo
import dev.inmo.micro_utils.repos.exposed.initTable
import dev.inmo.micro_utils.repos.versions.*
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction
/**
* Use this method to create [StandardVersionsRepo] based on [Database] with [ExposedStandardVersionsRepoProxy] as
* [StandardVersionsRepoProxy]
*/
@Suppress("NOTHING_TO_INLINE")
inline fun versionsRepo(database: Database): VersionsRepo<Database> = StandardVersionsRepo(
ExposedStandardVersionsRepoProxy(database)
)
class ExposedStandardVersionsRepoProxy(
override val database: Database
) : StandardVersionsRepoProxy<Database>, Table("ExposedVersionsProxy"), ExposedRepo {
private val tableNameColumn = text("tableName")
private val tableVersionColumn = integer("tableName")
init {
initTable()
}
override suspend fun getTableVersion(tableName: String): Int? = transaction(database) {
select { tableNameColumn.eq(tableName) }.limit(1).firstOrNull() ?.getOrNull(tableVersionColumn)
}
override suspend fun updateTableVersion(tableName: String, version: Int) {
transaction(database) {
val updated = update(
{ tableNameColumn.eq(tableName) }
) {
it[tableVersionColumn] = version
} > 0
if (!updated) {
insert {
it[tableNameColumn] = tableName
it[tableVersionColumn] = version
}
}
}
}
}

View File

@@ -2,7 +2,6 @@ plugins {
id "org.jetbrains.kotlin.multiplatform" id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization" id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library" id "com.android.library"
id "kotlin-android-extensions"
} }
apply from: "$mppProjectWithSerializationPresetPath" apply from: "$mppProjectWithSerializationPresetPath"

View File

@@ -2,7 +2,6 @@ plugins {
id "org.jetbrains.kotlin.multiplatform" id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization" id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library" id "com.android.library"
id "kotlin-android-extensions"
} }
apply from: "$mppProjectWithSerializationPresetPath" apply from: "$mppProjectWithSerializationPresetPath"

View File

@@ -1,8 +1,7 @@
package dev.inmo.micro_utils.repos.ktor.client.crud package dev.inmo.micro_utils.repos.ktor.client.crud
import dev.inmo.micro_utils.ktor.client.encodeUrlQueryValue import dev.inmo.micro_utils.ktor.client.*
import dev.inmo.micro_utils.ktor.client.uniget import dev.inmo.micro_utils.ktor.common.*
import dev.inmo.micro_utils.ktor.common.buildStandardUrl
import dev.inmo.micro_utils.pagination.* import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.ReadStandardCRUDRepo import dev.inmo.micro_utils.repos.ReadStandardCRUDRepo
import dev.inmo.micro_utils.repos.ktor.common.crud.* import dev.inmo.micro_utils.repos.ktor.common.crud.*
@@ -12,41 +11,52 @@ import kotlinx.serialization.builtins.serializer
class KtorReadStandardCrudRepo<ObjectType, IdType> ( class KtorReadStandardCrudRepo<ObjectType, IdType> (
private val baseUrl: String, private val baseUrl: String,
private val client: HttpClient = HttpClient(), private val unifiedRequester: UnifiedRequester,
private val objectsSerializer: KSerializer<ObjectType>, private val objectsSerializer: KSerializer<ObjectType>,
private val objectsSerializerNullable: KSerializer<ObjectType?>, private val objectsSerializerNullable: KSerializer<ObjectType?>,
private val idsSerializer: KSerializer<IdType> private val idsSerializer: KSerializer<IdType>
) : ReadStandardCRUDRepo<ObjectType, IdType> { ) : ReadStandardCRUDRepo<ObjectType, IdType> {
private val paginationResultSerializer = PaginationResult.serializer(objectsSerializer) private val paginationResultSerializer = PaginationResult.serializer(objectsSerializer)
override suspend fun getByPagination(pagination: Pagination): PaginationResult<ObjectType> = client.uniget( constructor(
baseUrl: String,
client: HttpClient,
objectsSerializer: KSerializer<ObjectType>,
objectsSerializerNullable: KSerializer<ObjectType?>,
idsSerializer: KSerializer<IdType>,
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
) : this (
baseUrl, UnifiedRequester(client, serialFormat), objectsSerializer, objectsSerializerNullable, idsSerializer
)
override suspend fun getByPagination(pagination: Pagination): PaginationResult<ObjectType> = unifiedRequester.uniget(
buildStandardUrl(baseUrl, getByPaginationRouting, pagination.asUrlQueryParts), buildStandardUrl(baseUrl, getByPaginationRouting, pagination.asUrlQueryParts),
paginationResultSerializer paginationResultSerializer
) )
override suspend fun getById(id: IdType): ObjectType? = client.uniget( override suspend fun getById(id: IdType): ObjectType? = unifiedRequester.uniget(
buildStandardUrl( buildStandardUrl(
baseUrl, baseUrl,
getByIdRouting, getByIdRouting,
mapOf( mapOf(
"id" to idsSerializer.encodeUrlQueryValue(id) "id" to unifiedRequester.encodeUrlQueryValue(idsSerializer, id)
) )
), ),
objectsSerializerNullable objectsSerializerNullable
) )
override suspend fun contains(id: IdType): Boolean = client.uniget( override suspend fun contains(id: IdType): Boolean = unifiedRequester.uniget(
buildStandardUrl( buildStandardUrl(
baseUrl, baseUrl,
containsRouting, containsRouting,
mapOf( mapOf(
"id" to idsSerializer.encodeUrlQueryValue(id) "id" to unifiedRequester.encodeUrlQueryValue(idsSerializer, id)
) )
), ),
Boolean.serializer() Boolean.serializer()
) )
override suspend fun count(): Long = client.uniget( override suspend fun count(): Long = unifiedRequester.uniget(
buildStandardUrl( buildStandardUrl(
baseUrl, baseUrl,
countRouting countRouting

View File

@@ -1,5 +1,8 @@
package dev.inmo.micro_utils.repos.ktor.client.crud package dev.inmo.micro_utils.repos.ktor.client.crud
import dev.inmo.micro_utils.ktor.client.UnifiedRequester
import dev.inmo.micro_utils.ktor.common.StandardKtorSerialFormat
import dev.inmo.micro_utils.ktor.common.standardKtorSerialFormat
import dev.inmo.micro_utils.repos.* import dev.inmo.micro_utils.repos.*
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
import kotlinx.serialization.KSerializer import kotlinx.serialization.KSerializer
@@ -7,7 +10,7 @@ import kotlinx.serialization.KSerializer
class KtorStandardCrudRepo<ObjectType, IdType, InputValue> ( class KtorStandardCrudRepo<ObjectType, IdType, InputValue> (
baseUrl: String, baseUrl: String,
baseSubpart: String, baseSubpart: String,
client: HttpClient, unifiedRequester: UnifiedRequester,
objectsSerializer: KSerializer<ObjectType>, objectsSerializer: KSerializer<ObjectType>,
objectsNullableSerializer: KSerializer<ObjectType?>, objectsNullableSerializer: KSerializer<ObjectType?>,
inputsSerializer: KSerializer<InputValue>, inputsSerializer: KSerializer<InputValue>,
@@ -15,16 +18,29 @@ class KtorStandardCrudRepo<ObjectType, IdType, InputValue> (
) : StandardCRUDRepo<ObjectType, IdType, InputValue>, ) : StandardCRUDRepo<ObjectType, IdType, InputValue>,
ReadStandardCRUDRepo<ObjectType, IdType> by KtorReadStandardCrudRepo( ReadStandardCRUDRepo<ObjectType, IdType> by KtorReadStandardCrudRepo(
"$baseUrl/$baseSubpart", "$baseUrl/$baseSubpart",
client, unifiedRequester,
objectsSerializer, objectsSerializer,
objectsNullableSerializer, objectsNullableSerializer,
idsSerializer idsSerializer
), ),
WriteStandardCRUDRepo<ObjectType, IdType, InputValue> by KtorWriteStandardCrudRepo( WriteStandardCRUDRepo<ObjectType, IdType, InputValue> by KtorWriteStandardCrudRepo(
"$baseUrl/$baseSubpart", "$baseUrl/$baseSubpart",
client, unifiedRequester,
objectsSerializer, objectsSerializer,
objectsNullableSerializer, objectsNullableSerializer,
inputsSerializer, inputsSerializer,
idsSerializer idsSerializer
) {
constructor(
baseUrl: String,
baseSubpart: String,
client: HttpClient,
objectsSerializer: KSerializer<ObjectType>,
objectsNullableSerializer: KSerializer<ObjectType?>,
inputsSerializer: KSerializer<InputValue>,
idsSerializer: KSerializer<IdType>,
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
) : this(
baseUrl, baseSubpart, UnifiedRequester(client, serialFormat), objectsSerializer, objectsNullableSerializer, inputsSerializer, idsSerializer
) )
}

View File

@@ -1,7 +1,7 @@
package dev.inmo.micro_utils.repos.ktor.client.crud package dev.inmo.micro_utils.repos.ktor.client.crud
import dev.inmo.micro_utils.ktor.client.* import dev.inmo.micro_utils.ktor.client.*
import dev.inmo.micro_utils.ktor.common.buildStandardUrl import dev.inmo.micro_utils.ktor.common.*
import dev.inmo.micro_utils.repos.UpdatedValuePair import dev.inmo.micro_utils.repos.UpdatedValuePair
import dev.inmo.micro_utils.repos.WriteStandardCRUDRepo import dev.inmo.micro_utils.repos.WriteStandardCRUDRepo
import dev.inmo.micro_utils.repos.ktor.common.crud.* import dev.inmo.micro_utils.repos.ktor.common.crud.*
@@ -12,7 +12,7 @@ import kotlinx.serialization.builtins.*
class KtorWriteStandardCrudRepo<ObjectType, IdType, InputValue> ( class KtorWriteStandardCrudRepo<ObjectType, IdType, InputValue> (
private val baseUrl: String, private val baseUrl: String,
private val client: HttpClient = HttpClient(), private val unifiedRequester: UnifiedRequester,
private val objectsSerializer: KSerializer<ObjectType>, private val objectsSerializer: KSerializer<ObjectType>,
private val objectsNullableSerializer: KSerializer<ObjectType?>, private val objectsNullableSerializer: KSerializer<ObjectType?>,
private val inputsSerializer: KSerializer<InputValue>, private val inputsSerializer: KSerializer<InputValue>,
@@ -27,39 +27,51 @@ class KtorWriteStandardCrudRepo<ObjectType, IdType, InputValue> (
) )
private val listInputUpdateSerializer = ListSerializer(inputUpdateSerializer) private val listInputUpdateSerializer = ListSerializer(inputUpdateSerializer)
override val newObjectsFlow: Flow<ObjectType> = client.createStandardWebsocketFlow( constructor(
baseUrl: String,
client: HttpClient,
objectsSerializer: KSerializer<ObjectType>,
objectsSerializerNullable: KSerializer<ObjectType?>,
inputsSerializer: KSerializer<InputValue>,
idsSerializer: KSerializer<IdType>,
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
) : this (
baseUrl, UnifiedRequester(client, serialFormat), objectsSerializer, objectsSerializerNullable, inputsSerializer, idsSerializer
)
override val newObjectsFlow: Flow<ObjectType> = unifiedRequester.createStandardWebsocketFlow(
buildStandardUrl(baseUrl, newObjectsFlowRouting), buildStandardUrl(baseUrl, newObjectsFlowRouting),
deserializer = objectsSerializer deserializer = objectsSerializer
) )
override val updatedObjectsFlow: Flow<ObjectType> = client.createStandardWebsocketFlow( override val updatedObjectsFlow: Flow<ObjectType> = unifiedRequester.createStandardWebsocketFlow(
buildStandardUrl(baseUrl, updatedObjectsFlowRouting), buildStandardUrl(baseUrl, updatedObjectsFlowRouting),
deserializer = objectsSerializer deserializer = objectsSerializer
) )
override val deletedObjectsIdsFlow: Flow<IdType> = client.createStandardWebsocketFlow( override val deletedObjectsIdsFlow: Flow<IdType> = unifiedRequester.createStandardWebsocketFlow(
buildStandardUrl(baseUrl, deletedObjectsIdsFlowRouting), buildStandardUrl(baseUrl, deletedObjectsIdsFlowRouting),
deserializer = idsSerializer deserializer = idsSerializer
) )
override suspend fun create(values: List<InputValue>): List<ObjectType> = client.unipost( override suspend fun create(values: List<InputValue>): List<ObjectType> = unifiedRequester.unipost(
buildStandardUrl(baseUrl, createRouting), buildStandardUrl(baseUrl, createRouting),
BodyPair(listInputSerializer, values), BodyPair(listInputSerializer, values),
listObjectsSerializer listObjectsSerializer
) )
override suspend fun update(id: IdType, value: InputValue): ObjectType? = client.unipost( override suspend fun update(id: IdType, value: InputValue): ObjectType? = unifiedRequester.unipost(
buildStandardUrl(baseUrl, updateRouting), buildStandardUrl(baseUrl, updateRouting),
BodyPair(inputUpdateSerializer, id to value), BodyPair(inputUpdateSerializer, id to value),
objectsNullableSerializer objectsNullableSerializer
) )
override suspend fun update(values: List<UpdatedValuePair<IdType, InputValue>>): List<ObjectType> = client.unipost( override suspend fun update(values: List<UpdatedValuePair<IdType, InputValue>>): List<ObjectType> = unifiedRequester.unipost(
buildStandardUrl(baseUrl, updateManyRouting), buildStandardUrl(baseUrl, updateManyRouting),
BodyPair(listInputUpdateSerializer, values), BodyPair(listInputUpdateSerializer, values),
listObjectsSerializer listObjectsSerializer
) )
override suspend fun deleteById(ids: List<IdType>) = client.unipost( override suspend fun deleteById(ids: List<IdType>) = unifiedRequester.unipost(
buildStandardUrl(baseUrl, deleteByIdRouting), buildStandardUrl(baseUrl, deleteByIdRouting),
BodyPair(listIdsSerializer, ids), BodyPair(listIdsSerializer, ids),
Unit.serializer() Unit.serializer()

View File

@@ -1,80 +1,90 @@
package dev.inmo.micro_utils.repos.ktor.client.key_value package dev.inmo.micro_utils.repos.ktor.client.key_value
import dev.inmo.micro_utils.ktor.client.encodeUrlQueryValue import dev.inmo.micro_utils.ktor.client.*
import dev.inmo.micro_utils.ktor.client.uniget import dev.inmo.micro_utils.ktor.common.*
import dev.inmo.micro_utils.ktor.common.buildStandardUrl
import dev.inmo.micro_utils.pagination.* import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.ReadStandardKeyValueRepo import dev.inmo.micro_utils.repos.ReadStandardKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.key_value.* 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.valueParameterName
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
import kotlinx.serialization.KSerializer import kotlinx.serialization.*
import kotlinx.serialization.builtins.serializer import kotlinx.serialization.builtins.serializer
class KtorReadStandardKeyValueRepo<Key, Value> ( class KtorReadStandardKeyValueRepo<Key, Value> (
private var baseUrl: String, private val baseUrl: String,
private var client: HttpClient = HttpClient(), private val unifiedRequester: UnifiedRequester,
private var keySerializer: KSerializer<Key>, private val keySerializer: KSerializer<Key>,
private var valueSerializer: KSerializer<Value>, private val valueSerializer: KSerializer<Value>,
private var valueNullableSerializer: KSerializer<Value?>, private val valueNullableSerializer: KSerializer<Value?>
) : ReadStandardKeyValueRepo<Key, Value> { ) : ReadStandardKeyValueRepo<Key, Value> {
override suspend fun get(k: Key): Value? = client.uniget( constructor(
baseUrl: String,
client: HttpClient,
keySerializer: KSerializer<Key>,
valueSerializer: KSerializer<Value>,
valueNullableSerializer: KSerializer<Value?>,
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
) : this (
baseUrl, UnifiedRequester(client, serialFormat), keySerializer, valueSerializer, valueNullableSerializer
)
override suspend fun get(k: Key): Value? = unifiedRequester.uniget(
buildStandardUrl( buildStandardUrl(
baseUrl, baseUrl,
getRoute, getRoute,
mapOf( mapOf(
keyParameterName to keySerializer.encodeUrlQueryValue(k) keyParameterName to unifiedRequester.encodeUrlQueryValue(keySerializer, k)
) )
), ),
valueNullableSerializer valueNullableSerializer
) )
override suspend fun values(pagination: Pagination, reversed: Boolean): PaginationResult<Value> = client.uniget( override suspend fun values(pagination: Pagination, reversed: Boolean): PaginationResult<Value> = unifiedRequester.uniget(
buildStandardUrl( buildStandardUrl(
baseUrl, baseUrl,
valuesRoute, valuesRoute,
mapOf( mapOf(
reversedParameterName to Boolean.serializer().encodeUrlQueryValue(reversed) reversedParameterName to unifiedRequester.encodeUrlQueryValue(Boolean.serializer(), reversed)
) + pagination.asUrlQueryParts ) + pagination.asUrlQueryParts
), ),
PaginationResult.serializer(valueSerializer) PaginationResult.serializer(valueSerializer)
) )
override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<Key> = client.uniget( override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<Key> = unifiedRequester.uniget(
buildStandardUrl( buildStandardUrl(
baseUrl, baseUrl,
keysRoute, keysRoute,
mapOf( mapOf(
reversedParameterName to Boolean.serializer().encodeUrlQueryValue(reversed) reversedParameterName to unifiedRequester.encodeUrlQueryValue(Boolean.serializer(), reversed)
) + pagination.asUrlQueryParts ) + pagination.asUrlQueryParts
), ),
PaginationResult.serializer(keySerializer) PaginationResult.serializer(keySerializer)
) )
override suspend fun keys(v: Value, pagination: Pagination, reversed: Boolean): PaginationResult<Key> = client.uniget( override suspend fun keys(v: Value, pagination: Pagination, reversed: Boolean): PaginationResult<Key> = unifiedRequester.uniget(
buildStandardUrl( buildStandardUrl(
baseUrl, baseUrl,
keysRoute, keysRoute,
mapOf( mapOf(
valueParameterName to valueSerializer.encodeUrlQueryValue(v), valueParameterName to unifiedRequester.encodeUrlQueryValue(valueSerializer, v),
reversedParameterName to Boolean.serializer().encodeUrlQueryValue(reversed) reversedParameterName to unifiedRequester.encodeUrlQueryValue(Boolean.serializer(), reversed)
) + pagination.asUrlQueryParts ) + pagination.asUrlQueryParts
), ),
PaginationResult.serializer(keySerializer) PaginationResult.serializer(keySerializer)
) )
override suspend fun contains(key: Key): Boolean = client.uniget( override suspend fun contains(key: Key): Boolean = unifiedRequester.uniget(
buildStandardUrl( buildStandardUrl(
baseUrl, baseUrl,
containsRoute, containsRoute,
mapOf( mapOf(
keyParameterName to keySerializer.encodeUrlQueryValue(key) keyParameterName to unifiedRequester.encodeUrlQueryValue(keySerializer, key)
), ),
), ),
Boolean.serializer(), Boolean.serializer(),
) )
override suspend fun count(): Long = client.uniget( override suspend fun count(): Long = unifiedRequester.uniget(
buildStandardUrl( buildStandardUrl(
baseUrl, baseUrl,
countRoute, countRoute,

View File

@@ -1,27 +1,40 @@
package dev.inmo.micro_utils.repos.ktor.client.key_value package dev.inmo.micro_utils.repos.ktor.client.key_value
import dev.inmo.micro_utils.ktor.client.UnifiedRequester
import dev.inmo.micro_utils.ktor.common.StandardKtorSerialFormat
import dev.inmo.micro_utils.ktor.common.standardKtorSerialFormat
import dev.inmo.micro_utils.repos.* import dev.inmo.micro_utils.repos.*
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
import kotlinx.serialization.KSerializer import kotlinx.serialization.*
class KtorStandartKeyValueRepo<K, V> ( class KtorStandartKeyValueRepo<K, V> (
baseUrl: String, baseUrl: String,
baseSubpart: String, baseSubpart: String,
client: HttpClient = HttpClient(), unifiedRequester: UnifiedRequester,
keySerializer: KSerializer<K>, keySerializer: KSerializer<K>,
valueSerializer: KSerializer<V>, valueSerializer: KSerializer<V>,
valueNullableSerializer: KSerializer<V?> valueNullableSerializer: KSerializer<V?>
) : StandardKeyValueRepo<K, V>, ) : StandardKeyValueRepo<K, V>,
ReadStandardKeyValueRepo<K, V> by KtorReadStandardKeyValueRepo( ReadStandardKeyValueRepo<K, V> by KtorReadStandardKeyValueRepo(
"$baseUrl/$baseSubpart", "$baseUrl/$baseSubpart",
client, unifiedRequester,
keySerializer, keySerializer,
valueSerializer, valueSerializer,
valueNullableSerializer valueNullableSerializer
), ),
WriteStandardKeyValueRepo<K, V> by KtorWriteStandardKeyValueRepo( WriteStandardKeyValueRepo<K, V> by KtorWriteStandardKeyValueRepo(
"$baseUrl/$baseSubpart", "$baseUrl/$baseSubpart",
client, unifiedRequester,
keySerializer, keySerializer,
valueSerializer valueSerializer
) ) {
constructor(
baseUrl: String,
baseSubpart: String,
client: HttpClient = HttpClient(),
keySerializer: KSerializer<K>,
valueSerializer: KSerializer<V>,
valueNullableSerializer: KSerializer<V?>,
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
) : this(baseUrl, baseSubpart, UnifiedRequester(client, serialFormat), keySerializer, valueSerializer, valueNullableSerializer)
}

View File

@@ -1,7 +1,7 @@
package dev.inmo.micro_utils.repos.ktor.client.key_value package dev.inmo.micro_utils.repos.ktor.client.key_value
import dev.inmo.micro_utils.ktor.client.* import dev.inmo.micro_utils.ktor.client.*
import dev.inmo.micro_utils.ktor.common.buildStandardUrl import dev.inmo.micro_utils.ktor.common.*
import dev.inmo.micro_utils.repos.WriteStandardKeyValueRepo import dev.inmo.micro_utils.repos.WriteStandardKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.key_value.* import dev.inmo.micro_utils.repos.ktor.common.key_value.*
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
@@ -11,23 +11,34 @@ import kotlinx.serialization.builtins.*
class KtorWriteStandardKeyValueRepo<K, V> ( class KtorWriteStandardKeyValueRepo<K, V> (
private var baseUrl: String, private var baseUrl: String,
private var client: HttpClient = HttpClient(), private var unifiedRequester: UnifiedRequester,
private var keySerializer: KSerializer<K>, private var keySerializer: KSerializer<K>,
private var valueSerializer: KSerializer<V>, private var valueSerializer: KSerializer<V>,
) : WriteStandardKeyValueRepo<K, V> { ) : WriteStandardKeyValueRepo<K, V> {
private val keyValueMapSerializer = MapSerializer(keySerializer, valueSerializer) private val keyValueMapSerializer = MapSerializer(keySerializer, valueSerializer)
private val keysListSerializer = ListSerializer(keySerializer) private val keysListSerializer = ListSerializer(keySerializer)
override val onNewValue: Flow<Pair<K, V>> = client.createStandardWebsocketFlow(
constructor(
baseUrl: String,
client: HttpClient,
keySerializer: KSerializer<K>,
valueSerializer: KSerializer<V>,
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
) : this (
baseUrl, UnifiedRequester(client, serialFormat), keySerializer, valueSerializer
)
override val onNewValue: Flow<Pair<K, V>> = unifiedRequester.createStandardWebsocketFlow(
buildStandardUrl(baseUrl, onNewValueRoute), buildStandardUrl(baseUrl, onNewValueRoute),
deserializer = PairSerializer(keySerializer, valueSerializer) deserializer = PairSerializer(keySerializer, valueSerializer)
) )
override val onValueRemoved: Flow<K> = client.createStandardWebsocketFlow( override val onValueRemoved: Flow<K> = unifiedRequester.createStandardWebsocketFlow(
buildStandardUrl(baseUrl, onValueRemovedRoute), buildStandardUrl(baseUrl, onValueRemovedRoute),
deserializer = keySerializer deserializer = keySerializer
) )
override suspend fun set(toSet: Map<K, V>) = client.unipost( override suspend fun set(toSet: Map<K, V>) = unifiedRequester.unipost(
buildStandardUrl( buildStandardUrl(
baseUrl, baseUrl,
setRoute setRoute
@@ -36,7 +47,7 @@ class KtorWriteStandardKeyValueRepo<K, V> (
Unit.serializer() Unit.serializer()
) )
override suspend fun unset(toUnset: List<K>) = client.unipost( override suspend fun unset(toUnset: List<K>) = unifiedRequester.unipost(
buildStandardUrl( buildStandardUrl(
baseUrl, baseUrl,
unsetRoute, unsetRoute,

View File

@@ -1,5 +1,8 @@
package dev.inmo.micro_utils.repos.ktor.client.one_to_many package dev.inmo.micro_utils.repos.ktor.client.one_to_many
import dev.inmo.micro_utils.ktor.client.UnifiedRequester
import dev.inmo.micro_utils.ktor.common.StandardKtorSerialFormat
import dev.inmo.micro_utils.ktor.common.standardKtorSerialFormat
import dev.inmo.micro_utils.repos.* import dev.inmo.micro_utils.repos.*
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
import kotlinx.serialization.KSerializer import kotlinx.serialization.KSerializer
@@ -7,19 +10,28 @@ import kotlinx.serialization.KSerializer
class KtorOneToManyKeyValueRepo<Key, Value>( class KtorOneToManyKeyValueRepo<Key, Value>(
baseUrl: String, baseUrl: String,
baseSubpart: String, baseSubpart: String,
client: HttpClient, unifiedRequester: UnifiedRequester,
keySerializer: KSerializer<Key>, keySerializer: KSerializer<Key>,
valueSerializer: KSerializer<Value>, valueSerializer: KSerializer<Value>,
) : OneToManyKeyValueRepo<Key, Value>, ) : OneToManyKeyValueRepo<Key, Value>,
ReadOneToManyKeyValueRepo<Key, Value> by KtorReadOneToManyKeyValueRepo<Key, Value> ( ReadOneToManyKeyValueRepo<Key, Value> by KtorReadOneToManyKeyValueRepo<Key, Value> (
"$baseUrl/$baseSubpart", "$baseUrl/$baseSubpart",
client, unifiedRequester,
keySerializer, keySerializer,
valueSerializer, valueSerializer,
), ),
WriteOneToManyKeyValueRepo<Key, Value> by KtorWriteOneToManyKeyValueRepo<Key, Value> ( WriteOneToManyKeyValueRepo<Key, Value> by KtorWriteOneToManyKeyValueRepo<Key, Value> (
"$baseUrl/$baseSubpart", "$baseUrl/$baseSubpart",
client, unifiedRequester,
keySerializer, keySerializer,
valueSerializer, valueSerializer,
) ) {
constructor(
baseUrl: String,
baseSubpart: String,
client: HttpClient,
keySerializer: KSerializer<Key>,
valueSerializer: KSerializer<Value>,
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
) : this (baseUrl, baseSubpart, UnifiedRequester(client, serialFormat), keySerializer, valueSerializer)
}

View File

@@ -1,8 +1,7 @@
package dev.inmo.micro_utils.repos.ktor.client.one_to_many package dev.inmo.micro_utils.repos.ktor.client.one_to_many
import dev.inmo.micro_utils.ktor.client.encodeUrlQueryValue import dev.inmo.micro_utils.ktor.client.*
import dev.inmo.micro_utils.ktor.client.uniget import dev.inmo.micro_utils.ktor.common.*
import dev.inmo.micro_utils.ktor.common.buildStandardUrl
import dev.inmo.micro_utils.pagination.* import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.ReadOneToManyKeyValueRepo import dev.inmo.micro_utils.repos.ReadOneToManyKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.keyParameterName import dev.inmo.micro_utils.repos.ktor.common.keyParameterName
@@ -15,81 +14,89 @@ import kotlinx.serialization.builtins.serializer
class KtorReadOneToManyKeyValueRepo<Key, Value> ( class KtorReadOneToManyKeyValueRepo<Key, Value> (
private val baseUrl: String, private val baseUrl: String,
private val client: HttpClient = HttpClient(), private val unifiedRequester: UnifiedRequester,
private val keySerializer: KSerializer<Key>, private val keySerializer: KSerializer<Key>,
private val valueSerializer: KSerializer<Value>, private val valueSerializer: KSerializer<Value>
) : ReadOneToManyKeyValueRepo<Key, Value> { ) : ReadOneToManyKeyValueRepo<Key, Value> {
private val paginationValueResultSerializer = PaginationResult.serializer(valueSerializer) private val paginationValueResultSerializer = PaginationResult.serializer(valueSerializer)
private val paginationKeyResultSerializer = PaginationResult.serializer(keySerializer) private val paginationKeyResultSerializer = PaginationResult.serializer(keySerializer)
override suspend fun get(k: Key, pagination: Pagination, reversed: Boolean): PaginationResult<Value> = client.uniget( constructor(
baseUrl: String,
client: HttpClient,
keySerializer: KSerializer<Key>,
valueSerializer: KSerializer<Value>,
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
) : this (baseUrl, UnifiedRequester(client, serialFormat), keySerializer, valueSerializer)
override suspend fun get(k: Key, pagination: Pagination, reversed: Boolean): PaginationResult<Value> = unifiedRequester.uniget(
buildStandardUrl( buildStandardUrl(
baseUrl, baseUrl,
getRoute, getRoute,
mapOf( mapOf(
keyParameterName to keySerializer.encodeUrlQueryValue(k), keyParameterName to unifiedRequester.encodeUrlQueryValue(keySerializer, k),
reversedParameterName to Boolean.serializer().encodeUrlQueryValue(reversed) reversedParameterName to unifiedRequester.encodeUrlQueryValue(Boolean.serializer(), reversed)
) + pagination.asUrlQueryParts ) + pagination.asUrlQueryParts
), ),
paginationValueResultSerializer paginationValueResultSerializer
) )
override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<Key> = client.uniget( override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<Key> = unifiedRequester.uniget(
buildStandardUrl( buildStandardUrl(
baseUrl, baseUrl,
keysRoute, keysRoute,
mapOf( mapOf(
reversedParameterName to Boolean.serializer().encodeUrlQueryValue(reversed) reversedParameterName to unifiedRequester.encodeUrlQueryValue(Boolean.serializer(), reversed)
) + pagination.asUrlQueryParts ) + pagination.asUrlQueryParts
), ),
paginationKeyResultSerializer paginationKeyResultSerializer
) )
override suspend fun keys(v: Value, pagination: Pagination, reversed: Boolean): PaginationResult<Key> = client.uniget( override suspend fun keys(v: Value, pagination: Pagination, reversed: Boolean): PaginationResult<Key> = unifiedRequester.uniget(
buildStandardUrl( buildStandardUrl(
baseUrl, baseUrl,
keysRoute, keysRoute,
mapOf( mapOf(
valueParameterName to valueSerializer.encodeUrlQueryValue(v), valueParameterName to unifiedRequester.encodeUrlQueryValue(valueSerializer, v),
reversedParameterName to Boolean.serializer().encodeUrlQueryValue(reversed) reversedParameterName to unifiedRequester.encodeUrlQueryValue(Boolean.serializer(), reversed)
) + pagination.asUrlQueryParts ) + pagination.asUrlQueryParts
), ),
paginationKeyResultSerializer paginationKeyResultSerializer
) )
override suspend fun contains(k: Key): Boolean = client.uniget( override suspend fun contains(k: Key): Boolean = unifiedRequester.uniget(
buildStandardUrl( buildStandardUrl(
baseUrl, baseUrl,
containsByKeyRoute, containsByKeyRoute,
mapOf(keyParameterName to keySerializer.encodeUrlQueryValue(k)) mapOf(keyParameterName to unifiedRequester.encodeUrlQueryValue(keySerializer, k))
), ),
Boolean.serializer() Boolean.serializer()
) )
override suspend fun contains(k: Key, v: Value): Boolean = client.uniget( override suspend fun contains(k: Key, v: Value): Boolean = unifiedRequester.uniget(
buildStandardUrl( buildStandardUrl(
baseUrl, baseUrl,
containsByKeyValueRoute, containsByKeyValueRoute,
mapOf( mapOf(
keyParameterName to keySerializer.encodeUrlQueryValue(k), keyParameterName to unifiedRequester.encodeUrlQueryValue(keySerializer, k),
valueParameterName to valueSerializer.encodeUrlQueryValue(v), valueParameterName to unifiedRequester.encodeUrlQueryValue(valueSerializer, v),
) )
), ),
Boolean.serializer() Boolean.serializer()
) )
override suspend fun count(k: Key): Long = client.uniget( override suspend fun count(k: Key): Long = unifiedRequester.uniget(
buildStandardUrl( buildStandardUrl(
baseUrl, baseUrl,
countByKeyRoute, countByKeyRoute,
mapOf( mapOf(
keyParameterName to keySerializer.encodeUrlQueryValue(k) keyParameterName to unifiedRequester.encodeUrlQueryValue(keySerializer, k)
) )
), ),
Long.serializer() Long.serializer()
) )
override suspend fun count(): Long = client.uniget( override suspend fun count(): Long = unifiedRequester.uniget(
buildStandardUrl( buildStandardUrl(
baseUrl, baseUrl,
countRoute, countRoute,

View File

@@ -1,7 +1,7 @@
package dev.inmo.micro_utils.repos.ktor.client.one_to_many package dev.inmo.micro_utils.repos.ktor.client.one_to_many
import dev.inmo.micro_utils.ktor.client.* import dev.inmo.micro_utils.ktor.client.*
import dev.inmo.micro_utils.ktor.common.buildStandardUrl import dev.inmo.micro_utils.ktor.common.*
import dev.inmo.micro_utils.repos.WriteOneToManyKeyValueRepo import dev.inmo.micro_utils.repos.WriteOneToManyKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.one_to_many.* import dev.inmo.micro_utils.repos.ktor.common.one_to_many.*
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
@@ -11,26 +11,37 @@ import kotlinx.serialization.builtins.*
class KtorWriteOneToManyKeyValueRepo<Key, Value> ( class KtorWriteOneToManyKeyValueRepo<Key, Value> (
private val baseUrl: String, private val baseUrl: String,
private val client: HttpClient = HttpClient(), private val unifiedRequester: UnifiedRequester,
private val keySerializer: KSerializer<Key>, private val keySerializer: KSerializer<Key>,
private val valueSerializer: KSerializer<Value> private val valueSerializer: KSerializer<Value>
) : WriteOneToManyKeyValueRepo<Key, Value> { ) : WriteOneToManyKeyValueRepo<Key, Value> {
private val keyValueSerializer = PairSerializer(keySerializer, valueSerializer) private val keyValueSerializer = PairSerializer(keySerializer, valueSerializer)
private val keyValueMapSerializer = MapSerializer(keySerializer, ListSerializer(valueSerializer)) private val keyValueMapSerializer = MapSerializer(keySerializer, ListSerializer(valueSerializer))
override val onNewValue: Flow<Pair<Key, Value>> = client.createStandardWebsocketFlow(
constructor(
baseUrl: String,
client: HttpClient,
keySerializer: KSerializer<Key>,
valueSerializer: KSerializer<Value>,
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
) : this (
baseUrl, UnifiedRequester(client, serialFormat), keySerializer, valueSerializer
)
override val onNewValue: Flow<Pair<Key, Value>> = unifiedRequester.createStandardWebsocketFlow(
buildStandardUrl(baseUrl, onNewValueRoute), buildStandardUrl(baseUrl, onNewValueRoute),
deserializer = keyValueSerializer deserializer = keyValueSerializer
) )
override val onValueRemoved: Flow<Pair<Key, Value>> = client.createStandardWebsocketFlow( override val onValueRemoved: Flow<Pair<Key, Value>> = unifiedRequester.createStandardWebsocketFlow(
buildStandardUrl(baseUrl, onValueRemovedRoute), buildStandardUrl(baseUrl, onValueRemovedRoute),
deserializer = keyValueSerializer deserializer = keyValueSerializer
) )
override val onDataCleared: Flow<Key> = client.createStandardWebsocketFlow( override val onDataCleared: Flow<Key> = unifiedRequester.createStandardWebsocketFlow(
buildStandardUrl(baseUrl, onDataClearedRoute), buildStandardUrl(baseUrl, onDataClearedRoute),
deserializer = keySerializer deserializer = keySerializer
) )
override suspend fun remove(toRemove: Map<Key, List<Value>>) = client.unipost( override suspend fun remove(toRemove: Map<Key, List<Value>>) = unifiedRequester.unipost(
buildStandardUrl( buildStandardUrl(
baseUrl, baseUrl,
removeRoute, removeRoute,
@@ -39,15 +50,15 @@ class KtorWriteOneToManyKeyValueRepo<Key, Value> (
Unit.serializer(), Unit.serializer(),
) )
override suspend fun add(toAdd: Map<Key, List<Value>>) = client.unipost( override suspend fun add(toAdd: Map<Key, List<Value>>) = unifiedRequester.unipost(
buildStandardUrl( buildStandardUrl(
baseUrl, baseUrl,
clearRoute, addRoute,
), ),
BodyPair(keyValueMapSerializer, toAdd), BodyPair(keyValueMapSerializer, toAdd),
Unit.serializer(), Unit.serializer(),
) )
override suspend fun clear(k: Key) = client.unipost( override suspend fun clear(k: Key) = unifiedRequester.unipost(
buildStandardUrl( buildStandardUrl(
baseUrl, baseUrl,
clearRoute, clearRoute,
@@ -55,4 +66,13 @@ class KtorWriteOneToManyKeyValueRepo<Key, Value> (
BodyPair(keySerializer, k), BodyPair(keySerializer, k),
Unit.serializer(), Unit.serializer(),
) )
override suspend fun set(toSet: Map<Key, List<Value>>) = unifiedRequester.unipost(
buildStandardUrl(
baseUrl,
setRoute,
),
BodyPair(keyValueMapSerializer, toSet),
Unit.serializer(),
)
} }

View File

@@ -2,7 +2,6 @@ plugins {
id "org.jetbrains.kotlin.multiplatform" id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization" id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library" id "com.android.library"
id "kotlin-android-extensions"
} }
apply from: "$mppProjectWithSerializationPresetPath" apply from: "$mppProjectWithSerializationPresetPath"

View File

@@ -13,4 +13,5 @@ const val onDataClearedRoute = "onDataCleared"
const val addRoute = "add" const val addRoute = "add"
const val removeRoute = "remove" const val removeRoute = "remove"
const val clearRoute = "clear" const val clearRoute = "clear"
const val setRoute = "set"

View File

@@ -1,12 +1,14 @@
package dev.inmo.micro_utils.repos.ktor.server.crud package dev.inmo.micro_utils.repos.ktor.server.crud
import dev.inmo.micro_utils.ktor.server.decodeUrlQueryValueOrSendError import dev.inmo.micro_utils.ktor.common.StandardKtorSerialFormat
import dev.inmo.micro_utils.ktor.server.unianswer import dev.inmo.micro_utils.ktor.common.standardKtorSerialFormat
import dev.inmo.micro_utils.ktor.server.*
import dev.inmo.micro_utils.pagination.PaginationResult import dev.inmo.micro_utils.pagination.PaginationResult
import dev.inmo.micro_utils.pagination.extractPagination import dev.inmo.micro_utils.pagination.extractPagination
import dev.inmo.micro_utils.repos.ReadStandardCRUDRepo import dev.inmo.micro_utils.repos.ReadStandardCRUDRepo
import dev.inmo.micro_utils.repos.ktor.common.crud.* import dev.inmo.micro_utils.repos.ktor.common.crud.*
import io.ktor.application.call import io.ktor.application.call
import io.ktor.http.ContentType
import io.ktor.routing.Route import io.ktor.routing.Route
import io.ktor.routing.get import io.ktor.routing.get
import kotlinx.serialization.KSerializer import kotlinx.serialization.KSerializer
@@ -16,47 +18,65 @@ fun <ObjectType, IdType> Route.configureReadStandardCrudRepoRoutes(
originalRepo: ReadStandardCRUDRepo<ObjectType, IdType>, originalRepo: ReadStandardCRUDRepo<ObjectType, IdType>,
objectsSerializer: KSerializer<ObjectType>, objectsSerializer: KSerializer<ObjectType>,
objectsNullableSerializer: KSerializer<ObjectType?>, objectsNullableSerializer: KSerializer<ObjectType?>,
idsSerializer: KSerializer<IdType> idsSerializer: KSerializer<IdType>,
unifiedRouter: UnifiedRouter
) { ) {
val paginationResultSerializer = PaginationResult.serializer(objectsSerializer) val paginationResultSerializer = PaginationResult.serializer(objectsSerializer)
get(getByPaginationRouting) { get(getByPaginationRouting) {
val pagination = call.request.queryParameters.extractPagination unifiedRouter.apply {
val pagination = call.request.queryParameters.extractPagination
call.unianswer( unianswer(
paginationResultSerializer, paginationResultSerializer,
originalRepo.getByPagination(pagination) originalRepo.getByPagination(pagination)
) )
}
} }
get(getByIdRouting) { get(getByIdRouting) {
val id = call.decodeUrlQueryValueOrSendError( unifiedRouter.apply {
"id", val id = decodeUrlQueryValueOrSendError(
idsSerializer "id",
) ?: return@get idsSerializer
) ?: return@get
call.unianswer( unianswer(
objectsNullableSerializer, objectsNullableSerializer,
originalRepo.getById(id) originalRepo.getById(id)
) )
}
} }
get(containsRouting) { get(containsRouting) {
val id = call.decodeUrlQueryValueOrSendError( unifiedRouter.apply {
"id", val id = decodeUrlQueryValueOrSendError(
idsSerializer "id",
) ?: return@get idsSerializer
) ?: return@get
call.unianswer( unianswer(
Boolean.serializer(), Boolean.serializer(),
originalRepo.contains(id) originalRepo.contains(id)
) )
}
} }
get(countRouting) { get(countRouting) {
call.unianswer( unifiedRouter.apply {
Long.serializer(), unianswer(
originalRepo.count() Long.serializer(),
) originalRepo.count()
)
}
} }
} }
inline fun <ObjectType, IdType> Route.configureReadStandardCrudRepoRoutes(
originalRepo: ReadStandardCRUDRepo<ObjectType, IdType>,
objectsSerializer: KSerializer<ObjectType>,
objectsNullableSerializer: KSerializer<ObjectType?>,
idsSerializer: KSerializer<IdType>,
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat,
serialFormatContentType: ContentType = standardKtorSerialFormatContentType
) = configureReadStandardCrudRepoRoutes(originalRepo, objectsSerializer, objectsNullableSerializer, idsSerializer, UnifiedRouter(serialFormat, serialFormatContentType))

View File

@@ -1,6 +1,11 @@
package dev.inmo.micro_utils.repos.ktor.server.crud package dev.inmo.micro_utils.repos.ktor.server.crud
import dev.inmo.micro_utils.ktor.common.StandardKtorSerialFormat
import dev.inmo.micro_utils.ktor.common.standardKtorSerialFormat
import dev.inmo.micro_utils.ktor.server.UnifiedRouter
import dev.inmo.micro_utils.ktor.server.standardKtorSerialFormatContentType
import dev.inmo.micro_utils.repos.StandardCRUDRepo import dev.inmo.micro_utils.repos.StandardCRUDRepo
import io.ktor.http.ContentType
import io.ktor.routing.Route import io.ktor.routing.Route
import io.ktor.routing.route import io.ktor.routing.route
import kotlinx.serialization.KSerializer import kotlinx.serialization.KSerializer
@@ -11,10 +16,24 @@ fun <ObjectType, IdType, InputValue> Route.configureStandardCrudRepoRoutes(
objectsSerializer: KSerializer<ObjectType>, objectsSerializer: KSerializer<ObjectType>,
objectsNullableSerializer: KSerializer<ObjectType?>, objectsNullableSerializer: KSerializer<ObjectType?>,
inputsSerializer: KSerializer<InputValue>, inputsSerializer: KSerializer<InputValue>,
idsSerializer: KSerializer<IdType> idsSerializer: KSerializer<IdType>,
unifiedRouter: UnifiedRouter
) { ) {
route(baseSubpart) { route(baseSubpart) {
configureReadStandardCrudRepoRoutes(originalRepo, objectsSerializer, objectsNullableSerializer, idsSerializer) configureReadStandardCrudRepoRoutes(originalRepo, objectsSerializer, objectsNullableSerializer, idsSerializer, unifiedRouter)
configureWriteStandardCrudRepoRoutes(originalRepo, objectsSerializer, objectsNullableSerializer, inputsSerializer, idsSerializer) configureWriteStandardCrudRepoRoutes(originalRepo, objectsSerializer, objectsNullableSerializer, inputsSerializer, idsSerializer, unifiedRouter)
} }
} }
fun <ObjectType, IdType, InputValue> Route.configureStandardCrudRepoRoutes(
baseSubpart: String,
originalRepo: StandardCRUDRepo<ObjectType, IdType, InputValue>,
objectsSerializer: KSerializer<ObjectType>,
objectsNullableSerializer: KSerializer<ObjectType?>,
inputsSerializer: KSerializer<InputValue>,
idsSerializer: KSerializer<IdType>,
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat,
serialFormatContentType: ContentType = standardKtorSerialFormatContentType
) = configureStandardCrudRepoRoutes(
baseSubpart, originalRepo, objectsSerializer, objectsNullableSerializer, inputsSerializer, idsSerializer, UnifiedRouter(serialFormat, serialFormatContentType)
)

View File

@@ -1,9 +1,12 @@
package dev.inmo.micro_utils.repos.ktor.server.crud package dev.inmo.micro_utils.repos.ktor.server.crud
import dev.inmo.micro_utils.ktor.common.StandardKtorSerialFormat
import dev.inmo.micro_utils.ktor.common.standardKtorSerialFormat
import dev.inmo.micro_utils.ktor.server.* import dev.inmo.micro_utils.ktor.server.*
import dev.inmo.micro_utils.repos.WriteStandardCRUDRepo import dev.inmo.micro_utils.repos.WriteStandardCRUDRepo
import dev.inmo.micro_utils.repos.ktor.common.crud.* import dev.inmo.micro_utils.repos.ktor.common.crud.*
import io.ktor.application.call import io.ktor.application.call
import io.ktor.http.ContentType
import io.ktor.routing.Route import io.ktor.routing.Route
import io.ktor.routing.post import io.ktor.routing.post
import kotlinx.serialization.KSerializer import kotlinx.serialization.KSerializer
@@ -14,7 +17,8 @@ fun <ObjectType, IdType, InputValue> Route.configureWriteStandardCrudRepoRoutes(
objectsSerializer: KSerializer<ObjectType>, objectsSerializer: KSerializer<ObjectType>,
objectsNullableSerializer: KSerializer<ObjectType?>, objectsNullableSerializer: KSerializer<ObjectType?>,
inputsSerializer: KSerializer<InputValue>, inputsSerializer: KSerializer<InputValue>,
idsSerializer: KSerializer<IdType> idsSerializer: KSerializer<IdType>,
unifiedRouter: UnifiedRouter
) { ) {
val listObjectsSerializer = ListSerializer(objectsSerializer) val listObjectsSerializer = ListSerializer(objectsSerializer)
val listInputSerializer = ListSerializer(inputsSerializer) val listInputSerializer = ListSerializer(inputsSerializer)
@@ -25,58 +29,80 @@ fun <ObjectType, IdType, InputValue> Route.configureWriteStandardCrudRepoRoutes(
) )
val listInputUpdateSerializer = ListSerializer(inputUpdateSerializer) val listInputUpdateSerializer = ListSerializer(inputUpdateSerializer)
includeWebsocketHandling( unifiedRouter.apply {
newObjectsFlowRouting, includeWebsocketHandling(
originalRepo.newObjectsFlow, newObjectsFlowRouting,
objectsSerializer originalRepo.newObjectsFlow,
) objectsSerializer
includeWebsocketHandling( )
updatedObjectsFlowRouting, includeWebsocketHandling(
originalRepo.updatedObjectsFlow, updatedObjectsFlowRouting,
objectsSerializer originalRepo.updatedObjectsFlow,
) objectsSerializer
includeWebsocketHandling( )
deletedObjectsIdsFlowRouting, includeWebsocketHandling(
originalRepo.deletedObjectsIdsFlow, deletedObjectsIdsFlowRouting,
idsSerializer originalRepo.deletedObjectsIdsFlow,
) idsSerializer
)
}
post(createRouting) { post(createRouting) {
call.unianswer( unifiedRouter.apply {
listObjectsSerializer, unianswer(
originalRepo.create( listObjectsSerializer,
call.uniload(listInputSerializer) originalRepo.create(
uniload(listInputSerializer)
)
) )
) }
} }
post(updateRouting) { post(updateRouting) {
val (id, input) = call.uniload(inputUpdateSerializer) unifiedRouter.apply {
call.unianswer( val (id, input) = uniload(inputUpdateSerializer)
objectsNullableSerializer, unianswer(
originalRepo.update( objectsNullableSerializer,
id, input originalRepo.update(
id, input
)
) )
) }
} }
post(updateManyRouting) { post(updateManyRouting) {
val updates = call.uniload(listInputUpdateSerializer) unifiedRouter.apply {
call.unianswer( val updates = uniload(listInputUpdateSerializer)
listObjectsSerializer, unianswer(
originalRepo.update( listObjectsSerializer,
updates originalRepo.update(
updates
)
) )
) }
} }
post(deleteByIdRouting) { post(deleteByIdRouting) {
val ids = call.uniload(listIdsSerializer) unifiedRouter.apply {
call.unianswer( val ids = uniload(listIdsSerializer)
Unit.serializer(), unianswer(
originalRepo.deleteById( Unit.serializer(),
ids originalRepo.deleteById(
ids
)
) )
) }
} }
} }
fun <ObjectType, IdType, InputValue> Route.configureWriteStandardCrudRepoRoutes(
originalRepo: WriteStandardCRUDRepo<ObjectType, IdType, InputValue>,
objectsSerializer: KSerializer<ObjectType>,
objectsNullableSerializer: KSerializer<ObjectType?>,
inputsSerializer: KSerializer<InputValue>,
idsSerializer: KSerializer<IdType>,
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat,
serialFormatContentType: ContentType = standardKtorSerialFormatContentType
) = configureWriteStandardCrudRepoRoutes(
originalRepo, objectsSerializer, objectsNullableSerializer, inputsSerializer, idsSerializer, UnifiedRouter(serialFormat, serialFormatContentType)
)

View File

@@ -1,16 +1,22 @@
package dev.inmo.micro_utils.repos.ktor.server.key_value package dev.inmo.micro_utils.repos.ktor.server.key_value
import dev.inmo.micro_utils.ktor.common.StandardKtorSerialFormat
import dev.inmo.micro_utils.ktor.common.standardKtorSerialFormat
import dev.inmo.micro_utils.ktor.server.UnifiedRouter
import dev.inmo.micro_utils.ktor.server.standardKtorSerialFormatContentType
import dev.inmo.micro_utils.repos.StandardKeyValueRepo import dev.inmo.micro_utils.repos.StandardKeyValueRepo
import io.ktor.http.ContentType
import io.ktor.routing.Route import io.ktor.routing.Route
import io.ktor.routing.route import io.ktor.routing.route
import kotlinx.serialization.KSerializer import kotlinx.serialization.KSerializer
fun <K, V> Route.configureStandartKeyValueRepoRoutes( fun <K, V> Route.configureStandardKeyValueRepoRoutes(
baseSubpart: String, baseSubpart: String,
originalRepo: StandardKeyValueRepo<K, V>, originalRepo: StandardKeyValueRepo<K, V>,
keySerializer: KSerializer<K>, keySerializer: KSerializer<K>,
valueSerializer: KSerializer<V>, valueSerializer: KSerializer<V>,
valueNullableSerializer: KSerializer<V?>, valueNullableSerializer: KSerializer<V?>,
unifiedRouter: UnifiedRouter
) { ) {
route(baseSubpart) { route(baseSubpart) {
configureReadStandartKeyValueRepoRoutes( configureReadStandartKeyValueRepoRoutes(
@@ -18,11 +24,23 @@ fun <K, V> Route.configureStandartKeyValueRepoRoutes(
keySerializer, keySerializer,
valueSerializer, valueSerializer,
valueNullableSerializer, valueNullableSerializer,
unifiedRouter
) )
configureWriteStandartKeyValueRepoRoutes( configureWriteStandardKeyValueRepoRoutes(
originalRepo, originalRepo,
keySerializer, keySerializer,
valueSerializer, valueSerializer,
unifiedRouter
) )
} }
} }
fun <K, V> Route.configureStandartKeyValueRepoRoutes(
baseSubpart: String,
originalRepo: StandardKeyValueRepo<K, V>,
keySerializer: KSerializer<K>,
valueSerializer: KSerializer<V>,
valueNullableSerializer: KSerializer<V?>,
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat,
serialFormatContentType: ContentType = standardKtorSerialFormatContentType
) = configureStandardKeyValueRepoRoutes(baseSubpart, originalRepo, keySerializer, valueSerializer, valueNullableSerializer, UnifiedRouter(serialFormat, serialFormatContentType))

View File

@@ -1,5 +1,7 @@
package dev.inmo.micro_utils.repos.ktor.server.key_value package dev.inmo.micro_utils.repos.ktor.server.key_value
import dev.inmo.micro_utils.ktor.common.StandardKtorSerialFormat
import dev.inmo.micro_utils.ktor.common.standardKtorSerialFormat
import dev.inmo.micro_utils.ktor.server.* import dev.inmo.micro_utils.ktor.server.*
import dev.inmo.micro_utils.pagination.PaginationResult import dev.inmo.micro_utils.pagination.PaginationResult
import dev.inmo.micro_utils.pagination.extractPagination import dev.inmo.micro_utils.pagination.extractPagination
@@ -7,6 +9,7 @@ import dev.inmo.micro_utils.repos.ReadStandardKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.key_value.* 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.valueParameterName
import io.ktor.application.call import io.ktor.application.call
import io.ktor.http.ContentType
import io.ktor.routing.Route import io.ktor.routing.Route
import io.ktor.routing.get import io.ktor.routing.get
import kotlinx.serialization.KSerializer import kotlinx.serialization.KSerializer
@@ -17,62 +20,84 @@ fun <K, V> Route.configureReadStandartKeyValueRepoRoutes (
keySerializer: KSerializer<K>, keySerializer: KSerializer<K>,
valueSerializer: KSerializer<V>, valueSerializer: KSerializer<V>,
valueNullableSerializer: KSerializer<V?>, valueNullableSerializer: KSerializer<V?>,
unifiedRouter: UnifiedRouter
) { ) {
get(getRoute) { get(getRoute) {
val key = call.decodeUrlQueryValueOrSendError( unifiedRouter.apply {
keyParameterName, val key = decodeUrlQueryValueOrSendError(
keySerializer keyParameterName,
) ?: return@get keySerializer
) ?: return@get
call.unianswer( unianswer(
valueNullableSerializer, valueNullableSerializer,
originalRepo.get(key) originalRepo.get(key)
) )
}
} }
get(valuesRoute) { get(valuesRoute) {
val parination = call.request.queryParameters.extractPagination; unifiedRouter.apply {
val reversed = call.decodeUrlQueryValueOrSendError( val parination = call.request.queryParameters.extractPagination;
reversedParameterName, val reversed = decodeUrlQueryValueOrSendError(
Boolean.serializer() reversedParameterName,
) ?: return@get Boolean.serializer()
) ?: return@get
call.unianswer( unianswer(
PaginationResult.serializer(valueSerializer), PaginationResult.serializer(valueSerializer),
originalRepo.values(parination, reversed) originalRepo.values(parination, reversed)
) )
}
} }
get(keysRoute) { get(keysRoute) {
val parination = call.request.queryParameters.extractPagination; unifiedRouter.apply {
val reversed = call.decodeUrlQueryValueOrSendError( val parination = call.request.queryParameters.extractPagination;
reversedParameterName, val reversed = decodeUrlQueryValueOrSendError(
Boolean.serializer() reversedParameterName,
) ?: return@get Boolean.serializer()
val value = call.decodeUrlQueryValue(valueParameterName, valueSerializer) ) ?: return@get
val value = decodeUrlQueryValue(valueParameterName, valueSerializer)
call.unianswer( unianswer(
PaginationResult.serializer(keySerializer), PaginationResult.serializer(keySerializer),
value ?.let { originalRepo.keys(value, parination, reversed) } ?: originalRepo.keys(parination, reversed) value?.let { originalRepo.keys(value, parination, reversed) } ?: originalRepo.keys(parination, reversed)
) )
}
} }
get(containsRoute) { get(containsRoute) {
val key = call.decodeUrlQueryValueOrSendError( unifiedRouter.apply {
keyParameterName, val key = decodeUrlQueryValueOrSendError(
keySerializer keyParameterName,
) ?: return@get keySerializer
) ?: return@get
call.unianswer( unianswer(
Boolean.serializer(), Boolean.serializer(),
originalRepo.contains(key) originalRepo.contains(key)
) )
}
} }
get(countRoute) { get(countRoute) {
call.unianswer( unifiedRouter.apply {
Long.serializer(), unianswer(
originalRepo.count() Long.serializer(),
) originalRepo.count()
)
}
} }
} }
inline fun <K, V> Route.configureReadStandartKeyValueRepoRoutes (
originalRepo: ReadStandardKeyValueRepo<K, V>,
keySerializer: KSerializer<K>,
valueSerializer: KSerializer<V>,
valueNullableSerializer: KSerializer<V?>,
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat,
serialFormatContentType: ContentType = standardKtorSerialFormatContentType
) = configureReadStandartKeyValueRepoRoutes(
originalRepo, keySerializer, valueSerializer, valueNullableSerializer, UnifiedRouter(serialFormat, serialFormatContentType)
)

View File

@@ -1,44 +1,61 @@
package dev.inmo.micro_utils.repos.ktor.server.key_value package dev.inmo.micro_utils.repos.ktor.server.key_value
import dev.inmo.micro_utils.ktor.common.StandardKtorSerialFormat
import dev.inmo.micro_utils.ktor.common.standardKtorSerialFormat
import dev.inmo.micro_utils.ktor.server.* import dev.inmo.micro_utils.ktor.server.*
import dev.inmo.micro_utils.repos.WriteStandardKeyValueRepo import dev.inmo.micro_utils.repos.WriteStandardKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.key_value.* import dev.inmo.micro_utils.repos.ktor.common.key_value.*
import io.ktor.application.call import io.ktor.http.ContentType
import io.ktor.routing.Route import io.ktor.routing.Route
import io.ktor.routing.post import io.ktor.routing.post
import kotlinx.serialization.KSerializer import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.* import kotlinx.serialization.builtins.*
fun <K, V> Route.configureWriteStandardKeyValueRepoRoutes (
originalRepo: WriteStandardKeyValueRepo<K, V>,
keySerializer: KSerializer<K>,
valueSerializer: KSerializer<V>,
unifiedRouter: UnifiedRouter
) {
val keyValueMapSerializer = MapSerializer(keySerializer, valueSerializer)
val keysListSerializer = ListSerializer(keySerializer)
unifiedRouter.apply {
includeWebsocketHandling(
onNewValueRoute,
originalRepo.onNewValue,
PairSerializer(keySerializer, valueSerializer)
)
includeWebsocketHandling(
onValueRemovedRoute,
originalRepo.onValueRemoved,
keySerializer
)
}
post(setRoute) {
unifiedRouter.apply {
val toSet = uniload(
keyValueMapSerializer
)
unianswer(Unit.serializer(), originalRepo.set(toSet))
}
}
post(unsetRoute) {
unifiedRouter.apply {
val toUnset = uniload(keysListSerializer)
unianswer(Unit.serializer(), originalRepo.unset(toUnset))
}
}
}
fun <K, V> Route.configureWriteStandartKeyValueRepoRoutes ( fun <K, V> Route.configureWriteStandartKeyValueRepoRoutes (
originalRepo: WriteStandardKeyValueRepo<K, V>, originalRepo: WriteStandardKeyValueRepo<K, V>,
keySerializer: KSerializer<K>, keySerializer: KSerializer<K>,
valueSerializer: KSerializer<V>, valueSerializer: KSerializer<V>,
) { serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat,
val keyValueMapSerializer = MapSerializer(keySerializer, valueSerializer) serialFormatContentType: ContentType = standardKtorSerialFormatContentType
val keysListSerializer = ListSerializer(keySerializer) ) = configureWriteStandardKeyValueRepoRoutes(originalRepo, keySerializer, valueSerializer, UnifiedRouter(serialFormat, serialFormatContentType))
includeWebsocketHandling(
onNewValueRoute,
originalRepo.onNewValue,
PairSerializer(keySerializer, valueSerializer)
)
includeWebsocketHandling(
onValueRemovedRoute,
originalRepo.onValueRemoved,
keySerializer
)
post(setRoute) {
val toSet = call.uniload(
keyValueMapSerializer
)
call.unianswer(Unit.serializer(), originalRepo.set(toSet))
}
post(unsetRoute) {
val toUnset = call.uniload(keysListSerializer)
call.unianswer(Unit.serializer(), originalRepo.unset(toUnset))
}
}

View File

@@ -1,6 +1,11 @@
package dev.inmo.micro_utils.repos.ktor.server.one_to_many package dev.inmo.micro_utils.repos.ktor.server.one_to_many
import dev.inmo.micro_utils.ktor.common.StandardKtorSerialFormat
import dev.inmo.micro_utils.ktor.common.standardKtorSerialFormat
import dev.inmo.micro_utils.ktor.server.UnifiedRouter
import dev.inmo.micro_utils.ktor.server.standardKtorSerialFormatContentType
import dev.inmo.micro_utils.repos.OneToManyKeyValueRepo import dev.inmo.micro_utils.repos.OneToManyKeyValueRepo
import io.ktor.http.ContentType
import io.ktor.routing.Route import io.ktor.routing.Route
import io.ktor.routing.route import io.ktor.routing.route
import kotlinx.serialization.KSerializer import kotlinx.serialization.KSerializer
@@ -9,10 +14,22 @@ fun <Key, Value> Route.configureOneToManyKeyValueRepoRoutes(
baseSubpart: String, baseSubpart: String,
originalRepo: OneToManyKeyValueRepo<Key, Value>, originalRepo: OneToManyKeyValueRepo<Key, Value>,
keySerializer: KSerializer<Key>, keySerializer: KSerializer<Key>,
valueSealizer: KSerializer<Value>, valueSerializer: KSerializer<Value>,
unifiedRouter: UnifiedRouter
) { ) {
route(baseSubpart) { route(baseSubpart) {
configureOneToManyReadKeyValueRepoRoutes(originalRepo, keySerializer, valueSealizer) configureOneToManyReadKeyValueRepoRoutes(originalRepo, keySerializer, valueSerializer, unifiedRouter)
configureOneToManyWriteKeyValueRepoRoutes(originalRepo, keySerializer, valueSealizer) configureOneToManyWriteKeyValueRepoRoutes(originalRepo, keySerializer, valueSerializer, unifiedRouter)
} }
} }
fun <Key, Value> Route.configureOneToManyKeyValueRepoRoutes(
baseSubpart: String,
originalRepo: OneToManyKeyValueRepo<Key, Value>,
keySerializer: KSerializer<Key>,
valueSerializer: KSerializer<Value>,
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat,
serialFormatContentType: ContentType = standardKtorSerialFormatContentType
) = configureOneToManyKeyValueRepoRoutes(
baseSubpart, originalRepo, keySerializer, valueSerializer, UnifiedRouter(serialFormat, serialFormatContentType)
)

View File

@@ -1,5 +1,7 @@
package dev.inmo.micro_utils.repos.ktor.server.one_to_many package dev.inmo.micro_utils.repos.ktor.server.one_to_many
import dev.inmo.micro_utils.ktor.common.StandardKtorSerialFormat
import dev.inmo.micro_utils.ktor.common.standardKtorSerialFormat
import dev.inmo.micro_utils.ktor.server.* import dev.inmo.micro_utils.ktor.server.*
import dev.inmo.micro_utils.pagination.PaginationResult import dev.inmo.micro_utils.pagination.PaginationResult
import dev.inmo.micro_utils.pagination.extractPagination import dev.inmo.micro_utils.pagination.extractPagination
@@ -10,6 +12,7 @@ import dev.inmo.micro_utils.repos.ktor.common.one_to_many.*
import dev.inmo.micro_utils.repos.ktor.common.valueParameterName import dev.inmo.micro_utils.repos.ktor.common.valueParameterName
import dev.inmo.micro_utils.repos.ktor.common.reversedParameterName import dev.inmo.micro_utils.repos.ktor.common.reversedParameterName
import io.ktor.application.call import io.ktor.application.call
import io.ktor.http.ContentType
import io.ktor.routing.Route import io.ktor.routing.Route
import io.ktor.routing.get import io.ktor.routing.get
import kotlinx.serialization.KSerializer import kotlinx.serialization.KSerializer
@@ -18,89 +21,110 @@ import kotlinx.serialization.builtins.serializer
fun <Key, Value> Route.configureOneToManyReadKeyValueRepoRoutes( fun <Key, Value> Route.configureOneToManyReadKeyValueRepoRoutes(
originalRepo: ReadOneToManyKeyValueRepo<Key, Value>, originalRepo: ReadOneToManyKeyValueRepo<Key, Value>,
keySerializer: KSerializer<Key>, keySerializer: KSerializer<Key>,
valueSealizer: KSerializer<Value>, valueSerializer: KSerializer<Value>,
unifiedRouter: UnifiedRouter
) { ) {
val paginationKeyResult = PaginationResult.serializer(keySerializer) val paginationKeyResult = PaginationResult.serializer(keySerializer)
val paginationValueResult = PaginationResult.serializer(valueSealizer) val paginationValueResult = PaginationResult.serializer(valueSerializer)
get(getRoute) { get(getRoute) {
val pagination = call.request.queryParameters.extractPagination unifiedRouter.apply {
val key = call.decodeUrlQueryValueOrSendError( val pagination = call.request.queryParameters.extractPagination
keyParameterName, val key = decodeUrlQueryValueOrSendError(
keySerializer keyParameterName,
) ?: return@get keySerializer
val reversed = call.decodeUrlQueryValue( ) ?: return@get
reversedParameterName, val reversed = decodeUrlQueryValue(
Boolean.serializer() reversedParameterName,
) ?: false Boolean.serializer()
) ?: false
call.unianswer( unianswer(
paginationValueResult, paginationValueResult,
originalRepo.get(key, pagination, reversed) originalRepo.get(key, pagination, reversed)
) )
}
} }
get(keysRoute) { get(keysRoute) {
val pagination = call.request.queryParameters.extractPagination unifiedRouter.apply {
val reversed = call.decodeUrlQueryValue( val pagination = call.request.queryParameters.extractPagination
reversedParameterName, val reversed = decodeUrlQueryValue(
Boolean.serializer() reversedParameterName,
) ?: false Boolean.serializer()
val value: Value? = call.decodeUrlQueryValue( ) ?: false
valueParameterName, val value: Value? = decodeUrlQueryValue(
valueSealizer valueParameterName,
) valueSerializer
)
call.unianswer( unianswer(
paginationKeyResult, paginationKeyResult,
value ?.let { originalRepo.keys(value, pagination, reversed) } ?: originalRepo.keys(pagination, reversed) value?.let { originalRepo.keys(value, pagination, reversed) } ?: originalRepo.keys(pagination, reversed)
) )
}
} }
get(containsByKeyRoute) { get(containsByKeyRoute) {
val key = call.decodeUrlQueryValueOrSendError( unifiedRouter.apply {
keyParameterName, val key = decodeUrlQueryValueOrSendError(
keySerializer keyParameterName,
) ?: return@get keySerializer
) ?: return@get
call.unianswer( unianswer(
Boolean.serializer(), Boolean.serializer(),
originalRepo.contains(key) originalRepo.contains(key)
) )
}
} }
get(containsByKeyValueRoute) { get(containsByKeyValueRoute) {
val key = call.decodeUrlQueryValueOrSendError( unifiedRouter.apply {
keyParameterName, val key = decodeUrlQueryValueOrSendError(
keySerializer keyParameterName,
) ?: return@get keySerializer
val value = call.decodeUrlQueryValueOrSendError( ) ?: return@get
valueParameterName, val value = decodeUrlQueryValueOrSendError(
valueSealizer valueParameterName,
) ?: return@get valueSerializer
) ?: return@get
call.unianswer( unianswer(
Boolean.serializer(), Boolean.serializer(),
originalRepo.contains(key, value) originalRepo.contains(key, value)
) )
}
} }
get(countByKeyRoute) { get(countByKeyRoute) {
val key = call.decodeUrlQueryValueOrSendError( unifiedRouter.apply {
keyParameterName, val key = decodeUrlQueryValueOrSendError(
keySerializer keyParameterName,
) ?: return@get keySerializer
) ?: return@get
call.unianswer( unianswer(
Long.serializer(), Long.serializer(),
originalRepo.count(key) originalRepo.count(key)
) )
}
} }
get(countRoute) { get(countRoute) {
call.unianswer( unifiedRouter.apply {
Long.serializer(), unianswer(
originalRepo.count() Long.serializer(),
) originalRepo.count()
)
}
} }
} }
inline fun <Key, Value> Route.configureOneToManyReadKeyValueRepoRoutes(
originalRepo: ReadOneToManyKeyValueRepo<Key, Value>,
keySerializer: KSerializer<Key>,
valueSerializer: KSerializer<Value>,
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat,
serialFormatContentType: ContentType = standardKtorSerialFormatContentType
) = configureOneToManyReadKeyValueRepoRoutes(originalRepo, keySerializer, valueSerializer, UnifiedRouter(serialFormat, serialFormatContentType))

View File

@@ -1,9 +1,12 @@
package dev.inmo.micro_utils.repos.ktor.server.one_to_many package dev.inmo.micro_utils.repos.ktor.server.one_to_many
import dev.inmo.micro_utils.ktor.common.StandardKtorSerialFormat
import dev.inmo.micro_utils.ktor.common.standardKtorSerialFormat
import dev.inmo.micro_utils.ktor.server.* import dev.inmo.micro_utils.ktor.server.*
import dev.inmo.micro_utils.repos.WriteOneToManyKeyValueRepo import dev.inmo.micro_utils.repos.WriteOneToManyKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.one_to_many.* import dev.inmo.micro_utils.repos.ktor.common.one_to_many.*
import io.ktor.application.call import io.ktor.application.call
import io.ktor.http.ContentType
import io.ktor.routing.Route import io.ktor.routing.Route
import io.ktor.routing.post import io.ktor.routing.post
import kotlinx.serialization.KSerializer import kotlinx.serialization.KSerializer
@@ -13,52 +16,78 @@ fun <Key, Value> Route.configureOneToManyWriteKeyValueRepoRoutes(
originalRepo: WriteOneToManyKeyValueRepo<Key, Value>, originalRepo: WriteOneToManyKeyValueRepo<Key, Value>,
keySerializer: KSerializer<Key>, keySerializer: KSerializer<Key>,
valueSerializer: KSerializer<Value>, valueSerializer: KSerializer<Value>,
unifiedRouter: UnifiedRouter
) { ) {
val keyValueSerializer = PairSerializer(keySerializer, valueSerializer) val keyValueSerializer = PairSerializer(keySerializer, valueSerializer)
val keyValueMapSerializer = MapSerializer(keySerializer, ListSerializer(valueSerializer)) val keyValueMapSerializer = MapSerializer(keySerializer, ListSerializer(valueSerializer))
includeWebsocketHandling( unifiedRouter.apply {
onNewValueRoute, includeWebsocketHandling(
originalRepo.onNewValue, onNewValueRoute,
keyValueSerializer originalRepo.onNewValue,
) keyValueSerializer
includeWebsocketHandling( )
onValueRemovedRoute, includeWebsocketHandling(
originalRepo.onValueRemoved, onValueRemovedRoute,
keyValueSerializer originalRepo.onValueRemoved,
) keyValueSerializer
includeWebsocketHandling( )
onDataClearedRoute, includeWebsocketHandling(
originalRepo.onDataCleared, onDataClearedRoute,
keySerializer originalRepo.onDataCleared,
) keySerializer
)
}
post(addRoute) { post(addRoute) {
val obj = call.uniload(keyValueMapSerializer) unifiedRouter.apply {
val obj = uniload(keyValueMapSerializer)
call.unianswer( unianswer(
Unit.serializer(), Unit.serializer(),
originalRepo.add(obj) originalRepo.add(obj)
) )
}
} }
post(removeRoute) { post(removeRoute) {
val obj = call.uniload( unifiedRouter.apply {
keyValueMapSerializer val obj = uniload(keyValueMapSerializer)
)
call.unianswer( unianswer(
Unit.serializer(), Unit.serializer(),
originalRepo.remove(obj), originalRepo.remove(obj),
) )
}
} }
post(clearRoute) { post(clearRoute) {
val key = call.uniload(keySerializer) unifiedRouter.apply {
val key = uniload(keySerializer)
call.unianswer( unianswer(
Unit.serializer(), Unit.serializer(),
originalRepo.clear(key), originalRepo.clear(key),
) )
}
} }
}
post(setRoute) {
unifiedRouter.apply {
val obj = uniload(keyValueMapSerializer)
unianswer(
Unit.serializer(),
originalRepo.set(obj)
)
}
}
}
fun <Key, Value> Route.configureOneToManyWriteKeyValueRepoRoutes(
originalRepo: WriteOneToManyKeyValueRepo<Key, Value>,
keySerializer: KSerializer<Key>,
valueSerializer: KSerializer<Value>,
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat,
serialFormatContentType: ContentType = standardKtorSerialFormatContentType
) = configureOneToManyWriteKeyValueRepoRoutes(originalRepo, keySerializer, valueSerializer, UnifiedRouter(serialFormat, serialFormatContentType))

View File

@@ -18,6 +18,8 @@ String[] includes = [
":ktor:client", ":ktor:client",
":coroutines", ":coroutines",
":android:recyclerview", ":android:recyclerview",
":android:alerts:common",
":android:alerts:recyclerview",
":dokka" ":dokka"
] ]