Compare commits

...

118 Commits

Author SHA1 Message Date
23590be5de Update CHANGELOG.md 2023-01-04 20:20:25 +06:00
94acc3c93b Update libs.versions.toml 2023-01-04 08:56:46 +06:00
5616326a3b start 0.16.5 2023-01-04 08:50:19 +06:00
7601860c5c Merge pull request #214 from InsanusMokrassar/0.16.4
0.16.4
2022-12-27 19:03:35 +06:00
8b43d785cc update changelog 2022-12-27 18:51:01 +06:00
b62d3a0b7d launchInCurrentThread 2022-12-27 18:50:41 +06:00
fad73c7213 start 0.16.4 2022-12-27 18:49:04 +06:00
2403c7c2b0 Merge pull request #213 from InsanusMokrassar/0.16.3
0.16.3
2022-12-25 13:24:37 +06:00
fa090bf920 Update StartLauncherPlugin.kt 2022-12-25 10:29:16 +06:00
a83ee86340 improvements in startup launcher 2022-12-25 10:26:44 +06:00
204955bcce add template for startup and readme 2022-12-19 17:51:05 +06:00
ee56e9543a start 0.16.3 2022-12-17 17:27:15 +06:00
96fdff6ffd Merge pull request #212 from InsanusMokrassar/0.16.2
0.16.2
2022-12-16 13:59:47 +06:00
58b007cbb3 fill changelog 2022-12-16 13:59:24 +06:00
4f0c139889 remove redundant stop koin expect-actual 2022-12-16 11:58:18 +06:00
c584c24fce fixes and improvements 2022-12-15 15:07:10 +06:00
85e5cee154 replace createStartupPluginAndRegister 2022-12-15 14:59:07 +06:00
5af91981f1 upgrades and filling of README 2022-12-15 10:26:31 +06:00
2fe4f08059 update compose 2022-12-15 08:51:53 +06:00
83796f345a start fix startup 2022-12-14 22:26:23 +06:00
c1e21364a6 start 0.16.2 2022-12-14 21:40:18 +06:00
067d9d0d3b Merge pull request #211 from InsanusMokrassar/0.16.1
0.16.1
2022-12-09 19:58:54 +06:00
03f527d83e Update CHANGELOG.md 2022-12-09 19:46:58 +06:00
ced05a4586 improve default runCatchingSafely/safelyWithResult and add suspend variances of safe/unsafe SafeWrapper interface 2022-12-09 12:14:24 +06:00
43fe06206a add safe wrapper 2022-12-09 11:09:32 +06:00
023657558e start 0.16.1 2022-12-09 10:52:53 +06:00
9b0b726c80 Merge pull request #210 from InsanusMokrassar/0.16.0
0.16.0
2022-12-08 09:29:12 +06:00
4ee67321c4 fill changelog and update android dependencies 2022-12-08 09:26:59 +06:00
59f1f2e59b Update libs.versions.toml 2022-12-08 08:53:37 +06:00
0766d48b7c Update libs.versions.toml 2022-12-08 07:56:04 +06:00
e18903b9e9 Update gradle.properties 2022-12-08 07:54:53 +06:00
d0eecdead2 Update gradle.properties 2022-12-08 07:53:59 +06:00
cc4a83a033 Update gradle.properties 2022-12-08 07:53:08 +06:00
1cf911bbde Update build.gradle 2022-12-07 19:40:30 +06:00
36d73d5023 Update StartupLaunchingTests.kt 2022-12-07 11:39:45 +06:00
c395242e3e fixes in StartupLauncingTests 2022-12-07 10:50:51 +06:00
cd9cd7cc5d Merge pull request #208 from InsanusMokrassar/0.15.1
0.15.1
2022-12-07 09:43:48 +06:00
acbb8a0c07 complete prepare startup module 2022-12-06 13:15:08 +06:00
b9d8528599 complete kdocs for the startup module 2022-12-06 13:14:05 +06:00
4971326eca add kdocs for the config of startup 2022-12-06 13:08:37 +06:00
09d1047260 add kdocs to the startup module 2022-12-06 13:06:21 +06:00
02dbd493c2 add tests and make several replacements/improvements 2022-12-06 12:38:24 +06:00
b17931e7bd complete startup module 2022-12-05 22:31:15 +06:00
2a4570eafc Update ServerLauncher.kt 2022-12-05 21:52:36 +06:00
c9514d3a6d Create DefaultJson.kt 2022-12-05 21:47:00 +06:00
072805efc7 Update ServerLauncher.kt 2022-12-05 21:45:30 +06:00
369ff26627 initialize startup module 2022-12-05 21:19:38 +06:00
c5abbbbd2d start 0.15.1 2022-12-05 20:04:50 +06:00
d974639f1e fixes in source roots of dokka 2022-12-05 07:47:35 +06:00
26efde316b remove redundant fix of android 32 dx files from worflows 2022-12-05 07:09:31 +06:00
fafe50f80a suppress kdocs of uniupload in actual realizations 2022-12-05 07:00:31 +06:00
41504469db Revert "potential fix of dokka"
This reverts commit 31022733ac.
2022-12-05 06:57:29 +06:00
03b3ddd98b Revert "remove redundant dokka println"
This reverts commit b53cfd5504.
2022-12-05 06:57:27 +06:00
89d919f2be Merge pull request #207 from InsanusMokrassar/0.15.0
0.15.0
2022-12-04 19:26:14 +06:00
b53cfd5504 remove redundant dokka println 2022-12-04 19:03:44 +06:00
31022733ac potential fix of dokka 2022-12-04 19:00:16 +06:00
f9a8c39879 updates and fixes in mime types 2022-12-02 21:07:32 +06:00
a812c2dd2f refresh language codes 2022-12-02 20:03:29 +06:00
217e977f0d ids in crud repos 2022-12-02 13:46:06 +06:00
04c301d1ac upgrade version up to 0.15.0 2022-12-02 13:31:11 +06:00
7f0c425389 start 0.14.5 2022-12-02 13:04:44 +06:00
1ede1c423b Merge pull request #206 from InsanusMokrassar/0.14.4
0.14.4
2022-11-27 17:34:07 +06:00
7cb064896a fill changelog 2022-11-27 14:44:03 +06:00
0c5e2862ca improvements in ktor client-server files handling 2022-11-27 14:38:35 +06:00
30d4507f54 hotfix in handleUniUpload 2022-11-26 12:12:01 +06:00
b6c6d89455 Update gradle.properties 2022-11-25 10:16:31 +06:00
9d218ee534 Update UniloadMultipart.kt 2022-11-25 10:15:59 +06:00
11116d8cab Merge pull request #205 from InsanusMokrassar/0.14.3
0.14.3
2022-11-24 13:15:08 +06:00
58d754bbde handleUniUpload now is inline 2022-11-24 12:29:48 +06:00
8f25c123dd add download and downloadToTemporalFile extensions 2022-11-24 11:27:30 +06:00
76e214fc08 upfixes 2022-11-22 14:39:31 +06:00
2b5380f8d6 add server part of uniUpload, fill changelog 2022-11-22 13:38:36 +06:00
844a129094 complete universal uniupload 2022-11-22 12:57:43 +06:00
a3090cca9d add uniupload for JVM 2022-11-22 10:36:15 +06:00
b7b5159e9c Revert "Revert "start adding uniupload""
This reverts commit 0d1aae0ef7.
2022-11-22 09:51:10 +06:00
0f8bc2c950 fix in workflow build 2022-11-21 20:50:28 +06:00
69f5c49f45 small update of build workflows 2022-11-21 20:49:04 +06:00
9b308e6fb8 update workflow 2022-11-21 20:32:48 +06:00
3e3f91128b add dependency of common android repos onto jvm 2022-11-21 20:23:05 +06:00
0d1aae0ef7 Revert "start adding uniupload"
This reverts commit 4d022f0480.
2022-11-21 20:20:37 +06:00
4d022f0480 start adding uniupload 2022-11-21 13:39:11 +06:00
153e20d00e start 0.14.3 2022-11-21 12:49:04 +06:00
a9a8171dd6 add gitea repo 2022-11-16 21:35:18 +06:00
bf5c3b59b2 Merge pull request #204 from InsanusMokrassar/0.14.2
0.14.2
2022-11-15 10:02:11 +06:00
607c432bdb Update CHANGELOG.md 2022-11-15 10:01:58 +06:00
ae5c010770 Update libs.versions.toml 2022-11-15 09:54:18 +06:00
d5e432437f Update gradle.properties 2022-11-15 09:53:28 +06:00
9f99ebce01 Merge pull request #203 from InsanusMokrassar/0.14.1
0.14.1
2022-11-10 17:19:17 +06:00
64ee899b84 revert koin version change 2022-11-10 17:13:25 +06:00
e0e0c1658b revert kotlin versin version change 2022-11-10 17:11:45 +06:00
2c586f667c update version 2022-11-10 17:09:12 +06:00
64164ef6c1 update dependencies 2022-11-10 17:06:20 +06:00
22343c0731 Merge pull request #202 from InsanusMokrassar/0.14.0
0.14.0
2022-11-08 14:20:12 +06:00
f4ec1a4c60 add deprecations removing note 2022-11-08 14:19:48 +06:00
c1c33cceb1 remove deprecations 2022-11-08 14:17:45 +06:00
a3e975b2ba Update CHANGELOG.md 2022-11-08 14:13:36 +06:00
06e705a687 update klock 2022-11-08 13:12:39 +06:00
d56eb6c867 fixes 2022-11-08 12:47:59 +06:00
9cbca864e3 Update CHANGELOG.md 2022-11-08 08:00:18 +06:00
abb4378694 Update libs.versions.toml 2022-11-08 07:59:50 +06:00
0eb698d9a4 start 0.14.0 2022-11-08 07:57:31 +06:00
15ea9f2093 add documentation for KeyValue repo 2022-11-03 13:01:51 +06:00
d47aca0923 Merge pull request #201 from InsanusMokrassar/0.13.2
0.13.2
2022-10-30 22:35:07 +06:00
1ac50e9959 update several dependencies and fill changelog 2022-10-30 21:45:19 +06:00
6adfbe3a96 Update exposed and revert several dependencies updates 2022-10-24 23:52:04 +06:00
59f36e62e9 update dependencies 2022-10-22 14:25:46 +06:00
54af116009 start 0.13.2 2022-10-22 14:14:12 +06:00
38fbec8e3b Merge pull request #200 from InsanusMokrassar/0.13.1
0.13.1
2022-10-17 15:42:13 +06:00
babbfc55e4 update default of AbstractExposedWriteCRUDRepo 2022-10-17 15:31:27 +06:00
2511e18d69 AbstractExposedWriteCRUDRepo#createAndInsertId now is optional and returns nullable value 2022-10-17 14:42:53 +06:00
29658c70a0 start 0.13.1 2022-10-17 14:35:16 +06:00
96311ee43d Merge pull request #199 from InsanusMokrassar/0.13.0
0.13.0
2022-10-13 16:50:50 +06:00
bd33b09052 A LOT OF KTOR METHODS RELATED TO UnifierRouter/UnifiedRequester HAVE BEEN REMOVED 2022-10-11 13:24:39 +06:00
8055003b47 deprecations removing 2022-10-11 12:58:29 +06:00
1257492f85 changes in insert/update exposed methods and rewriting of read keyvalue(s) exposed repos to be extending abstract key value(s) repos 2022-10-11 12:55:25 +06:00
1107b7f4ef start 0.13.0 2022-10-11 12:07:49 +06:00
a1a1171240 start 0.12.18 2022-10-04 15:36:43 +06:00
46c02e5df1 Merge pull request #198 from InsanusMokrassar/0.12.17
0.12.17
2022-10-01 21:59:42 +06:00
121 changed files with 4507 additions and 3719 deletions

View File

@@ -9,9 +9,6 @@ jobs:
- uses: actions/setup-java@v1
with:
java-version: 11
- name: Fix android 32.0.0 dx
continue-on-error: true
run: cd /usr/local/lib/android/sdk/build-tools/32.0.0/ && mv d8 dx && cd lib && mv d8.jar dx.jar
- name: Rewrite version
run: |
branch="`echo "${{ github.ref }}" | grep -o "[^/]*$"`"
@@ -20,9 +17,8 @@ jobs:
mv gradle.properties.tmp gradle.properties
- name: Build
run: ./gradlew build
# - name: Publish
# continue-on-error: true
# run: ./gradlew --no-parallel publishAllPublicationsToGithubPackagesRepository
# env:
# GITHUBPACKAGES_USER: ${{ github.actor }}
# GITHUBPACKAGES_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
- name: Publish
continue-on-error: true
run: ./gradlew publishAllPublicationsToGiteaRepository
env:
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}

View File

@@ -11,9 +11,6 @@ jobs:
- uses: actions/setup-java@v1
with:
java-version: 11
- name: Fix android 32.0.0 dx
continue-on-error: true
run: cd /usr/local/lib/android/sdk/build-tools/32.0.0/ && mv d8 dx && cd lib && mv d8.jar dx.jar
- name: Build
run: ./gradlew build && ./gradlew dokkaHtml
- name: Publish KDocs

View File

@@ -1,8 +0,0 @@
job("Build and run tests") {
container(displayName = "Run gradle build", image = "openjdk:11") {
kotlinScript { api ->
// here can be your complex logic
api.gradlew("build")
}
}
}

View File

@@ -1,5 +1,141 @@
# Changelog
## 0.16.5
* `Versions`:
* `Ktor`: `2.2.1` -> `2.2.2`
## 0.16.4
* `Coroutines`:
* Create `launchInCurrentThread`
## 0.16.3
* `Startup`:
* `Launcher`:
* All starting API have been moved into `StartLauncherPlugin` and do not require serialize/deserialize cycle for now
## 0.16.2
* `Versions`:
* `Compose`: `1.2.1` -> `1.2.2`
* `Startup`:
* Module become available on `JS` target
## 0.16.1
* `Coroutines`:
* New `runCatchingSafely`/`safelyWithResult` with receivers
* `SafeWrapper`:
* Module inited
## 0.16.0
* `Versions`:
* `Ktor`: `2.1.3` -> `2.2.1`
* `Android Fragment`: `1.5.3` -> `1.5.5`
## 0.15.1
* `Startup`:
* Inited :)
* `Plugin`:
* Inited :)
* `Launcher`:
* Inited :)
## 0.15.0
* `Repos`:
* `CRUD`:
* `Common`:
* New method `ReadCRUDRepo#getIdsByPagination`
* `Android`:
* `AbstractAndroidCRUDRepo` got new abstract method `toId`
* `Exposed`:
* `CommonExposedRepo` new abstract property `asId`
* `Ktor`:
* `Client`:
* `KtorReadCRUDRepoClient` now requires `paginationIdType`
* `LanguageCodes`:
* Updates and fixes in generation
* `MimeTypes`:
* Updates and fixes in generation
## 0.14.4
* `Common`:
* `JVM`:
* New extension `downloadToTempFile`
* `Ktor`:
* `Server`:
* Small fix in `handleUniUpload`
* `ApplicationCall#uniloadMultipartFile` now uses `uniloadMultipart`
* `Common`:
* New extension `downloadToTempFile`
* `Client`:
* New extensions on top of `uniUpload`
## 0.14.3
* `Common`:
* New type `Progress`
* `Ktor`:
* `Client`:
* New universal `uniUpload` extension for `HttpClient`
* `Server`:
* New universal `handleUniUpload` extension for `ApplicationCall`
* Add extensions `download` and `downloadToTemporalFile`
## 0.14.2
* `Versions`:
* `Exposed`: `0.40.1` -> `0.41.1`
## 0.14.1
* `Versions`:
* `Klock`: `3.3.1` -> `3.4.0`
* `UUID`: `0.5.0` -> `0.6.0`
## 0.14.0
**ALL DEPRECATIONS HAVE BEEN REMOVED**
* `Versions`:
* `Kotlin`: `1.7.10` -> `1.7.20`
* `Klock`: `3.3.0` -> `3.3.1`
* `Compose`: `1.2.0` -> `1.2.1`
* `Exposed`: `0.39.2` -> `0.40.1`
## 0.13.2
* `Versions`:
* `Klock`: `3.1.0` -> `3.3.0`
* `Ktor`: `2.1.2` -> `2.1.3`
## 0.13.1
* `Repos`:
* `Exposed`:
* `AbstractExposedWriteCRUDRepo#createAndInsertId` now is optional and returns nullable value
## 0.13.0
**ALL DEPRECATIONS HAVE BEEN REMOVED**
**A LOT OF KTOR METHODS RELATED TO UnifierRouter/UnifiedRequester HAVE BEEN REMOVED**
* `Repos`:
* `Exposed`:
* `AbstractExposedWriteCRUDRepo` got two new methods: `update` with `it` as `UpdateBuilder<Int>` and `createAndInsertId`
* Old `update` method has been deprecated and not recommended to override anymore in realizations
* Old `insert` method now is `open` instead of `abstract` and can be omitted
* `AbstractExposedKeyValueRepo` got two new methods: `update` with `it` as `UpdateBuilder<Int>` and `insertKey`
* Old `update` method has been deprecated and not recommended to override anymore
* Old `insert` method now is `open` instead of `abstract` and can be omitted in realizations
## 0.12.17
* `Versions`:

View File

@@ -0,0 +1,37 @@
package dev.inmo.micro_utils.common
import kotlinx.serialization.Serializable
import kotlin.jvm.JvmInline
@Serializable
@JvmInline
value class Progress private constructor(
val of1: Double
) {
val of1Float
get() = of1.toFloat()
val of100
get() = of1 * 100
val of100Float
get() = of100.toFloat()
val of100Int
get() = of100.toInt()
init {
require(of1 in rangeOfValues) {
"Progress main value should be in $rangeOfValues, but incoming value is $of1"
}
}
companion object {
val rangeOfValues = 0.0 .. 1.0
val START = Progress(rangeOfValues.start)
val COMPLETED = Progress(rangeOfValues.endInclusive)
operator fun invoke(of1: Double) = Progress(of1.coerceIn(rangeOfValues))
operator fun invoke(part: Number, total: Number) = Progress(
part.toDouble() / total.toDouble()
)
}
}

View File

@@ -0,0 +1,80 @@
@file:Suppress(
"RemoveRedundantCallsOfConversionMethods",
"RedundantVisibilityModifier",
)
package dev.inmo.micro_utils.common
import kotlin.Byte
import kotlin.Double
import kotlin.Float
import kotlin.Int
import kotlin.Long
import kotlin.Short
import kotlin.Suppress
public operator fun Progress.plus(other: Progress): Progress = Progress(of1 + other.of1)
public operator fun Progress.minus(other: Progress): Progress = Progress(of1 - other.of1)
public operator fun Progress.plus(i: Byte): Progress = Progress((of1 + i).toDouble())
public operator fun Progress.minus(i: Byte): Progress = Progress((of1 - i).toDouble())
public operator fun Progress.times(i: Byte): Progress = Progress((of1 * i).toDouble())
public operator fun Progress.div(i: Byte): Progress = Progress((of1 / i).toDouble())
public operator fun Progress.rem(i: Byte): Progress = Progress((of1 % i).toDouble())
public operator fun Progress.plus(i: Short): Progress = Progress((of1 + i).toDouble())
public operator fun Progress.minus(i: Short): Progress = Progress((of1 - i).toDouble())
public operator fun Progress.times(i: Short): Progress = Progress((of1 * i).toDouble())
public operator fun Progress.div(i: Short): Progress = Progress((of1 / i).toDouble())
public operator fun Progress.rem(i: Short): Progress = Progress((of1 % i).toDouble())
public operator fun Progress.plus(i: Int): Progress = Progress((of1 + i).toDouble())
public operator fun Progress.minus(i: Int): Progress = Progress((of1 - i).toDouble())
public operator fun Progress.times(i: Int): Progress = Progress((of1 * i).toDouble())
public operator fun Progress.div(i: Int): Progress = Progress((of1 / i).toDouble())
public operator fun Progress.rem(i: Int): Progress = Progress((of1 % i).toDouble())
public operator fun Progress.plus(i: Long): Progress = Progress((of1 + i).toDouble())
public operator fun Progress.minus(i: Long): Progress = Progress((of1 - i).toDouble())
public operator fun Progress.times(i: Long): Progress = Progress((of1 * i).toDouble())
public operator fun Progress.div(i: Long): Progress = Progress((of1 / i).toDouble())
public operator fun Progress.rem(i: Long): Progress = Progress((of1 % i).toDouble())
public operator fun Progress.plus(i: Float): Progress = Progress((of1 + i).toDouble())
public operator fun Progress.minus(i: Float): Progress = Progress((of1 - i).toDouble())
public operator fun Progress.times(i: Float): Progress = Progress((of1 * i).toDouble())
public operator fun Progress.div(i: Float): Progress = Progress((of1 / i).toDouble())
public operator fun Progress.rem(i: Float): Progress = Progress((of1 % i).toDouble())
public operator fun Progress.plus(i: Double): Progress = Progress((of1 + i).toDouble())
public operator fun Progress.minus(i: Double): Progress = Progress((of1 - i).toDouble())
public operator fun Progress.times(i: Double): Progress = Progress((of1 * i).toDouble())
public operator fun Progress.div(i: Double): Progress = Progress((of1 / i).toDouble())
public operator fun Progress.rem(i: Double): Progress = Progress((of1 % i).toDouble())
public operator fun Progress.compareTo(other: Progress): Int = (of1 - other.of1).toInt()

View File

@@ -0,0 +1,20 @@
package dev.inmo.micro_utils.common
import java.io.File
import java.io.InputStream
import java.util.UUID
fun InputStream.downloadToTempFile(
fileName: String = UUID.randomUUID().toString(),
fileExtension: String? = ".temp",
folder: File? = null
) = File.createTempFile(
fileName,
fileExtension,
folder
).apply {
outputStream().use {
copyTo(it)
}
deleteOnExit()
}

View File

@@ -22,6 +22,7 @@ kotlin {
dependencies {
api libs.kt.coroutines.android
}
dependsOn(jvmMain)
}
}
}

View File

@@ -115,10 +115,21 @@ suspend inline fun <T> runCatchingSafely(
safely(onException, block)
}
suspend inline fun <T, R> T.runCatchingSafely(
noinline onException: ExceptionHandler<R> = defaultSafelyExceptionHandler,
noinline block: suspend T.() -> R
): Result<R> = runCatching {
safely(onException) { block() }
}
suspend inline fun <T> safelyWithResult(
noinline block: suspend CoroutineScope.() -> T
): Result<T> = runCatchingSafely(defaultSafelyExceptionHandler, block)
suspend inline fun <T, R> T.safelyWithResult(
noinline block: suspend T.() -> R
): Result<R> = runCatchingSafely(defaultSafelyExceptionHandler, block)
/**
* Use this handler in cases you wish to include handling of exceptions by [defaultSafelyWithoutExceptionHandler] and
* returning null at one time

View File

@@ -0,0 +1,9 @@
package dev.inmo.micro_utils.coroutines
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
fun <T> launchInCurrentThread(block: suspend CoroutineScope.() -> T): T {
val scope = CoroutineScope(Dispatchers.Unconfined)
return scope.launchSynchronously(block)
}

View File

@@ -6,7 +6,7 @@ fun <T> CoroutineScope.launchSynchronously(block: suspend CoroutineScope.() -> T
var result: Result<T>? = null
val objectToSynchronize = Object()
synchronized(objectToSynchronize) {
launch {
launch(start = CoroutineStart.UNDISPATCHED) {
result = safelyWithResult(block)
}.invokeOnCompletion {
synchronized(objectToSynchronize) {

View File

@@ -0,0 +1,47 @@
package dev.inmo.micro_utils.coroutines
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import kotlin.test.Test
import kotlin.test.assertEquals
class LaunchInCurrentThreadTests {
@Test
fun simpleTestThatLaunchInCurrentThreadWorks() {
val expectedResult = 10
val result = launchInCurrentThread {
expectedResult
}
assertEquals(expectedResult, result)
}
@Test
fun simpleTestThatSeveralLaunchInCurrentThreadWorks() {
val testData = 0 until 100
testData.forEach {
val result = launchInCurrentThread {
it
}
assertEquals(it, result)
}
}
@Test
fun simpleTestThatLaunchInCurrentThreadWillCorrectlyHandleSuspensionsWorks() {
val testData = 0 until 100
suspend fun test(data: Any): Any {
return withContext(Dispatchers.Default) {
delay(1)
data
}
}
testData.forEach {
val result = launchInCurrentThread {
test(it)
}
assertEquals(it, result)
}
}
}

View File

@@ -121,11 +121,11 @@ tasks.dokkaHtml {
// }
named("jvmMain") {
sourceRoots.setFrom(findSourcesWithName("jvmMain", "commonMain"))
sourceRoots.setFrom(findSourcesWithName("jvmMain"))
}
named("androidMain") {
sourceRoots.setFrom(findSourcesWithName("androidMain", "commonMain"))
sourceRoots.setFrom(findSourcesWithName("androidMain"))
}
}
}

View File

@@ -23,6 +23,7 @@ allprojects {
mppProjectWithSerializationPresetPath = "${rootProject.projectDir.absolutePath}/mppProjectWithSerialization.gradle"
mppProjectWithSerializationAndComposePresetPath = "${rootProject.projectDir.absolutePath}/mppProjectWithSerializationAndCompose.gradle"
mppJavaProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppJavaProject.gradle"
mppJsAndJavaProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppJsAndJavaProject.gradle"
mppAndroidProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppAndroidProject.gradle"
defaultAndroidSettingsPresetPath = "${rootProject.projectDir.absolutePath}/defaultAndroidSettings.gradle"

View File

@@ -14,5 +14,5 @@ crypto_js_version=4.1.1
# Project data
group=dev.inmo
version=0.12.17
android_code_version=156
version=0.16.5
android_code_version=173

View File

@@ -1,29 +1,31 @@
[versions]
kt = "1.7.10"
kt-serialization = "1.4.0"
kt = "1.7.20"
kt-serialization = "1.4.1"
kt-coroutines = "1.6.4"
jb-compose = "1.2.0-beta02"
jb-exposed = "0.39.2"
jb-dokka = "1.7.10"
kslog = "0.5.4"
klock = "3.1.0"
uuid = "0.5.0"
jb-compose = "1.2.2"
jb-exposed = "0.41.1"
jb-dokka = "1.7.20"
ktor = "2.1.2"
klock = "3.4.0"
uuid = "0.6.0"
ktor = "2.2.2"
gh-release = "2.4.1"
koin = "3.2.2"
android-gradle = "7.2.2"
android-gradle = "7.3.0"
dexcount = "3.1.0"
android-coreKtx = "1.9.0"
android-recyclerView = "1.2.1"
android-appCompat = "1.5.1"
android-fragment = "1.5.3"
android-fragment = "1.5.5"
android-espresso = "3.4.0"
android-test = "1.1.3"
@@ -60,6 +62,7 @@ ktor-server-websockets = { module = "io.ktor:ktor-server-websockets", version.re
ktor-server-statusPages = { module = "io.ktor:ktor-server-status-pages", version.ref = "ktor" }
ktor-server-content-negotiation = { module = "io.ktor:ktor-server-content-negotiation", version.ref = "ktor" }
kslog = { module = "dev.inmo:kslog", version.ref = "kslog" }
klock = { module = "com.soywiz.korlibs.klock:klock", version.ref = "klock" }
uuid = { module = "com.benasher44:uuid", version.ref = "uuid" }

View File

@@ -15,5 +15,9 @@ kotlin {
api libs.ktor.client
}
}
androidMain {
dependsOn jvmMain
}
}
}

View File

@@ -1,82 +0,0 @@
package dev.inmo.micro_utils.ktor.client
import dev.inmo.micro_utils.coroutines.runCatchingSafely
import dev.inmo.micro_utils.coroutines.safely
import dev.inmo.micro_utils.ktor.common.*
import io.ktor.client.HttpClient
import io.ktor.client.plugins.pluginOrNull
import io.ktor.client.plugins.websocket.WebSockets
import io.ktor.client.plugins.websocket.ws
import io.ktor.client.request.HttpRequestBuilder
import io.ktor.websocket.Frame
import io.ktor.websocket.readBytes
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.isActive
import kotlinx.serialization.DeserializationStrategy
/**
* @param checkReconnection This lambda will be called when it is required to reconnect to websocket to establish
* connection. Must return true in case if must be reconnected. By default always reconnecting
*/
@Deprecated("This method will be removed soon. It is now recommended to use built-in ktor features instead")
inline fun <T> HttpClient.createStandardWebsocketFlow(
url: String,
crossinline checkReconnection: suspend (Throwable?) -> Boolean = { true },
noinline requestBuilder: HttpRequestBuilder.() -> Unit = {},
crossinline conversation: suspend (StandardKtorSerialInputData) -> T
): Flow<T> {
pluginOrNull(WebSockets) ?: error("Plugin $WebSockets must be installed for using createStandardWebsocketFlow")
val correctedUrl = url.asCorrectWebSocketUrl
return channelFlow {
do {
val reconnect = runCatchingSafely {
ws(correctedUrl, requestBuilder) {
for (received in incoming) {
when (received) {
is Frame.Binary -> send(conversation(received.data))
else -> {
close()
return@ws
}
}
}
}
checkReconnection(null)
}.getOrElse { e ->
checkReconnection(e).also {
if (!it) {
close(e)
}
}
}
} while (reconnect && isActive)
if (isActive) {
safely {
close()
}
}
}
}
/**
* @param checkReconnection This lambda will be called when it is required to reconnect to websocket to establish
* connection. Must return true in case if must be reconnected. By default always reconnecting
*/
@Deprecated("This method will be removed soon. It is now recommended to use built-in ktor features instead")
inline fun <T> HttpClient.createStandardWebsocketFlow(
url: String,
deserializer: DeserializationStrategy<T>,
crossinline checkReconnection: suspend (Throwable?) -> Boolean = { true },
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat,
noinline requestBuilder: HttpRequestBuilder.() -> Unit = {},
) = createStandardWebsocketFlow(
url,
checkReconnection,
requestBuilder
) {
serialFormat.decodeDefault(deserializer, it)
}

View File

@@ -0,0 +1,3 @@
package dev.inmo.micro_utils.ktor.client
typealias OnUploadCallback = suspend (uploaded: Long, count: Long) -> Unit

View File

@@ -1,260 +0,0 @@
package dev.inmo.micro_utils.ktor.client
import dev.inmo.micro_utils.common.MPPFile
import dev.inmo.micro_utils.common.filename
import dev.inmo.micro_utils.ktor.common.*
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.request.*
import io.ktor.client.request.forms.*
import io.ktor.client.statement.readBytes
import io.ktor.http.*
import io.ktor.utils.io.core.ByteReadPacket
import kotlinx.serialization.*
@Deprecated("This class will be removed soon. It is now recommended to use built-in ktor features instead")
class UnifiedRequester(
val client: HttpClient = HttpClient(),
val serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
) {
suspend fun <ResultType> uniget(
url: String,
resultDeserializer: DeserializationStrategy<ResultType>
): ResultType = client.uniget(url, resultDeserializer, serialFormat)
fun <T> encodeUrlQueryValue(
serializationStrategy: SerializationStrategy<T>,
value: T
) = serializationStrategy.encodeUrlQueryValue(
value,
serialFormat
)
suspend fun <BodyType, ResultType> unipost(
url: String,
bodyInfo: Pair<SerializationStrategy<BodyType>, BodyType>,
resultDeserializer: DeserializationStrategy<ResultType>
) = client.unipost(url, bodyInfo, resultDeserializer, serialFormat)
suspend fun <ResultType> unimultipart(
url: String,
filename: String,
inputProvider: InputProvider,
resultDeserializer: DeserializationStrategy<ResultType>,
mimetype: String = "*/*",
additionalParametersBuilder: FormBuilder.() -> Unit = {},
dataHeadersBuilder: HeadersBuilder.() -> Unit = {},
requestBuilder: HttpRequestBuilder.() -> Unit = {},
): ResultType = client.unimultipart(url, filename, inputProvider, resultDeserializer, mimetype, additionalParametersBuilder, dataHeadersBuilder, requestBuilder, serialFormat)
suspend fun <BodyType, ResultType> unimultipart(
url: String,
filename: String,
inputProvider: InputProvider,
otherData: Pair<SerializationStrategy<BodyType>, BodyType>,
resultDeserializer: DeserializationStrategy<ResultType>,
mimetype: String = "*/*",
additionalParametersBuilder: FormBuilder.() -> Unit = {},
dataHeadersBuilder: HeadersBuilder.() -> Unit = {},
requestBuilder: HttpRequestBuilder.() -> Unit = {},
): ResultType = client.unimultipart(url, filename, otherData, inputProvider, resultDeserializer, mimetype, additionalParametersBuilder, dataHeadersBuilder, requestBuilder, serialFormat)
suspend fun <ResultType> unimultipart(
url: String,
mppFile: MPPFile,
resultDeserializer: DeserializationStrategy<ResultType>,
mimetype: String = "*/*",
additionalParametersBuilder: FormBuilder.() -> Unit = {},
dataHeadersBuilder: HeadersBuilder.() -> Unit = {},
requestBuilder: HttpRequestBuilder.() -> Unit = {}
): ResultType = client.unimultipart(
url, mppFile, resultDeserializer, mimetype, additionalParametersBuilder, dataHeadersBuilder, requestBuilder, serialFormat
)
suspend fun <BodyType, ResultType> unimultipart(
url: String,
mppFile: MPPFile,
otherData: Pair<SerializationStrategy<BodyType>, BodyType>,
resultDeserializer: DeserializationStrategy<ResultType>,
mimetype: String = "*/*",
additionalParametersBuilder: FormBuilder.() -> Unit = {},
dataHeadersBuilder: HeadersBuilder.() -> Unit = {},
requestBuilder: HttpRequestBuilder.() -> Unit = {}
): ResultType = client.unimultipart(
url, mppFile, otherData, resultDeserializer, mimetype, additionalParametersBuilder, dataHeadersBuilder, requestBuilder, serialFormat
)
fun <T> createStandardWebsocketFlow(
url: String,
checkReconnection: suspend (Throwable?) -> Boolean,
deserializer: DeserializationStrategy<T>,
requestBuilder: HttpRequestBuilder.() -> Unit = {},
) = client.createStandardWebsocketFlow(url, deserializer, checkReconnection, serialFormat, requestBuilder)
fun <T> createStandardWebsocketFlow(
url: String,
deserializer: DeserializationStrategy<T>,
requestBuilder: HttpRequestBuilder.() -> Unit = {},
) = createStandardWebsocketFlow(url, { true }, deserializer, requestBuilder)
}
@Deprecated("This property will be removed soon. It is now recommended to use built-in ktor features instead")
val defaultRequester = UnifiedRequester()
@Deprecated("This method will be removed soon. It is now recommended to use built-in ktor features instead")
suspend fun <ResultType> HttpClient.uniget(
url: String,
resultDeserializer: DeserializationStrategy<ResultType>,
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
) = get(url).let {
serialFormat.decodeDefault(resultDeserializer, it.body<StandardKtorSerialInputData>())
}
@Deprecated("This method will be removed soon. It is now recommended to use built-in ktor features instead")
fun <T> SerializationStrategy<T>.encodeUrlQueryValue(
value: T,
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
) = serialFormat.encodeHex(
this,
value
)
@Deprecated("This method will be removed soon. It is now recommended to use built-in ktor features instead")
suspend fun <BodyType, ResultType> HttpClient.unipost(
url: String,
bodyInfo: Pair<SerializationStrategy<BodyType>, BodyType>,
resultDeserializer: DeserializationStrategy<ResultType>,
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
) = post(url) {
setBody(
serialFormat.encodeDefault(bodyInfo.first, bodyInfo.second)
)
}.let {
serialFormat.decodeDefault(resultDeserializer, it.body<StandardKtorSerialInputData>())
}
@Deprecated("This method will be removed soon. It is now recommended to use built-in ktor features instead")
suspend fun <ResultType> HttpClient.unimultipart(
url: String,
filename: String,
inputProvider: InputProvider,
resultDeserializer: DeserializationStrategy<ResultType>,
mimetype: String = "*/*",
additionalParametersBuilder: FormBuilder.() -> Unit = {},
dataHeadersBuilder: HeadersBuilder.() -> Unit = {},
requestBuilder: HttpRequestBuilder.() -> Unit = {},
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
): ResultType = submitFormWithBinaryData(
url,
formData = formData {
append(
"bytes",
inputProvider,
Headers.build {
append(HttpHeaders.ContentType, mimetype)
append(HttpHeaders.ContentDisposition, "filename=\"$filename\"")
dataHeadersBuilder()
}
)
additionalParametersBuilder()
}
) {
requestBuilder()
}.let { serialFormat.decodeDefault(resultDeserializer, it.body<StandardKtorSerialInputData>()) }
@Deprecated("This method will be removed soon. It is now recommended to use built-in ktor features instead")
suspend fun <BodyType, ResultType> HttpClient.unimultipart(
url: String,
filename: String,
otherData: Pair<SerializationStrategy<BodyType>, BodyType>,
inputProvider: InputProvider,
resultDeserializer: DeserializationStrategy<ResultType>,
mimetype: String = "*/*",
additionalParametersBuilder: FormBuilder.() -> Unit = {},
dataHeadersBuilder: HeadersBuilder.() -> Unit = {},
requestBuilder: HttpRequestBuilder.() -> Unit = {},
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
): ResultType = unimultipart(
url,
filename,
inputProvider,
resultDeserializer,
mimetype,
additionalParametersBuilder = {
val serialized = serialFormat.encodeDefault(otherData.first, otherData.second)
append(
"data",
InputProvider(serialized.size.toLong()) {
ByteReadPacket(serialized)
},
Headers.build {
append(HttpHeaders.ContentType, ContentType.Application.Cbor.contentType)
append(HttpHeaders.ContentDisposition, "filename=data.bytes")
dataHeadersBuilder()
}
)
additionalParametersBuilder()
},
dataHeadersBuilder,
requestBuilder,
serialFormat
)
@Deprecated("This method will be removed soon. It is now recommended to use built-in ktor features instead")
suspend fun <ResultType> HttpClient.unimultipart(
url: String,
mppFile: MPPFile,
resultDeserializer: DeserializationStrategy<ResultType>,
mimetype: String = "*/*",
additionalParametersBuilder: FormBuilder.() -> Unit = {},
dataHeadersBuilder: HeadersBuilder.() -> Unit = {},
requestBuilder: HttpRequestBuilder.() -> Unit = {},
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
): ResultType = unimultipart(
url,
mppFile.filename.string,
mppFile.inputProvider(),
resultDeserializer,
mimetype,
additionalParametersBuilder,
dataHeadersBuilder,
requestBuilder,
serialFormat
)
@Deprecated("This method will be removed soon. It is now recommended to use built-in ktor features instead")
suspend fun <BodyType, ResultType> HttpClient.unimultipart(
url: String,
mppFile: MPPFile,
otherData: Pair<SerializationStrategy<BodyType>, BodyType>,
resultDeserializer: DeserializationStrategy<ResultType>,
mimetype: String = "*/*",
additionalParametersBuilder: FormBuilder.() -> Unit = {},
dataHeadersBuilder: HeadersBuilder.() -> Unit = {},
requestBuilder: HttpRequestBuilder.() -> Unit = {},
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat
): ResultType = unimultipart(
url,
mppFile,
resultDeserializer,
mimetype,
additionalParametersBuilder = {
val serialized = serialFormat.encodeDefault(otherData.first, otherData.second)
append(
"data",
InputProvider(serialized.size.toLong()) {
ByteReadPacket(serialized)
},
Headers.build {
append(HttpHeaders.ContentType, ContentType.Application.Cbor.contentType)
append(HttpHeaders.ContentDisposition, "filename=data.bytes")
dataHeadersBuilder()
}
)
additionalParametersBuilder()
},
dataHeadersBuilder,
requestBuilder,
serialFormat
)

View File

@@ -7,13 +7,5 @@ import io.ktor.client.HttpClient
expect suspend fun HttpClient.tempUpload(
fullTempUploadDraftPath: String,
file: MPPFile,
onUpload: (uploaded: Long, count: Long) -> Unit = { _, _ -> }
onUpload: OnUploadCallback = { _, _ -> }
): TemporalFileId
suspend fun UnifiedRequester.tempUpload(
fullTempUploadDraftPath: String,
file: MPPFile,
onUpload: (uploaded: Long, count: Long) -> Unit = { _, _ -> }
): TemporalFileId = client.tempUpload(
fullTempUploadDraftPath, file, onUpload
)

View File

@@ -0,0 +1,105 @@
package dev.inmo.micro_utils.ktor.client
import dev.inmo.micro_utils.common.FileName
import dev.inmo.micro_utils.common.MPPFile
import dev.inmo.micro_utils.ktor.common.LambdaInputProvider
import io.ktor.client.HttpClient
import io.ktor.http.Headers
import io.ktor.utils.io.core.Input
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.StringFormat
import kotlinx.serialization.json.Json
data class UniUploadFileInfo(
val fileName: FileName,
val mimeType: String,
val inputAllocator: LambdaInputProvider
)
/**
* Will execute submitting of multipart data request
*
* @param data [Map] where keys will be used as names for multipart parts and values as values. If you will pass
* [dev.inmo.micro_utils.common.MPPFile] (File from JS or JVM platform). Also you may pass [UniUploadFileInfo] as value
* in case you wish to pass other source of multipart binary data than regular file
*
* @see dev.inmo.micro_utils.ktor.server.handleUniUpload
*/
expect suspend fun <T> HttpClient.uniUpload(
url: String,
data: Map<String, Any>,
resultDeserializer: DeserializationStrategy<T>,
headers: Headers = Headers.Empty,
stringFormat: StringFormat = Json,
onUpload: OnUploadCallback = { _, _ -> }
): T?
/**
* Additional variant of [uniUpload] which will unify sending of some [MPPFile] with the server
*
* @see dev.inmo.micro_utils.ktor.server.uniloadMultipartFile
*/
suspend fun <T> HttpClient.uniUpload(
url: String,
file: MPPFile,
resultDeserializer: DeserializationStrategy<T>,
additionalData: Map<String, Any> = emptyMap(),
headers: Headers = Headers.Empty,
stringFormat: StringFormat = Json,
onUpload: OnUploadCallback = { _, _ -> }
): T? = uniUpload(
url,
additionalData + ("bytes" to file),
resultDeserializer,
headers,
stringFormat,
onUpload
)
/**
* Additional variant of [uniUpload] which will unify sending of some [UniUploadFileInfo] with the server
*
* @see dev.inmo.micro_utils.ktor.server.uniloadMultipartFile
*/
suspend fun <T> HttpClient.uniUpload(
url: String,
info: UniUploadFileInfo,
resultDeserializer: DeserializationStrategy<T>,
additionalData: Map<String, Any> = emptyMap(),
headers: Headers = Headers.Empty,
stringFormat: StringFormat = Json,
onUpload: OnUploadCallback = { _, _ -> }
): T? = uniUpload(
url,
additionalData + ("bytes" to info),
resultDeserializer,
headers,
stringFormat,
onUpload
)
/**
* Additional variant of [uniUpload] which will unify sending of some [UniUploadFileInfo] (built from [fileName],
* [mimeType] and [inputAllocator]) with the server
*
* @see dev.inmo.micro_utils.ktor.server.uniloadMultipartFile
*/
suspend fun <T> HttpClient.uniUpload(
url: String,
fileName: FileName,
mimeType: String,
inputAllocator: LambdaInputProvider,
resultDeserializer: DeserializationStrategy<T>,
additionalData: Map<String, Any> = emptyMap(),
headers: Headers = Headers.Empty,
stringFormat: StringFormat = Json,
onUpload: OnUploadCallback = { _, _ -> }
): T? = uniUpload(
url,
UniUploadFileInfo(fileName, mimeType, inputAllocator),
resultDeserializer,
additionalData,
headers,
stringFormat,
onUpload
)

View File

@@ -1,6 +1,8 @@
package dev.inmo.micro_utils.ktor.client
import dev.inmo.micro_utils.common.MPPFile
import dev.inmo.micro_utils.coroutines.LinkedSupervisorJob
import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions
import dev.inmo.micro_utils.ktor.common.TemporalFileId
import io.ktor.client.HttpClient
import kotlinx.coroutines.*
@@ -12,10 +14,11 @@ import org.w3c.xhr.XMLHttpRequest.Companion.DONE
suspend fun tempUpload(
fullTempUploadDraftPath: String,
file: MPPFile,
onUpload: (Long, Long) -> Unit
onUpload: OnUploadCallback
): TemporalFileId {
val formData = FormData()
val answer = CompletableDeferred<TemporalFileId>(currentCoroutineContext().job)
val subscope = CoroutineScope(currentCoroutineContext().LinkedSupervisorJob())
formData.append(
"data",
@@ -25,7 +28,7 @@ suspend fun tempUpload(
val request = XMLHttpRequest()
request.responseType = XMLHttpRequestResponseType.TEXT
request.upload.onprogress = {
onUpload(it.loaded.toLong(), it.total.toLong())
subscope.launchSafelyWithoutExceptions { onUpload(it.loaded.toLong(), it.total.toLong()) }
}
request.onload = {
if (request.status == 200.toShort()) {
@@ -48,12 +51,14 @@ suspend fun tempUpload(
}
}
return answer.await()
return answer.await().also {
subscope.cancel()
}
}
actual suspend fun HttpClient.tempUpload(
fullTempUploadDraftPath: String,
file: MPPFile,
onUpload: (uploaded: Long, count: Long) -> Unit
onUpload: OnUploadCallback
): TemporalFileId = dev.inmo.micro_utils.ktor.client.tempUpload(fullTempUploadDraftPath, file, onUpload)

View File

@@ -0,0 +1,97 @@
package dev.inmo.micro_utils.ktor.client
import dev.inmo.micro_utils.common.MPPFile
import dev.inmo.micro_utils.common.Progress
import dev.inmo.micro_utils.coroutines.LinkedSupervisorJob
import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions
import io.ktor.client.HttpClient
import io.ktor.http.Headers
import io.ktor.utils.io.core.readBytes
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.cancel
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.job
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.StringFormat
import kotlinx.serialization.encodeToString
import org.khronos.webgl.Int8Array
import org.w3c.files.Blob
import org.w3c.xhr.FormData
import org.w3c.xhr.TEXT
import org.w3c.xhr.XMLHttpRequest
import org.w3c.xhr.XMLHttpRequestResponseType
/**
* Will execute submitting of multipart data request
*
* @param data [Map] where keys will be used as names for multipart parts and values as values. If you will pass
* [dev.inmo.micro_utils.common.MPPFile] (File from JS or JVM platform). Also you may pass [UniUploadFileInfo] as value
* in case you wish to pass other source of multipart binary data than regular file
* @suppress
*/
actual suspend fun <T> HttpClient.uniUpload(
url: String,
data: Map<String, Any>,
resultDeserializer: DeserializationStrategy<T>,
headers: Headers,
stringFormat: StringFormat,
onUpload: OnUploadCallback
): T? {
val formData = FormData()
val answer = CompletableDeferred<T?>(currentCoroutineContext().job)
val subscope = CoroutineScope(currentCoroutineContext().LinkedSupervisorJob())
data.forEach { (k, v) ->
when (v) {
is MPPFile -> formData.append(
k,
v
)
is UniUploadFileInfo -> formData.append(
k,
Blob(arrayOf(Int8Array(v.inputAllocator().readBytes().toTypedArray()))),
v.fileName.name
)
else -> formData.append(
k,
stringFormat.encodeToString(v)
)
}
}
val request = XMLHttpRequest()
headers.forEach { s, strings ->
request.setRequestHeader(s, strings.joinToString())
}
request.responseType = XMLHttpRequestResponseType.TEXT
request.upload.onprogress = {
subscope.launchSafelyWithoutExceptions { onUpload(it.loaded.toLong(), it.total.toLong()) }
}
request.onload = {
if (request.status == 200.toShort()) {
answer.complete(
stringFormat.decodeFromString(resultDeserializer, request.responseText)
)
} else {
answer.completeExceptionally(Exception("Something went wrong: $it"))
}
}
request.onerror = {
answer.completeExceptionally(Exception("Something went wrong: $it"))
}
request.open("POST", url, true)
request.send(formData)
answer.invokeOnCompletion {
runCatching {
if (request.readyState != XMLHttpRequest.DONE) {
request.abort()
}
}
}
return answer.await().also {
subscope.cancel()
}
}

View File

@@ -4,6 +4,8 @@ import dev.inmo.micro_utils.common.MPPFile
import io.ktor.client.request.forms.InputProvider
import io.ktor.utils.io.streams.asInput
actual suspend fun MPPFile.inputProvider(): InputProvider = InputProvider(length()) {
fun MPPFile.inputProviderSync(): InputProvider = InputProvider(length()) {
inputStream().asInput()
}
actual suspend fun MPPFile.inputProvider(): InputProvider = inputProviderSync()

View File

@@ -18,7 +18,7 @@ internal val MPPFile.mimeType: String
actual suspend fun HttpClient.tempUpload(
fullTempUploadDraftPath: String,
file: MPPFile,
onUpload: (Long, Long) -> Unit
onUpload: OnUploadCallback
): TemporalFileId {
val inputProvider = file.inputProvider()
val fileId = submitFormWithBinaryData(

View File

@@ -0,0 +1,102 @@
package dev.inmo.micro_utils.ktor.client
import dev.inmo.micro_utils.common.Progress
import io.ktor.client.HttpClient
import io.ktor.client.engine.mergeHeaders
import io.ktor.client.plugins.onUpload
import io.ktor.client.request.HttpRequestBuilder
import io.ktor.client.request.forms.InputProvider
import io.ktor.client.request.forms.formData
import io.ktor.client.request.forms.submitForm
import io.ktor.client.request.forms.submitFormWithBinaryData
import io.ktor.client.request.headers
import io.ktor.client.statement.bodyAsText
import io.ktor.http.Headers
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpStatusCode
import io.ktor.http.Parameters
import io.ktor.http.content.PartData
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.StringFormat
import kotlinx.serialization.encodeToString
import java.io.File
/**
* Will execute submitting of multipart data request
*
* @param data [Map] where keys will be used as names for multipart parts and values as values. If you will pass
* [dev.inmo.micro_utils.common.MPPFile] (File from JS or JVM platform). Also you may pass [UniUploadFileInfo] as value
* in case you wish to pass other source of multipart binary data than regular file
* @suppress
*/
actual suspend fun <T> HttpClient.uniUpload(
url: String,
data: Map<String, Any>,
resultDeserializer: DeserializationStrategy<T>,
headers: Headers,
stringFormat: StringFormat,
onUpload: OnUploadCallback
): T? {
val withBinary = data.values.any { it is File || it is UniUploadFileInfo }
val formData = formData {
data.forEach { (k, v) ->
when (v) {
is File -> append(
k,
v.inputProviderSync(),
Headers.build {
append(HttpHeaders.ContentType, v.mimeType)
append(HttpHeaders.ContentDisposition, "filename=\"${v.name}\"")
}
)
is UniUploadFileInfo -> append(
k,
InputProvider(block = v.inputAllocator),
Headers.build {
append(HttpHeaders.ContentType, v.mimeType)
append(HttpHeaders.ContentDisposition, "filename=\"${v.fileName.name}\"")
}
)
else -> append(
k,
stringFormat.encodeToString(v)
)
}
}
}
val requestBuilder: HttpRequestBuilder.() -> Unit = {
headers {
appendAll(headers)
}
onUpload { bytesSentTotal, contentLength ->
onUpload(bytesSentTotal, contentLength)
}
}
val response = if (withBinary) {
submitFormWithBinaryData(
url,
formData,
block = requestBuilder
)
} else {
submitForm(
url,
Parameters.build {
formData.forEach {
val formItem = (it as PartData.FormItem)
append(it.name!!, it.value)
}
},
block = requestBuilder
)
}
return if (response.status == HttpStatusCode.OK) {
stringFormat.decodeFromString(resultDeserializer, response.bodyAsText())
} else {
null
}
}

View File

@@ -0,0 +1,5 @@
package dev.inmo.micro_utils.ktor.common
import io.ktor.utils.io.core.Input
typealias LambdaInputProvider = () -> Input

View File

@@ -0,0 +1,23 @@
package dev.inmo.micro_utils.ktor.common
import io.ktor.utils.io.core.Input
import io.ktor.utils.io.core.copyTo
import io.ktor.utils.io.streams.asOutput
import java.io.File
import java.io.InputStream
import java.util.UUID
fun Input.downloadToTempFile(
fileName: String = UUID.randomUUID().toString(),
fileExtension: String? = ".temp",
folder: File? = null
) = File.createTempFile(
fileName,
fileExtension,
folder
).apply {
outputStream().use {
copyTo(it.asOutput())
}
deleteOnExit()
}

View File

@@ -0,0 +1,56 @@
package dev.inmo.micro_utils.ktor.server
import com.benasher44.uuid.uuid4
import io.ktor.http.content.PartData
import io.ktor.utils.io.copyTo
import io.ktor.utils.io.core.copyTo
import io.ktor.utils.io.jvm.javaio.copyTo
import io.ktor.utils.io.streams.asOutput
import java.io.File
fun PartData.FileItem.download(target: File) {
provider().use { input ->
target.outputStream().use {
input.copyTo(it.asOutput())
}
}
}
fun PartData.FileItem.downloadToTemporalFile(): File {
val outputFile = File.createTempFile(uuid4().toString(), ".temp").apply {
deleteOnExit()
}
download(outputFile)
return outputFile
}
fun PartData.BinaryItem.download(target: File) {
provider().use { input ->
target.outputStream().use {
input.copyTo(it.asOutput())
}
}
}
fun PartData.BinaryItem.downloadToTemporalFile(): File {
val outputFile = File.createTempFile(uuid4().toString(), ".temp").apply {
deleteOnExit()
}
download(outputFile)
return outputFile
}
suspend fun PartData.BinaryChannelItem.download(target: File) {
val input = provider()
target.outputStream().use {
input.copyTo(it)
}
}
suspend fun PartData.BinaryChannelItem.downloadToTemporalFile(): File {
val outputFile = File.createTempFile(uuid4().toString(), ".temp").apply {
deleteOnExit()
}
download(outputFile)
return outputFile
}

View File

@@ -1,61 +0,0 @@
package dev.inmo.micro_utils.ktor.server
import dev.inmo.micro_utils.coroutines.safely
import dev.inmo.micro_utils.ktor.common.*
import io.ktor.http.URLProtocol
import io.ktor.server.application.install
import io.ktor.server.application.pluginOrNull
import io.ktor.server.routing.Route
import io.ktor.server.routing.application
import io.ktor.server.websocket.*
import io.ktor.websocket.send
import kotlinx.coroutines.flow.Flow
import kotlinx.serialization.SerializationStrategy
@Deprecated("This method will be removed soon")
fun <T> Route.includeWebsocketHandling(
suburl: String,
flow: Flow<T>,
protocol: URLProtocol? = null,
converter: suspend WebSocketServerSession.(T) -> StandardKtorSerialInputData?
) {
application.apply {
pluginOrNull(WebSockets) ?: install(WebSockets)
}
webSocket(suburl, protocol ?.name) {
safely {
flow.collect {
converter(it) ?.let { data ->
send(data)
}
}
}
}
}
@Deprecated("This method will be removed soon")
fun <T> Route.includeWebsocketHandling(
suburl: String,
flow: Flow<T>,
serializer: SerializationStrategy<T>,
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat,
protocol: URLProtocol? = null,
filter: (suspend WebSocketServerSession.(T) -> Boolean)? = null
) = includeWebsocketHandling(
suburl,
flow,
protocol,
converter = if (filter == null) {
{
serialFormat.encodeDefault(serializer, it)
}
} else {
{
if (filter(it)) {
serialFormat.encodeDefault(serializer, it)
} else {
null
}
}
}
)

View File

@@ -0,0 +1,25 @@
package dev.inmo.micro_utils.ktor.server
import io.ktor.http.HttpStatusCode
import io.ktor.server.application.ApplicationCall
import io.ktor.server.response.respond
suspend fun ApplicationCall.getParameterOrSendError(
field: String
) = parameters[field].also {
if (it == null) {
respond(HttpStatusCode.BadRequest, "Request must contains $field")
}
}
fun ApplicationCall.getQueryParameter(
field: String
) = request.queryParameters[field]
suspend fun ApplicationCall.getQueryParameterOrSendError(
field: String
) = getQueryParameter(field).also {
if (it == null) {
respond(HttpStatusCode.BadRequest, "Request query parameters must contains $field")
}
}

View File

@@ -1,308 +0,0 @@
package dev.inmo.micro_utils.ktor.server
import dev.inmo.micro_utils.common.*
import dev.inmo.micro_utils.coroutines.safely
import dev.inmo.micro_utils.ktor.common.*
import io.ktor.http.*
import io.ktor.http.content.*
import io.ktor.server.application.ApplicationCall
import io.ktor.server.application.call
import io.ktor.server.request.receive
import io.ktor.server.request.receiveMultipart
import io.ktor.server.response.respond
import io.ktor.server.response.respondBytes
import io.ktor.server.routing.Route
import io.ktor.server.websocket.WebSocketServerSession
import io.ktor.util.pipeline.PipelineContext
import io.ktor.utils.io.core.*
import kotlinx.coroutines.flow.Flow
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.SerializationStrategy
@Deprecated("This class method will be removed soon. It is now recommended to use built-in ktor features instead")
class UnifiedRouter(
val serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat,
val serialFormatContentType: ContentType = standardKtorSerialFormatContentType
) {
fun <T> Route.includeWebsocketHandling(
suburl: String,
flow: Flow<T>,
serializer: SerializationStrategy<T>,
protocol: URLProtocol? = null,
filter: (suspend WebSocketServerSession.(T) -> Boolean)? = null
) = includeWebsocketHandling(suburl, flow, serializer, serialFormat, protocol, filter)
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")
}
}
companion object {
val default
get() = defaultUnifiedRouter
}
}
val defaultUnifiedRouter = UnifiedRouter()
@Deprecated("This method will be removed soon. It is now recommended to use built-in ktor features instead")
suspend fun <T> ApplicationCall.unianswer(
answerSerializer: SerializationStrategy<T>,
answer: T
) {
respondBytes (
standardKtorSerialFormat.encodeDefault(answerSerializer, answer),
standardKtorSerialFormatContentType
)
}
@Deprecated("This method will be removed soon. It is now recommended to use built-in ktor features instead")
suspend fun <T> ApplicationCall.uniload(
deserializer: DeserializationStrategy<T>
) = safely {
standardKtorSerialFormat.decodeDefault(
deserializer,
receive()
)
}
suspend fun ApplicationCall.uniloadMultipart(
onFormItem: (PartData.FormItem) -> Unit = {},
onCustomFileItem: (PartData.FileItem) -> Unit = {},
onBinaryChannelItem: (PartData.BinaryChannelItem) -> Unit = {},
onBinaryContent: (PartData.BinaryItem) -> Unit = {}
) = safely {
val multipartData = receiveMultipart()
var resultInput: Input? = null
multipartData.forEachPart {
when (it) {
is PartData.FormItem -> onFormItem(it)
is PartData.FileItem -> {
when (it.name) {
"bytes" -> resultInput = it.provider()
else -> onCustomFileItem(it)
}
}
is PartData.BinaryItem -> onBinaryContent(it)
is PartData.BinaryChannelItem -> onBinaryChannelItem(it)
}
}
resultInput ?: error("Bytes has not been received")
}
@Deprecated("This method will be removed soon. It is now recommended to use built-in ktor features instead")
suspend fun <T> ApplicationCall.uniloadMultipart(
deserializer: DeserializationStrategy<T>,
onFormItem: (PartData.FormItem) -> Unit = {},
onCustomFileItem: (PartData.FileItem) -> Unit = {},
onBinaryChannelItem: (PartData.BinaryChannelItem) -> Unit = {},
onBinaryContent: (PartData.BinaryItem) -> Unit = {}
): Pair<Input, T> {
var data: Optional<T>? = null
val resultInput = uniloadMultipart(
onFormItem,
{
if (it.name == "data") {
data = standardKtorSerialFormat.decodeDefault(deserializer, it.provider().readBytes()).optional
} else {
onCustomFileItem(it)
}
},
onBinaryChannelItem,
onBinaryContent
)
val completeData = data ?: error("Data has not been received")
return resultInput to (completeData.dataOrNull().let { it as T })
}
@Deprecated("This method will be removed soon. It is now recommended to use built-in ktor features instead")
suspend fun <T> ApplicationCall.uniloadMultipartFile(
deserializer: DeserializationStrategy<T>,
onFormItem: (PartData.FormItem) -> Unit = {},
onCustomFileItem: (PartData.FileItem) -> Unit = {},
onBinaryChannelItem: (PartData.BinaryChannelItem) -> Unit = {},
onBinaryContent: (PartData.BinaryItem) -> Unit = {},
) = safely {
val multipartData = receiveMultipart()
var resultInput: MPPFile? = null
var data: Optional<T>? = null
multipartData.forEachPart {
when (it) {
is PartData.FormItem -> onFormItem(it)
is PartData.FileItem -> {
when (it.name) {
"bytes" -> {
val name = FileName(it.originalFileName ?: error("File name is unknown for default part"))
resultInput = MPPFile.createTempFile(
name.nameWithoutExtension.let {
var resultName = it
while (resultName.length < 3) {
resultName += "_"
}
resultName
},
".${name.extension}"
).apply {
outputStream().use { fileStream ->
it.streamProvider().use {
it.copyTo(fileStream)
}
}
}
}
"data" -> data = standardKtorSerialFormat.decodeDefault(deserializer, it.provider().readBytes()).optional
else -> onCustomFileItem(it)
}
}
is PartData.BinaryItem -> onBinaryContent(it)
is PartData.BinaryChannelItem -> onBinaryChannelItem(it)
}
}
val completeData = data ?: error("Data has not been received")
(resultInput ?: error("Bytes has not been received")) to (completeData.dataOrNull().let { it as T })
}
suspend fun ApplicationCall.uniloadMultipartFile(
onFormItem: (PartData.FormItem) -> Unit = {},
onCustomFileItem: (PartData.FileItem) -> Unit = {},
onBinaryChannelItem: (PartData.BinaryChannelItem) -> Unit = {},
onBinaryContent: (PartData.BinaryItem) -> Unit = {},
) = safely {
val multipartData = receiveMultipart()
var resultInput: MPPFile? = null
multipartData.forEachPart {
when (it) {
is PartData.FormItem -> onFormItem(it)
is PartData.FileItem -> {
if (it.name == "bytes") {
val name = FileName(it.originalFileName ?: error("File name is unknown for default part"))
resultInput = MPPFile.createTempFile(
name.nameWithoutExtension.let {
var resultName = it
while (resultName.length < 3) {
resultName += "_"
}
resultName
},
".${name.extension}"
).apply {
outputStream().use { fileStream ->
it.streamProvider().use {
it.copyTo(fileStream)
}
}
}
} else {
onCustomFileItem(it)
}
}
is PartData.BinaryItem -> onBinaryContent(it)
is PartData.BinaryChannelItem -> onBinaryChannelItem(it)
}
}
resultInput ?: error("Bytes has not been received")
}
suspend fun ApplicationCall.getParameterOrSendError(
field: String
) = parameters[field].also {
if (it == null) {
respond(HttpStatusCode.BadRequest, "Request must contains $field")
}
}
fun ApplicationCall.getQueryParameter(
field: String
) = request.queryParameters[field]
suspend fun ApplicationCall.getQueryParameterOrSendError(
field: String
) = getQueryParameter(field).also {
if (it == null) {
respond(HttpStatusCode.BadRequest, "Request query parameters must contains $field")
}
}
@Deprecated("This method will be removed soon. It is now recommended to use built-in ktor features instead")
fun <T> ApplicationCall.decodeUrlQueryValue(
field: String,
deserializer: DeserializationStrategy<T>
) = getQueryParameter(field) ?.let {
standardKtorSerialFormat.decodeHex(
deserializer,
it
)
}
@Deprecated("This method will be removed soon. It is now recommended to use built-in ktor features instead")
suspend fun <T> ApplicationCall.decodeUrlQueryValueOrSendError(
field: String,
deserializer: DeserializationStrategy<T>
) = decodeUrlQueryValue(field, deserializer).also {
if (it == null) {
respond(HttpStatusCode.BadRequest, "Request query parameters must contains $field")
}
}

View File

@@ -26,7 +26,6 @@ import java.nio.file.attribute.FileTime
class TemporalFilesRoutingConfigurator(
private val subpath: String = DefaultTemporalFilesSubPath,
private val unifiedRouter: UnifiedRouter = UnifiedRouter.default,
private val temporalFilesUtilizer: TemporalFilesUtilizer = TemporalFilesUtilizer
) : ApplicationRoutingConfigurator.Element {
interface TemporalFilesUtilizer {
@@ -80,42 +79,40 @@ class TemporalFilesRoutingConfigurator(
override fun Route.invoke() {
post(subpath) {
unifiedRouter.apply {
val multipart = call.receiveMultipart()
val multipart = call.receiveMultipart()
var fileInfo: Pair<TemporalFileId, MPPFile>? = null
var part = multipart.readPart()
var fileInfo: Pair<TemporalFileId, MPPFile>? = null
var part = multipart.readPart()
while (part != null) {
if (part is PartData.FileItem) {
break
}
part = multipart.readPart()
while (part != null) {
if (part is PartData.FileItem) {
break
}
part ?.let {
if (it is PartData.FileItem) {
val fileId = TemporalFileId(uuid4().toString())
val fileName = it.originalFileName ?.let { FileName(it) } ?: return@let
fileInfo = fileId to File.createTempFile(fileId.string, ".${fileName.extension}").apply {
outputStream().use { outputStream ->
it.streamProvider().use {
it.copyTo(outputStream)
}
}
deleteOnExit()
}
}
}
fileInfo ?.also { (fileId, file) ->
temporalFilesMutex.withLock {
temporalFilesMap[fileId] = file
}
call.respondText(fileId.string)
launchSafelyWithoutExceptions { filesFlow.emit(fileId) }
} ?: call.respond(HttpStatusCode.BadRequest)
part = multipart.readPart()
}
part ?.let {
if (it is PartData.FileItem) {
val fileId = TemporalFileId(uuid4().toString())
val fileName = it.originalFileName ?.let { FileName(it) } ?: return@let
fileInfo = fileId to File.createTempFile(fileId.string, ".${fileName.extension}").apply {
outputStream().use { outputStream ->
it.streamProvider().use {
it.copyTo(outputStream)
}
}
deleteOnExit()
}
}
}
fileInfo ?.also { (fileId, file) ->
temporalFilesMutex.withLock {
temporalFilesMap[fileId] = file
}
call.respondText(fileId.string)
launchSafelyWithoutExceptions { filesFlow.emit(fileId) }
} ?: call.respond(HttpStatusCode.BadRequest)
}
}

View File

@@ -0,0 +1,70 @@
package dev.inmo.micro_utils.ktor.server
import dev.inmo.micro_utils.common.*
import dev.inmo.micro_utils.coroutines.safely
import dev.inmo.micro_utils.ktor.common.downloadToTempFile
import io.ktor.http.content.*
import io.ktor.server.application.ApplicationCall
import io.ktor.server.request.receiveMultipart
import io.ktor.utils.io.core.*
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.isActive
/**
* Server-side part which receives [dev.inmo.micro_utils.ktor.client.uniUpload] request
*/
suspend inline fun ApplicationCall.handleUniUpload(
onFormItem: (PartData.FormItem) -> Unit = {},
onBinaryContent: (PartData.BinaryItem) -> Unit = {},
onBinaryChannelItem: (PartData.BinaryChannelItem) -> Unit = {},
onFileItem: (PartData.FileItem) -> Unit = {}
) {
val multipartData = receiveMultipart()
while (currentCoroutineContext().isActive) {
val partData = multipartData.readPart() ?: break
when (partData) {
is PartData.FormItem -> onFormItem(partData)
is PartData.FileItem -> onFileItem(partData)
is PartData.BinaryItem -> onBinaryContent(partData)
is PartData.BinaryChannelItem -> onBinaryChannelItem(partData)
}
partData.dispose()
}
}
suspend fun ApplicationCall.uniloadMultipart(
onFormItem: (PartData.FormItem) -> Unit = {},
onCustomFileItem: (PartData.FileItem) -> Unit = {},
onBinaryChannelItem: (PartData.BinaryChannelItem) -> Unit = {},
onBinaryContent: (PartData.BinaryItem) -> Unit = {}
): Input = safely {
var resultInput: Input? = null
handleUniUpload(
onFormItem,
onBinaryContent,
onBinaryChannelItem
) {
when (it.name) {
"bytes" -> resultInput = it.provider()
else -> onCustomFileItem(it)
}
}
resultInput ?: error("Bytes has not been received")
}
suspend fun ApplicationCall.uniloadMultipartFile(
onFormItem: (PartData.FormItem) -> Unit = {},
onCustomFileItem: (PartData.FileItem) -> Unit = {},
onBinaryChannelItem: (PartData.BinaryChannelItem) -> Unit = {},
onBinaryContent: (PartData.BinaryItem) -> Unit = {},
): MPPFile = safely {
uniloadMultipart(
onFormItem,
onCustomFileItem,
onBinaryChannelItem,
onBinaryContent
).downloadToTempFile()
}

View File

@@ -78,12 +78,14 @@ private fun printLanguageCodeAndTags(
indents: String = " "
): String = if (tag.subtags.isEmpty()) {
"""${indents}${baseClassSerializerAnnotationName}
${indents}object ${tag.title} : ${parent ?.title ?: baseClassName}() { override val code: String = "${tag.tag}" }"""
${indents}object ${tag.title} : ${parent ?.title ?: baseClassName}() { override val code: String = "${tag.tag}"; override val withoutDialect: String get() = ${parent ?.title ?.let { "$it.code" } ?: "code"} }"""
} else {
"""
${indents}${baseClassSerializerAnnotationName}
${indents}sealed class ${tag.title} : ${parent ?.title ?: baseClassName}() {
${indents} override val code: String = "${tag.tag}"
${indents} override val withoutDialect: String
${indents} get() = code
${tag.subtags.joinToString("\n") { printLanguageCodeAndTags(it, tag, "${indents} ") }}
@@ -104,11 +106,14 @@ import kotlinx.serialization.Serializable
${baseClassSerializerAnnotationName}
sealed class $baseClassName {
abstract val code: String
abstract val withoutDialect: String
${tags.joinToString("\n") { printLanguageCodeAndTags(it, indents = " ") } }
$baseClassSerializerAnnotationName
data class $unknownBaseClassName (override val code: String) : $baseClassName()
data class $unknownBaseClassName (override val code: String) : $baseClassName() {
override val withoutDialect: String = code.takeWhile { it != '-' }
}
override fun toString() = code
}

View File

@@ -17,10 +17,11 @@ def fix_name(category, raw_name):
result += out1
return result
# https://www.freeformatter.com/mime-types-list.html
if __name__ == '__main__':
df = pd.read_html(open('table.html', 'r'))
mimes = []
for row in df[0].iterrows():
for row in df[0].drop_duplicates(subset=['MIME Type / Internet Media Type'], keep='first').iterrows():
mime = row[1][1]
mime_category = mime.split('/', 1)[0]
mime_name = mime.split('/', 1)[1]
@@ -37,7 +38,7 @@ if __name__ == '__main__':
code2 = 'internal val knownMimeTypes: Set<MimeType> = setOf(\n'
code2 += ' KnownMimeTypes.Any,\n'
for key, group in grouped:
group_name = key.capitalize()
group_name = fix_name(group, key)
code += '@Serializable(MimeTypeSerializer::class)\nsealed class %s(raw: String) : MimeType, KnownMimeTypes(raw) {\n' % group_name
code += ' @Serializable(MimeTypeSerializer::class)\n object Any: %s ("%s/*")\n' % (group_name, key)
for mime in group:

View File

@@ -0,0 +1,3 @@
package dev.inmo.micro_utils.mime_types
data class CustomMimeType(override val raw: String) : MimeType

View File

@@ -0,0 +1,705 @@
package dev.inmo.micro_utils.mime_types
internal val knownMimeTypes: Set<MimeType> = setOf(
KnownMimeTypes.Any,
KnownMimeTypes.Application.VndHzn3dCrossword,
KnownMimeTypes.Application.VndMseq,
KnownMimeTypes.Application.Vnd3mPostItNotes,
KnownMimeTypes.Application.Vnd3gppPicBwLarge,
KnownMimeTypes.Application.Vnd3gppPicBwSmall,
KnownMimeTypes.Application.Vnd3gppPicBwVar,
KnownMimeTypes.Application.Vnd3gpp2Tcap,
KnownMimeTypes.Application.X7zCompressed,
KnownMimeTypes.Application.XAbiword,
KnownMimeTypes.Application.XAceCompressed,
KnownMimeTypes.Application.VndAmericandynamicsAcc,
KnownMimeTypes.Application.VndAcucobol,
KnownMimeTypes.Application.VndAcucorp,
KnownMimeTypes.Application.XAuthorwareBin,
KnownMimeTypes.Application.XAuthorwareMap,
KnownMimeTypes.Application.XAuthorwareSeg,
KnownMimeTypes.Application.VndAdobeAirApplicationInstallerPackageZip,
KnownMimeTypes.Application.XShockwaveFlash,
KnownMimeTypes.Application.VndAdobeFxp,
KnownMimeTypes.Application.Pdf,
KnownMimeTypes.Application.VndCupsPpd,
KnownMimeTypes.Application.XDirector,
KnownMimeTypes.Application.VndAdobeXdpXml,
KnownMimeTypes.Application.VndAdobeXfdf,
KnownMimeTypes.Application.VndAheadSpace,
KnownMimeTypes.Application.VndAirzipFilesecureAzf,
KnownMimeTypes.Application.VndAirzipFilesecureAzs,
KnownMimeTypes.Application.VndAmazonEbook,
KnownMimeTypes.Application.VndAmigaAmi,
KnownMimeTypes.Application.AndrewInset,
KnownMimeTypes.Application.VndAndroidPackageArchive,
KnownMimeTypes.Application.VndAnserWebCertificateIssueInitiation,
KnownMimeTypes.Application.VndAnserWebFundsTransferInitiation,
KnownMimeTypes.Application.VndAntixGameComponent,
KnownMimeTypes.Application.XAppleDiskimage,
KnownMimeTypes.Application.VndAppleInstallerXml,
KnownMimeTypes.Application.Applixware,
KnownMimeTypes.Application.VndHheLessonPlayer,
KnownMimeTypes.Application.XFreearc,
KnownMimeTypes.Application.VndAristanetworksSwi,
KnownMimeTypes.Application.AtomcatXml,
KnownMimeTypes.Application.AtomsvcXml,
KnownMimeTypes.Application.AtomXml,
KnownMimeTypes.Application.PkixAttrCert,
KnownMimeTypes.Application.VndAudiograph,
KnownMimeTypes.Application.XBcpio,
KnownMimeTypes.Application.OctetStream,
KnownMimeTypes.Application.XBittorrent,
KnownMimeTypes.Application.VndRimCod,
KnownMimeTypes.Application.VndBlueiceMultipass,
KnownMimeTypes.Application.VndBmi,
KnownMimeTypes.Application.XSh,
KnownMimeTypes.Application.VndBusinessobjects,
KnownMimeTypes.Application.XBzip,
KnownMimeTypes.Application.XBzip2,
KnownMimeTypes.Application.XCsh,
KnownMimeTypes.Application.VndChemdrawXml,
KnownMimeTypes.Application.XCdf,
KnownMimeTypes.Application.VndContactCmsg,
KnownMimeTypes.Application.VndClaymore,
KnownMimeTypes.Application.VndClonkC4group,
KnownMimeTypes.Application.CdmiCapability,
KnownMimeTypes.Application.CdmiContainer,
KnownMimeTypes.Application.CdmiDomain,
KnownMimeTypes.Application.CdmiObject,
KnownMimeTypes.Application.CdmiQueue,
KnownMimeTypes.Application.VndCluetrustCartomobileConfig,
KnownMimeTypes.Application.VndCluetrustCartomobileConfigPkg,
KnownMimeTypes.Application.MacCompactpro,
KnownMimeTypes.Application.VndWapWmlc,
KnownMimeTypes.Application.VndXara,
KnownMimeTypes.Application.VndCosmocaller,
KnownMimeTypes.Application.XCpio,
KnownMimeTypes.Application.VndCrickClicker,
KnownMimeTypes.Application.VndCrickClickerKeyboard,
KnownMimeTypes.Application.VndCrickClickerPalette,
KnownMimeTypes.Application.VndCrickClickerTemplate,
KnownMimeTypes.Application.VndCrickClickerWordbank,
KnownMimeTypes.Application.VndCriticaltoolsWbsXml,
KnownMimeTypes.Application.VndRigCryptonote,
KnownMimeTypes.Application.CuSeeme,
KnownMimeTypes.Application.PrsCww,
KnownMimeTypes.Application.VndCurlCar,
KnownMimeTypes.Application.VndCurlPcurl,
KnownMimeTypes.Application.VndYellowriverCustomMenu,
KnownMimeTypes.Application.DsscDer,
KnownMimeTypes.Application.DsscXml,
KnownMimeTypes.Application.XDebianPackage,
KnownMimeTypes.Application.XDvi,
KnownMimeTypes.Application.VndFdsnSeed,
KnownMimeTypes.Application.XDtbookXml,
KnownMimeTypes.Application.XDtbresourceXml,
KnownMimeTypes.Application.VndDvbAit,
KnownMimeTypes.Application.VndDvbService,
KnownMimeTypes.Application.XmlDtd,
KnownMimeTypes.Application.VndDolbyMlp,
KnownMimeTypes.Application.XDoom,
KnownMimeTypes.Application.VndDpgraph,
KnownMimeTypes.Application.VndDreamfactory,
KnownMimeTypes.Application.VndDynageo,
KnownMimeTypes.Application.Ecmascript,
KnownMimeTypes.Application.VndEcowinChart,
KnownMimeTypes.Application.Exi,
KnownMimeTypes.Application.VndProteusMagazine,
KnownMimeTypes.Application.EpubZip,
KnownMimeTypes.Application.VndEnliven,
KnownMimeTypes.Application.VndIsXpr,
KnownMimeTypes.Application.VndXfdl,
KnownMimeTypes.Application.EmmaXml,
KnownMimeTypes.Application.VndEzpixAlbum,
KnownMimeTypes.Application.VndEzpixPackage,
KnownMimeTypes.Application.VndDenovoFcselayoutLink,
KnownMimeTypes.Application.VndFluxtimeClip,
KnownMimeTypes.Application.VndFdf,
KnownMimeTypes.Application.VndMif,
KnownMimeTypes.Application.VndFramemaker,
KnownMimeTypes.Application.VndFscWeblaunch,
KnownMimeTypes.Application.VndFrogansFnc,
KnownMimeTypes.Application.VndFrogansLtf,
KnownMimeTypes.Application.VndFujixeroxDdd,
KnownMimeTypes.Application.VndFujixeroxDocuworks,
KnownMimeTypes.Application.VndFujixeroxDocuworksBinder,
KnownMimeTypes.Application.VndFujitsuOasys,
KnownMimeTypes.Application.VndFujitsuOasys2,
KnownMimeTypes.Application.VndFujitsuOasys3,
KnownMimeTypes.Application.VndFujitsuOasysgp,
KnownMimeTypes.Application.VndFujitsuOasysprs,
KnownMimeTypes.Application.XFuturesplash,
KnownMimeTypes.Application.VndFuzzysheet,
KnownMimeTypes.Application.VndGmx,
KnownMimeTypes.Application.VndGenomatixTuxedo,
KnownMimeTypes.Application.VndGeogebraFile,
KnownMimeTypes.Application.VndGeogebraTool,
KnownMimeTypes.Application.VndGeometryExplorer,
KnownMimeTypes.Application.VndGeonext,
KnownMimeTypes.Application.VndGeoplan,
KnownMimeTypes.Application.VndGeospace,
KnownMimeTypes.Application.XFontGhostscript,
KnownMimeTypes.Application.XFontBdf,
KnownMimeTypes.Application.XGtar,
KnownMimeTypes.Application.XTexinfo,
KnownMimeTypes.Application.XGnumeric,
KnownMimeTypes.Application.VndGoogleEarthKmlXml,
KnownMimeTypes.Application.VndGoogleEarthKmz,
KnownMimeTypes.Application.GpxXml,
KnownMimeTypes.Application.VndGrafeq,
KnownMimeTypes.Application.VndGrooveAccount,
KnownMimeTypes.Application.VndGrooveHelp,
KnownMimeTypes.Application.VndGrooveIdentityMessage,
KnownMimeTypes.Application.VndGrooveInjector,
KnownMimeTypes.Application.VndGrooveToolMessage,
KnownMimeTypes.Application.VndGrooveToolTemplate,
KnownMimeTypes.Application.VndGrooveVcard,
KnownMimeTypes.Application.Gzip,
KnownMimeTypes.Application.VndHpHpid,
KnownMimeTypes.Application.VndHpHps,
KnownMimeTypes.Application.XHdf,
KnownMimeTypes.Application.VndHbci,
KnownMimeTypes.Application.VndHpJlyt,
KnownMimeTypes.Application.VndHpPcl,
KnownMimeTypes.Application.VndHpHpgl,
KnownMimeTypes.Application.VndYamahaHvScript,
KnownMimeTypes.Application.VndYamahaHvDic,
KnownMimeTypes.Application.VndYamahaHvVoice,
KnownMimeTypes.Application.VndHydrostatixSofData,
KnownMimeTypes.Application.Hyperstudio,
KnownMimeTypes.Application.VndHalXml,
KnownMimeTypes.Application.VndIbmRightsManagement,
KnownMimeTypes.Application.VndIbmSecureContainer,
KnownMimeTypes.Application.VndIccprofile,
KnownMimeTypes.Application.VndIgloader,
KnownMimeTypes.Application.VndImmervisionIvp,
KnownMimeTypes.Application.VndImmervisionIvu,
KnownMimeTypes.Application.ReginfoXml,
KnownMimeTypes.Application.VndIntergeo,
KnownMimeTypes.Application.VndCinderella,
KnownMimeTypes.Application.VndInterconFormnet,
KnownMimeTypes.Application.VndIsacFcs,
KnownMimeTypes.Application.Ipfix,
KnownMimeTypes.Application.PkixCert,
KnownMimeTypes.Application.Pkixcmp,
KnownMimeTypes.Application.PkixCrl,
KnownMimeTypes.Application.PkixPkipath,
KnownMimeTypes.Application.VndInsorsIgm,
KnownMimeTypes.Application.VndIpunpluggedRcprofile,
KnownMimeTypes.Application.VndIrepositoryPackageXml,
KnownMimeTypes.Application.JavaArchive,
KnownMimeTypes.Application.JavaVm,
KnownMimeTypes.Application.XJavaJnlpFile,
KnownMimeTypes.Application.JavaSerializedObject,
KnownMimeTypes.Application.Javascript,
KnownMimeTypes.Application.Json,
KnownMimeTypes.Application.VndJoostJodaArchive,
KnownMimeTypes.Application.LdJson,
KnownMimeTypes.Application.VndKahootz,
KnownMimeTypes.Application.VndChipnutsKaraokeMmd,
KnownMimeTypes.Application.VndKdeKarbon,
KnownMimeTypes.Application.VndKdeKchart,
KnownMimeTypes.Application.VndKdeKformula,
KnownMimeTypes.Application.VndKdeKivio,
KnownMimeTypes.Application.VndKdeKontour,
KnownMimeTypes.Application.VndKdeKpresenter,
KnownMimeTypes.Application.VndKdeKspread,
KnownMimeTypes.Application.VndKdeKword,
KnownMimeTypes.Application.VndKenameaapp,
KnownMimeTypes.Application.VndKidspiration,
KnownMimeTypes.Application.VndKinar,
KnownMimeTypes.Application.VndKodakDescriptor,
KnownMimeTypes.Application.VndLasLasXml,
KnownMimeTypes.Application.XLatex,
KnownMimeTypes.Application.VndLlamagraphicsLifeBalanceDesktop,
KnownMimeTypes.Application.VndLlamagraphicsLifeBalanceExchangeXml,
KnownMimeTypes.Application.VndJam,
KnownMimeTypes.Application.VndLotus123,
KnownMimeTypes.Application.VndLotusApproach,
KnownMimeTypes.Application.VndLotusFreelance,
KnownMimeTypes.Application.VndLotusNotes,
KnownMimeTypes.Application.VndLotusOrganizer,
KnownMimeTypes.Application.VndLotusScreencam,
KnownMimeTypes.Application.VndLotusWordpro,
KnownMimeTypes.Application.MacBinhex40,
KnownMimeTypes.Application.VndMacportsPortpkg,
KnownMimeTypes.Application.VndOsgeoMapguidePackage,
KnownMimeTypes.Application.Marc,
KnownMimeTypes.Application.MarcxmlXml,
KnownMimeTypes.Application.Mxf,
KnownMimeTypes.Application.VndWolframPlayer,
KnownMimeTypes.Application.Mathematica,
KnownMimeTypes.Application.MathmlXml,
KnownMimeTypes.Application.Mbox,
KnownMimeTypes.Application.VndMedcalcdata,
KnownMimeTypes.Application.MediaservercontrolXml,
KnownMimeTypes.Application.VndMediastationCdkey,
KnownMimeTypes.Application.VndMfer,
KnownMimeTypes.Application.VndMfmp,
KnownMimeTypes.Application.MadsXml,
KnownMimeTypes.Application.MetsXml,
KnownMimeTypes.Application.ModsXml,
KnownMimeTypes.Application.Metalink4Xml,
KnownMimeTypes.Application.VndMcd,
KnownMimeTypes.Application.VndMicrografxFlo,
KnownMimeTypes.Application.VndMicrografxIgx,
KnownMimeTypes.Application.VndEszigno3Xml,
KnownMimeTypes.Application.XMsaccess,
KnownMimeTypes.Application.XMsdownload,
KnownMimeTypes.Application.VndMsArtgalry,
KnownMimeTypes.Application.VndMsCabCompressed,
KnownMimeTypes.Application.VndMsIms,
KnownMimeTypes.Application.XMsApplication,
KnownMimeTypes.Application.XMsclip,
KnownMimeTypes.Application.VndMsFontobject,
KnownMimeTypes.Application.VndMsExcel,
KnownMimeTypes.Application.VndMsExcelAddinMacroenabled12,
KnownMimeTypes.Application.VndMsExcelSheetBinaryMacroenabled12,
KnownMimeTypes.Application.VndMsExcelTemplateMacroenabled12,
KnownMimeTypes.Application.VndMsExcelSheetMacroenabled12,
KnownMimeTypes.Application.VndMsHtmlhelp,
KnownMimeTypes.Application.XMscardfile,
KnownMimeTypes.Application.VndMsLrm,
KnownMimeTypes.Application.XMsmediaview,
KnownMimeTypes.Application.XMsmoney,
KnownMimeTypes.Application.VndOpenxmlformatsOfficedocumentPresentationmlPresentation,
KnownMimeTypes.Application.VndOpenxmlformatsOfficedocumentPresentationmlSlide,
KnownMimeTypes.Application.VndOpenxmlformatsOfficedocumentPresentationmlSlideshow,
KnownMimeTypes.Application.VndOpenxmlformatsOfficedocumentPresentationmlTemplate,
KnownMimeTypes.Application.VndOpenxmlformatsOfficedocumentSpreadsheetmlSheet,
KnownMimeTypes.Application.VndOpenxmlformatsOfficedocumentSpreadsheetmlTemplate,
KnownMimeTypes.Application.VndOpenxmlformatsOfficedocumentWordprocessingmlDocument,
KnownMimeTypes.Application.VndOpenxmlformatsOfficedocumentWordprocessingmlTemplate,
KnownMimeTypes.Application.XMsbinder,
KnownMimeTypes.Application.VndMsOfficetheme,
KnownMimeTypes.Application.Onenote,
KnownMimeTypes.Application.VndMsPowerpoint,
KnownMimeTypes.Application.VndMsPowerpointAddinMacroenabled12,
KnownMimeTypes.Application.VndMsPowerpointSlideMacroenabled12,
KnownMimeTypes.Application.VndMsPowerpointPresentationMacroenabled12,
KnownMimeTypes.Application.VndMsPowerpointSlideshowMacroenabled12,
KnownMimeTypes.Application.VndMsPowerpointTemplateMacroenabled12,
KnownMimeTypes.Application.VndMsProject,
KnownMimeTypes.Application.XMspublisher,
KnownMimeTypes.Application.XMsschedule,
KnownMimeTypes.Application.XSilverlightApp,
KnownMimeTypes.Application.VndMsPkiStl,
KnownMimeTypes.Application.VndMsPkiSeccat,
KnownMimeTypes.Application.VndVisio,
KnownMimeTypes.Application.VndVisio2013,
KnownMimeTypes.Application.XMsWmd,
KnownMimeTypes.Application.VndMsWpl,
KnownMimeTypes.Application.XMsWmz,
KnownMimeTypes.Application.XMsmetafile,
KnownMimeTypes.Application.XMsterminal,
KnownMimeTypes.Application.Msword,
KnownMimeTypes.Application.VndMsWordDocumentMacroenabled12,
KnownMimeTypes.Application.VndMsWordTemplateMacroenabled12,
KnownMimeTypes.Application.XMswrite,
KnownMimeTypes.Application.VndMsWorks,
KnownMimeTypes.Application.XMsXbap,
KnownMimeTypes.Application.VndMsXpsdocument,
KnownMimeTypes.Application.VndIbmMinipay,
KnownMimeTypes.Application.VndIbmModcap,
KnownMimeTypes.Application.VndJcpJavameMidletRms,
KnownMimeTypes.Application.VndTmobileLivetv,
KnownMimeTypes.Application.XMobipocketEbook,
KnownMimeTypes.Application.VndMobiusMbk,
KnownMimeTypes.Application.VndMobiusDis,
KnownMimeTypes.Application.VndMobiusPlc,
KnownMimeTypes.Application.VndMobiusMqy,
KnownMimeTypes.Application.VndMobiusMsl,
KnownMimeTypes.Application.VndMobiusTxf,
KnownMimeTypes.Application.VndMobiusDaf,
KnownMimeTypes.Application.VndMophunCertificate,
KnownMimeTypes.Application.VndMophunApplication,
KnownMimeTypes.Application.Mp21,
KnownMimeTypes.Application.Mp4,
KnownMimeTypes.Application.VndAppleMpegurl,
KnownMimeTypes.Application.VndMusician,
KnownMimeTypes.Application.VndMuveeStyle,
KnownMimeTypes.Application.XvXml,
KnownMimeTypes.Application.VndNokiaNGageData,
KnownMimeTypes.Application.VndNokiaNGageSymbianInstall,
KnownMimeTypes.Application.XDtbncxXml,
KnownMimeTypes.Application.XNetcdf,
KnownMimeTypes.Application.VndNeurolanguageNlu,
KnownMimeTypes.Application.VndDna,
KnownMimeTypes.Application.VndNoblenetDirectory,
KnownMimeTypes.Application.VndNoblenetSealer,
KnownMimeTypes.Application.VndNoblenetWeb,
KnownMimeTypes.Application.VndNokiaRadioPreset,
KnownMimeTypes.Application.VndNokiaRadioPresets,
KnownMimeTypes.Application.VndNovadigmEdm,
KnownMimeTypes.Application.VndNovadigmEdx,
KnownMimeTypes.Application.VndNovadigmExt,
KnownMimeTypes.Application.VndFlographit,
KnownMimeTypes.Application.Oda,
KnownMimeTypes.Application.Ogg,
KnownMimeTypes.Application.VndOmaDd2Xml,
KnownMimeTypes.Application.VndOasisOpendocumentTextWeb,
KnownMimeTypes.Application.OebpsPackageXml,
KnownMimeTypes.Application.VndIntuQbo,
KnownMimeTypes.Application.VndOpenofficeorgExtension,
KnownMimeTypes.Application.VndYamahaOpenscoreformat,
KnownMimeTypes.Application.VndOasisOpendocumentChart,
KnownMimeTypes.Application.VndOasisOpendocumentChartTemplate,
KnownMimeTypes.Application.VndOasisOpendocumentDatabase,
KnownMimeTypes.Application.VndOasisOpendocumentFormula,
KnownMimeTypes.Application.VndOasisOpendocumentFormulaTemplate,
KnownMimeTypes.Application.VndOasisOpendocumentGraphics,
KnownMimeTypes.Application.VndOasisOpendocumentGraphicsTemplate,
KnownMimeTypes.Application.VndOasisOpendocumentImage,
KnownMimeTypes.Application.VndOasisOpendocumentImageTemplate,
KnownMimeTypes.Application.VndOasisOpendocumentPresentation,
KnownMimeTypes.Application.VndOasisOpendocumentPresentationTemplate,
KnownMimeTypes.Application.VndOasisOpendocumentSpreadsheet,
KnownMimeTypes.Application.VndOasisOpendocumentSpreadsheetTemplate,
KnownMimeTypes.Application.VndOasisOpendocumentText,
KnownMimeTypes.Application.VndOasisOpendocumentTextMaster,
KnownMimeTypes.Application.VndOasisOpendocumentTextTemplate,
KnownMimeTypes.Application.VndSunXmlCalc,
KnownMimeTypes.Application.VndSunXmlCalcTemplate,
KnownMimeTypes.Application.VndSunXmlDraw,
KnownMimeTypes.Application.VndSunXmlDrawTemplate,
KnownMimeTypes.Application.VndSunXmlImpress,
KnownMimeTypes.Application.VndSunXmlImpressTemplate,
KnownMimeTypes.Application.VndSunXmlMath,
KnownMimeTypes.Application.VndSunXmlWriter,
KnownMimeTypes.Application.VndSunXmlWriterGlobal,
KnownMimeTypes.Application.VndSunXmlWriterTemplate,
KnownMimeTypes.Application.XFontOtf,
KnownMimeTypes.Application.VndYamahaOpenscoreformatOsfpvgXml,
KnownMimeTypes.Application.VndOsgiDp,
KnownMimeTypes.Application.VndPalm,
KnownMimeTypes.Application.VndPawaafile,
KnownMimeTypes.Application.VndHpPclxl,
KnownMimeTypes.Application.VndPicsel,
KnownMimeTypes.Application.PicsRules,
KnownMimeTypes.Application.XChat,
KnownMimeTypes.Application.Pkcs10,
KnownMimeTypes.Application.XPkcs12,
KnownMimeTypes.Application.Pkcs7Mime,
KnownMimeTypes.Application.Pkcs7Signature,
KnownMimeTypes.Application.XPkcs7Certreqresp,
KnownMimeTypes.Application.XPkcs7Certificates,
KnownMimeTypes.Application.Pkcs8,
KnownMimeTypes.Application.VndPocketlearn,
KnownMimeTypes.Application.XFontPcf,
KnownMimeTypes.Application.FontTdpfr,
KnownMimeTypes.Application.XChessPgn,
KnownMimeTypes.Application.PskcXml,
KnownMimeTypes.Application.VndCtcPosml,
KnownMimeTypes.Application.Postscript,
KnownMimeTypes.Application.XFontType1,
KnownMimeTypes.Application.VndPowerbuilder6,
KnownMimeTypes.Application.PgpEncrypted,
KnownMimeTypes.Application.PgpSignature,
KnownMimeTypes.Application.VndPreviewsystemsBox,
KnownMimeTypes.Application.VndPviPtid1,
KnownMimeTypes.Application.PlsXml,
KnownMimeTypes.Application.VndPgFormat,
KnownMimeTypes.Application.VndPgOsasli,
KnownMimeTypes.Application.XFontLinuxPsf,
KnownMimeTypes.Application.VndPublishareDeltaTree,
KnownMimeTypes.Application.VndPmiWidget,
KnownMimeTypes.Application.VndQuarkQuarkxpress,
KnownMimeTypes.Application.VndEpsonEsf,
KnownMimeTypes.Application.VndEpsonMsf,
KnownMimeTypes.Application.VndEpsonSsf,
KnownMimeTypes.Application.VndEpsonQuickanime,
KnownMimeTypes.Application.VndIntuQfx,
KnownMimeTypes.Application.XRarCompressed,
KnownMimeTypes.Application.RsdXml,
KnownMimeTypes.Application.VndRnRealmedia,
KnownMimeTypes.Application.VndRealvncBed,
KnownMimeTypes.Application.VndRecordareMusicxml,
KnownMimeTypes.Application.VndRecordareMusicxmlXml,
KnownMimeTypes.Application.RelaxNgCompactSyntax,
KnownMimeTypes.Application.VndDataVisionRdz,
KnownMimeTypes.Application.RdfXml,
KnownMimeTypes.Application.VndCloantoRp9,
KnownMimeTypes.Application.VndJisp,
KnownMimeTypes.Application.Rtf,
KnownMimeTypes.Application.VndRoute66Link66Xml,
KnownMimeTypes.Application.RssXml,
KnownMimeTypes.Application.ShfXml,
KnownMimeTypes.Application.VndSailingtrackerTrack,
KnownMimeTypes.Application.VndSusCalendar,
KnownMimeTypes.Application.SruXml,
KnownMimeTypes.Application.SetPaymentInitiation,
KnownMimeTypes.Application.SetRegistrationInitiation,
KnownMimeTypes.Application.VndSema,
KnownMimeTypes.Application.VndSemd,
KnownMimeTypes.Application.VndSemf,
KnownMimeTypes.Application.VndSeemail,
KnownMimeTypes.Application.XFontSnf,
KnownMimeTypes.Application.ScvpVpRequest,
KnownMimeTypes.Application.ScvpVpResponse,
KnownMimeTypes.Application.ScvpCvRequest,
KnownMimeTypes.Application.ScvpCvResponse,
KnownMimeTypes.Application.Sdp,
KnownMimeTypes.Application.VndShanaInformedFormdata,
KnownMimeTypes.Application.VndShanaInformedFormtemplate,
KnownMimeTypes.Application.VndShanaInformedInterchange,
KnownMimeTypes.Application.VndShanaInformedPackage,
KnownMimeTypes.Application.ThraudXml,
KnownMimeTypes.Application.XShar,
KnownMimeTypes.Application.VndEpsonSalt,
KnownMimeTypes.Application.VndAccpacSimplyAso,
KnownMimeTypes.Application.VndAccpacSimplyImp,
KnownMimeTypes.Application.VndSimtechMindmapper,
KnownMimeTypes.Application.VndCommonspace,
KnownMimeTypes.Application.VndYamahaSmafAudio,
KnownMimeTypes.Application.VndSmaf,
KnownMimeTypes.Application.VndYamahaSmafPhrase,
KnownMimeTypes.Application.VndSmartTeacher,
KnownMimeTypes.Application.VndSvd,
KnownMimeTypes.Application.SparqlQuery,
KnownMimeTypes.Application.SparqlResultsXml,
KnownMimeTypes.Application.Srgs,
KnownMimeTypes.Application.SrgsXml,
KnownMimeTypes.Application.SsmlXml,
KnownMimeTypes.Application.VndKoan,
KnownMimeTypes.Application.VndStardivisionCalc,
KnownMimeTypes.Application.VndStardivisionDraw,
KnownMimeTypes.Application.VndStardivisionImpress,
KnownMimeTypes.Application.VndStardivisionMath,
KnownMimeTypes.Application.VndStardivisionWriter,
KnownMimeTypes.Application.VndStardivisionWriterGlobal,
KnownMimeTypes.Application.VndStepmaniaStepchart,
KnownMimeTypes.Application.XStuffit,
KnownMimeTypes.Application.XStuffitx,
KnownMimeTypes.Application.VndSolentSdkmXml,
KnownMimeTypes.Application.VndOlpcSugar,
KnownMimeTypes.Application.VndWqd,
KnownMimeTypes.Application.VndSymbianInstall,
KnownMimeTypes.Application.SmilXml,
KnownMimeTypes.Application.VndSyncmlXml,
KnownMimeTypes.Application.VndSyncmlDmWbxml,
KnownMimeTypes.Application.VndSyncmlDmXml,
KnownMimeTypes.Application.XSv4cpio,
KnownMimeTypes.Application.XSv4crc,
KnownMimeTypes.Application.SbmlXml,
KnownMimeTypes.Application.VndTaoIntentModuleArchive,
KnownMimeTypes.Application.XTar,
KnownMimeTypes.Application.XTcl,
KnownMimeTypes.Application.XTex,
KnownMimeTypes.Application.XTexTfm,
KnownMimeTypes.Application.TeiXml,
KnownMimeTypes.Application.VndSpotfireDxp,
KnownMimeTypes.Application.VndSpotfireSfs,
KnownMimeTypes.Application.TimestampedData,
KnownMimeTypes.Application.VndTridTpt,
KnownMimeTypes.Application.VndTriscapeMxs,
KnownMimeTypes.Application.VndTrueapp,
KnownMimeTypes.Application.XFontTtf,
KnownMimeTypes.Application.VndUmajin,
KnownMimeTypes.Application.VndUomlXml,
KnownMimeTypes.Application.VndUnity,
KnownMimeTypes.Application.VndUfdl,
KnownMimeTypes.Application.VndUiqTheme,
KnownMimeTypes.Application.XUstar,
KnownMimeTypes.Application.XCdlink,
KnownMimeTypes.Application.VndVsf,
KnownMimeTypes.Application.VndVcx,
KnownMimeTypes.Application.VndVisionary,
KnownMimeTypes.Application.CcxmlXml,
KnownMimeTypes.Application.VoicexmlXml,
KnownMimeTypes.Application.XWaisSource,
KnownMimeTypes.Application.VndWapWbxml,
KnownMimeTypes.Application.DavmountXml,
KnownMimeTypes.Application.XFontWoff,
KnownMimeTypes.Application.WspolicyXml,
KnownMimeTypes.Application.VndWebturbo,
KnownMimeTypes.Application.Widget,
KnownMimeTypes.Application.Winhlp,
KnownMimeTypes.Application.VndWapWmlscriptc,
KnownMimeTypes.Application.VndWordperfect,
KnownMimeTypes.Application.VndWtStf,
KnownMimeTypes.Application.WsdlXml,
KnownMimeTypes.Application.XX509CaCert,
KnownMimeTypes.Application.XXfig,
KnownMimeTypes.Application.XhtmlXml,
KnownMimeTypes.Application.Xml,
KnownMimeTypes.Application.XcapDiffXml,
KnownMimeTypes.Application.XencXml,
KnownMimeTypes.Application.PatchOpsErrorXml,
KnownMimeTypes.Application.ResourceListsXml,
KnownMimeTypes.Application.RlsServicesXml,
KnownMimeTypes.Application.ResourceListsDiffXml,
KnownMimeTypes.Application.XsltXml,
KnownMimeTypes.Application.XopXml,
KnownMimeTypes.Application.XXpinstall,
KnownMimeTypes.Application.XspfXml,
KnownMimeTypes.Application.VndMozillaXulXml,
KnownMimeTypes.Application.Yang,
KnownMimeTypes.Application.YinXml,
KnownMimeTypes.Application.VndZul,
KnownMimeTypes.Application.Zip,
KnownMimeTypes.Application.VndHandheldEntertainmentXml,
KnownMimeTypes.Application.VndZzazzDeckXml,
KnownMimeTypes.Audio.Adpcm,
KnownMimeTypes.Audio.XAac,
KnownMimeTypes.Audio.XAiff,
KnownMimeTypes.Audio.VndDeceAudio,
KnownMimeTypes.Audio.VndDigitalWinds,
KnownMimeTypes.Audio.VndDra,
KnownMimeTypes.Audio.VndDts,
KnownMimeTypes.Audio.VndDtsHd,
KnownMimeTypes.Audio.VndRip,
KnownMimeTypes.Audio.VndLucentVoice,
KnownMimeTypes.Audio.XMpegurl,
KnownMimeTypes.Audio.VndMsPlayreadyMediaPya,
KnownMimeTypes.Audio.XMsWma,
KnownMimeTypes.Audio.XMsWax,
KnownMimeTypes.Audio.Midi,
KnownMimeTypes.Audio.Mpeg,
KnownMimeTypes.Audio.Mp4,
KnownMimeTypes.Audio.VndNueraEcelp4800,
KnownMimeTypes.Audio.VndNueraEcelp7470,
KnownMimeTypes.Audio.VndNueraEcelp9600,
KnownMimeTypes.Audio.Ogg,
KnownMimeTypes.Audio.Webm,
KnownMimeTypes.Audio.Opus,
KnownMimeTypes.Audio.XPnRealaudio,
KnownMimeTypes.Audio.XPnRealaudioPlugin,
KnownMimeTypes.Audio.Basic,
KnownMimeTypes.Audio.XWav,
KnownMimeTypes.Chemical.XCdx,
KnownMimeTypes.Chemical.XCml,
KnownMimeTypes.Chemical.XCsml,
KnownMimeTypes.Chemical.XCif,
KnownMimeTypes.Chemical.XCmdf,
KnownMimeTypes.Chemical.XXyz,
KnownMimeTypes.Image.VndDxf,
KnownMimeTypes.Image.Avif,
KnownMimeTypes.Image.Bmp,
KnownMimeTypes.Image.PrsBtif,
KnownMimeTypes.Image.VndDvbSubtitle,
KnownMimeTypes.Image.XCmuRaster,
KnownMimeTypes.Image.Cgm,
KnownMimeTypes.Image.XCmx,
KnownMimeTypes.Image.VndDeceGraphic,
KnownMimeTypes.Image.VndDjvu,
KnownMimeTypes.Image.VndDwg,
KnownMimeTypes.Image.VndFujixeroxEdmicsMmr,
KnownMimeTypes.Image.VndFujixeroxEdmicsRlc,
KnownMimeTypes.Image.VndXiff,
KnownMimeTypes.Image.VndFst,
KnownMimeTypes.Image.VndFastbidsheet,
KnownMimeTypes.Image.VndFpx,
KnownMimeTypes.Image.VndNetFpx,
KnownMimeTypes.Image.XFreehand,
KnownMimeTypes.Image.G3fax,
KnownMimeTypes.Image.Gif,
KnownMimeTypes.Image.XIcon,
KnownMimeTypes.Image.Ief,
KnownMimeTypes.Image.Jpeg,
KnownMimeTypes.Image.XCitrixJpeg,
KnownMimeTypes.Image.Pjpeg,
KnownMimeTypes.Image.VndMsModi,
KnownMimeTypes.Image.Ktx,
KnownMimeTypes.Image.XPcx,
KnownMimeTypes.Image.VndAdobePhotoshop,
KnownMimeTypes.Image.XPict,
KnownMimeTypes.Image.XPortableAnymap,
KnownMimeTypes.Image.XPortableBitmap,
KnownMimeTypes.Image.XPortableGraymap,
KnownMimeTypes.Image.Png,
KnownMimeTypes.Image.XCitrixPng,
KnownMimeTypes.Image.XPng,
KnownMimeTypes.Image.XPortablePixmap,
KnownMimeTypes.Image.SvgXml,
KnownMimeTypes.Image.XRgb,
KnownMimeTypes.Image.Tiff,
KnownMimeTypes.Image.VndWapWbmp,
KnownMimeTypes.Image.Webp,
KnownMimeTypes.Image.XXbitmap,
KnownMimeTypes.Image.XXpixmap,
KnownMimeTypes.Image.XXwindowdump,
KnownMimeTypes.Message.Rfc822,
KnownMimeTypes.Model.VndDwf,
KnownMimeTypes.Model.VndColladaXml,
KnownMimeTypes.Model.VndGtw,
KnownMimeTypes.Model.VndGdl,
KnownMimeTypes.Model.Iges,
KnownMimeTypes.Model.Mesh,
KnownMimeTypes.Model.Vrml,
KnownMimeTypes.Model.VndMts,
KnownMimeTypes.Model.VndVtu,
KnownMimeTypes.Text.XAsm,
KnownMimeTypes.Text.PlainBas,
KnownMimeTypes.Text.XC,
KnownMimeTypes.Text.Css,
KnownMimeTypes.Text.Csv,
KnownMimeTypes.Text.VndCurl,
KnownMimeTypes.Text.VndCurlDcurl,
KnownMimeTypes.Text.VndCurlMcurl,
KnownMimeTypes.Text.VndCurlScurl,
KnownMimeTypes.Text.VndFmiFlexstor,
KnownMimeTypes.Text.XFortran,
KnownMimeTypes.Text.VndGraphviz,
KnownMimeTypes.Text.Html,
KnownMimeTypes.Text.Calendar,
KnownMimeTypes.Text.VndIn3d3dml,
KnownMimeTypes.Text.VndIn3dSpot,
KnownMimeTypes.Text.VndSunJ2meAppDescriptor,
KnownMimeTypes.Text.XJavaSourceJava,
KnownMimeTypes.Text.Javascript,
KnownMimeTypes.Text.VndFly,
KnownMimeTypes.Text.N3,
KnownMimeTypes.Text.XPascal,
KnownMimeTypes.Text.PrsLinesTag,
KnownMimeTypes.Text.Richtext,
KnownMimeTypes.Text.XSetext,
KnownMimeTypes.Text.Sgml,
KnownMimeTypes.Text.TabSeparatedValues,
KnownMimeTypes.Text.Plain,
KnownMimeTypes.Text.Troff,
KnownMimeTypes.Text.Turtle,
KnownMimeTypes.Text.UriList,
KnownMimeTypes.Text.XUuencode,
KnownMimeTypes.Text.XVcalendar,
KnownMimeTypes.Text.XVcard,
KnownMimeTypes.Text.VndWapWml,
KnownMimeTypes.Text.VndWapWmlscript,
KnownMimeTypes.Text.Yaml,
KnownMimeTypes.Video.V3gpp,
KnownMimeTypes.Video.V3gpp2,
KnownMimeTypes.Video.XMsvideo,
KnownMimeTypes.Video.VndDeceHd,
KnownMimeTypes.Video.VndDeceMobile,
KnownMimeTypes.Video.VndUvvuMp4,
KnownMimeTypes.Video.VndDecePd,
KnownMimeTypes.Video.VndDeceSd,
KnownMimeTypes.Video.VndDeceVideo,
KnownMimeTypes.Video.VndFvt,
KnownMimeTypes.Video.XF4v,
KnownMimeTypes.Video.XFlv,
KnownMimeTypes.Video.XFli,
KnownMimeTypes.Video.H261,
KnownMimeTypes.Video.H263,
KnownMimeTypes.Video.H264,
KnownMimeTypes.Video.Jpm,
KnownMimeTypes.Video.Jpeg,
KnownMimeTypes.Video.XM4v,
KnownMimeTypes.Video.XMsAsf,
KnownMimeTypes.Video.VndMsPlayreadyMediaPyv,
KnownMimeTypes.Video.XMsWm,
KnownMimeTypes.Video.XMsWmx,
KnownMimeTypes.Video.XMsWmv,
KnownMimeTypes.Video.XMsWvx,
KnownMimeTypes.Video.Mj2,
KnownMimeTypes.Video.Mp2t,
KnownMimeTypes.Video.VndMpegurl,
KnownMimeTypes.Video.Mpeg,
KnownMimeTypes.Video.Mp4,
KnownMimeTypes.Video.Ogg,
KnownMimeTypes.Video.Webm,
KnownMimeTypes.Video.Quicktime,
KnownMimeTypes.Video.XSgiMovie,
KnownMimeTypes.Video.VndVivo,
KnownMimeTypes.XConference.XCooltalk,
)

View File

@@ -0,0 +1,8 @@
package dev.inmo.micro_utils.mime_types
import kotlinx.serialization.Serializable
@Serializable(MimeTypeSerializer::class)
interface MimeType {
val raw: String
}

View File

@@ -0,0 +1,24 @@
package dev.inmo.micro_utils.mime_types
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializer
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
@Suppress("OPT_IN_USAGE")
@Serializer(MimeType::class)
object MimeTypeSerializer : KSerializer<MimeType> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("mimeType", PrimitiveKind.STRING)
override fun deserialize(decoder: Decoder): MimeType {
val mimeType = decoder.decodeString()
return mimeType(mimeType)
}
override fun serialize(encoder: Encoder, value: MimeType) {
encoder.encodeString(value.raw)
}
}

View File

@@ -1,10 +1,5 @@
package dev.inmo.micro_utils.mime_types
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
private val mimesCache = mutableMapOf<String, MimeType>().also {
knownMimeTypes.forEach { mimeType -> it[mimeType.raw] = mimeType }
}
@@ -15,17 +10,3 @@ fun mimeType(raw: String) = mimesCache.getOrPut(raw) {
internal fun parseMimeType(raw: String): MimeType = CustomMimeType(raw)
@Suppress("OPT_IN_USAGE")
@Serializer(MimeType::class)
object MimeTypeSerializer : KSerializer<MimeType> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("mimeType", PrimitiveKind.STRING)
override fun deserialize(decoder: Decoder): MimeType {
val mimeType = decoder.decodeString()
return mimeType(mimeType)
}
override fun serialize(encoder: Encoder, value: MimeType) {
encoder.encodeString(value.raw)
}
}

View File

@@ -0,0 +1,49 @@
project.version = "$version"
project.group = "$group"
apply from: "$publishGradlePath"
kotlin {
jvm {
compilations.main {
kotlinOptions {
jvmTarget = "1.8"
}
}
}
js (IR) {
browser()
nodejs()
}
sourceSets {
commonMain {
dependencies {
implementation kotlin('stdlib')
}
}
commonTest {
dependencies {
implementation kotlin('test-common')
implementation kotlin('test-annotations-common')
}
}
jvmTest {
dependencies {
implementation kotlin('test-junit')
}
}
jsTest {
dependencies {
implementation kotlin('test-js')
implementation kotlin('test-junit')
}
}
}
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

View File

@@ -23,7 +23,7 @@ kotlin {
commonMain {
dependencies {
implementation kotlin('stdlib')
implementation libs.kt.serialization
api libs.kt.serialization
}
}
commonTest {

View File

@@ -23,7 +23,7 @@ kotlin {
commonMain {
dependencies {
implementation kotlin('stdlib')
implementation libs.kt.serialization
api libs.kt.serialization
implementation compose.runtime
}
}

View File

@@ -33,18 +33,6 @@ data class PaginationResult<T>(
results,
(pagesNumber * size).toLong()
)
@Deprecated("Replace with The other order of incoming parameters or objectsCount parameter")
constructor(
page: Int,
pagesNumber: Int,
results: List<T>,
size: Int
) : this(
page,
results,
pagesNumber,
size
)
}
fun <T> emptyPaginationResult() = PaginationResult<T>(0, 0, emptyList(), 0L)

View File

@@ -19,29 +19,29 @@ publishing {
}
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://github.com/InsanusMokrassar/MicroUtils/blob/master/LICENSE"
}
}
}
repositories {
@@ -49,35 +49,54 @@ publishing {
maven {
name = "GithubPackages"
url = uri("https://maven.pkg.github.com/InsanusMokrassar/MicroUtils")
credentials {
username = project.hasProperty('GITHUBPACKAGES_USER') ? project.property('GITHUBPACKAGES_USER') : System.getenv('GITHUBPACKAGES_USER')
password = project.hasProperty('GITHUBPACKAGES_PASSWORD') ? project.property('GITHUBPACKAGES_PASSWORD') : System.getenv('GITHUBPACKAGES_PASSWORD')
}
}
}
if (project.hasProperty('GITEA_TOKEN') || System.getenv('GITEA_TOKEN') != null) {
maven {
name = "Gitea"
url = uri("https://git.inmo.dev/api/packages/InsanusMokrassar/maven")
credentials(HttpHeaderCredentials) {
name = "Authorization"
value = project.hasProperty('GITEA_TOKEN') ? project.property('GITEA_TOKEN') : System.getenv('GITEA_TOKEN')
}
authentication {
header(HttpHeaderAuthentication)
}
}
}
if ((project.hasProperty('SONATYPE_USER') || System.getenv('SONATYPE_USER') != null) && (project.hasProperty('SONATYPE_PASSWORD') || System.getenv('SONATYPE_PASSWORD') != null)) {
maven {
name = "sonatype"
url = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2/")
credentials {
username = project.hasProperty('SONATYPE_USER') ? project.property('SONATYPE_USER') : System.getenv('SONATYPE_USER')
password = project.hasProperty('SONATYPE_PASSWORD') ? project.property('SONATYPE_PASSWORD') : System.getenv('SONATYPE_PASSWORD')
}
}
}
}
}
}
if (project.hasProperty("signing.gnupg.keyName")) {
apply plugin: 'signing'
signing {
useGpgCmd()
sign publishing.publications
}
task signAll {
tasks.withType(Sign).forEach {
dependsOn(it)

View File

@@ -1 +1 @@
{"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://github.com/InsanusMokrassar/MicroUtils/blob/master/LICENSE"}],"mavenConfig":{"name":"${project.name}","description":"It is set of projects with micro tools for avoiding of routines coding","url":"https://github.com/InsanusMokrassar/MicroUtils/","vcsUrl":"https://github.com/InsanusMokrassar/MicroUtils.git","developers":[{"id":"InsanusMokrassar","name":"Aleksei Ovsiannikov","eMail":"ovsyannikov.alexey95@gmail.com"},{"id":"000Sanya","name":"Syrov Aleksandr","eMail":"000sanya.000sanya@gmail.com"}],"repositories":[{"name":"GithubPackages","url":"https://maven.pkg.github.com/InsanusMokrassar/MicroUtils"},{"name":"sonatype","url":"https://oss.sonatype.org/service/local/staging/deploy/maven2/"}],"gpgSigning":{"type":"dev.inmo.kmppscriptbuilder.core.models.GpgSigning.Optional"}}}
{"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://github.com/InsanusMokrassar/MicroUtils/blob/master/LICENSE"}],"mavenConfig":{"name":"${project.name}","description":"It is set of projects with micro tools for avoiding of routines coding","url":"https://github.com/InsanusMokrassar/MicroUtils/","vcsUrl":"https://github.com/InsanusMokrassar/MicroUtils.git","developers":[{"id":"InsanusMokrassar","name":"Aleksei Ovsiannikov","eMail":"ovsyannikov.alexey95@gmail.com"},{"id":"000Sanya","name":"Syrov Aleksandr","eMail":"000sanya.000sanya@gmail.com"}],"repositories":[{"name":"GithubPackages","url":"https://maven.pkg.github.com/InsanusMokrassar/MicroUtils"},{"name":"Gitea","url":"https://git.inmo.dev/api/packages/InsanusMokrassar/maven","credsType":{"type":"dev.inmo.kmppscriptbuilder.core.models.MavenPublishingRepository.CredentialsType.HttpHeaderCredentials","headerName":"Authorization","headerValueProperty":"GITEA_TOKEN"}},{"name":"sonatype","url":"https://oss.sonatype.org/service/local/staging/deploy/maven2/"}],"gpgSigning":{"type":"dev.inmo.kmppscriptbuilder.core.models.GpgSigning.Optional"}}}

View File

@@ -46,6 +46,12 @@ open class FullReadCRUDCacheRepo<ObjectType, IdType>(
{ if (it.results.isNotEmpty()) actualizeAll() }
)
override suspend fun getIdsByPagination(pagination: Pagination): PaginationResult<IdType> = doOrTakeAndActualize(
{ keys(pagination).takeIf { it.results.isNotEmpty() }.optionalOrAbsentIfNull },
{ getIdsByPagination(pagination) },
{ if (it.results.isNotEmpty()) actualizeAll() }
)
override suspend fun count(): Long = doOrTakeAndActualize(
{ count().takeIf { it != 0L }.optionalOrAbsentIfNull },
{ count() },

View File

@@ -28,6 +28,7 @@ kotlin {
api internalProject("micro_utils.common")
api internalProject("micro_utils.coroutines")
}
dependsOn jvmMain
}
}
}

View File

@@ -0,0 +1,138 @@
package dev.inmo.micro_utils.repos
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.pagination.utils.doAllWithCurrentPaging
import kotlinx.coroutines.flow.Flow
/**
* Read part of [KeyValueRepo]
*
* @param Key This type will be used as key in all operations related to searches of data
* @param Value This type will be used as returning data in most "get" operations
*/
interface ReadKeyValueRepo<Key, Value> : Repo {
/**
* @return Result [Value] in case when it is presented in repo by its [k] or null otherwise
*/
suspend fun get(k: Key): Value?
/**
* This method should use sorted by [Key]s search and take the [PaginationResult]. By default, it should use
* ascending sort for [Key]s
*/
suspend fun values(pagination: Pagination, reversed: Boolean = false): PaginationResult<Value>
/**
* This method should use sorted by [Key]s search and take the [PaginationResult]. By default, it should use
* ascending sort for [Key]s
*/
suspend fun keys(pagination: Pagination, reversed: Boolean = false): PaginationResult<Key>
/**
* This method should use sorted by [Key]s search and take the [PaginationResult]. By default, it should use
* ascending sort for [Key]s
*
* @param v This value should be used to exclude from search the items with different [Value]s
*/
suspend fun keys(v: Value, pagination: Pagination, reversed: Boolean = false): PaginationResult<Key>
/**
* @return true if [key] is presented in current collection or false otherwise
*/
suspend fun contains(key: Key): Boolean
/**
* @return count of all collection objects
*/
suspend fun count(): Long
}
typealias ReadStandardKeyValueRepo<Key,Value> = ReadKeyValueRepo<Key, Value>
/**
* Write part of [KeyValueRepo]
*
* @param Key This type will be used as key in all operations related to changes of data
* @param Value This type will be used as incoming data in most operations
*/
interface WriteKeyValueRepo<Key, Value> : Repo {
/**
* This flow must emit data each time when data by [Key] has been changed with [set] method or in any other way
* excluding cases of data removing
*
* @see onValueRemoved
*/
val onNewValue: Flow<Pair<Key, Value>>
/**
* This flow must emit data each time when data by [Key] has been removed with [unset]/[unsetWithValues] methods or
* in any other way
*
* @see onNewValue
*/
val onValueRemoved: Flow<Key>
/**
* Will set as batch [toSet] data in current repo. Must pass the data which were successfully updated in repo to
* [onNewValue]
*/
suspend fun set(toSet: Map<Key, Value>)
/**
* Will unset as batch data with keys from [toUnset]. Must pass the [Key]s which were successfully removed in repo to
* [onValueRemoved]
*/
suspend fun unset(toUnset: List<Key>)
/**
* Will unset as batch data with values from [toUnset]. Must pass the [Key]s which were successfully removed in repo
* to [onValueRemoved]
*/
suspend fun unsetWithValues(toUnset: List<Value>)
}
typealias WriteStandardKeyValueRepo<Key,Value> = WriteKeyValueRepo<Key, Value>
suspend inline fun <Key, Value> WriteKeyValueRepo<Key, Value>.set(
vararg toSet: Pair<Key, Value>
) = set(toSet.toMap())
suspend inline fun <Key, Value> WriteKeyValueRepo<Key, Value>.set(
k: Key, v: Value
) = set(k to v)
suspend inline fun <Key, Value> WriteKeyValueRepo<Key, Value>.unset(
vararg k: Key
) = unset(k.toList())
suspend inline fun <Key, Value> WriteKeyValueRepo<Key, Value>.unsetWithValues(
vararg v: Value
) = unsetWithValues(v.toList())
/**
* Full version of standard key-value repository with all set/unset/clear/get methods
*/
interface KeyValueRepo<Key, Value> : ReadKeyValueRepo<Key, Value>, WriteKeyValueRepo<Key, Value> {
/**
* By default, will walk throw all the [keys] with [Value]s from [toUnset] and run [doAllWithCurrentPaging] with
* [unset] of found data [Key]s
*/
override suspend fun unsetWithValues(toUnset: List<Value>) = toUnset.forEach { v ->
doAllWithCurrentPaging {
keys(v, it).also {
unset(it.results)
}
}
}
/**
* By default, will remove all the data of current repo using [doAllWithCurrentPaging], [keys] and [unset]
*/
suspend fun clear() {
doAllWithCurrentPaging { keys(it).also { unset(it.results) } }
}
}
typealias StandardKeyValueRepo<Key,Value> = KeyValueRepo<Key, Value>
class DelegateBasedKeyValueRepo<Key, Value>(
readDelegate: ReadKeyValueRepo<Key, Value>,
writeDelegate: WriteKeyValueRepo<Key, Value>
) : KeyValueRepo<Key, Value>,
ReadKeyValueRepo<Key, Value> by readDelegate,
WriteKeyValueRepo<Key, Value> by writeDelegate

View File

@@ -6,6 +6,7 @@ import kotlinx.coroutines.flow.Flow
interface ReadCRUDRepo<ObjectType, IdType> : Repo {
suspend fun getByPagination(pagination: Pagination): PaginationResult<ObjectType>
suspend fun getIdsByPagination(pagination: Pagination): PaginationResult<IdType>
suspend fun getById(id: IdType): ObjectType?
suspend fun contains(id: IdType): Boolean
suspend fun count(): Long

View File

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

View File

@@ -20,6 +20,14 @@ open class MapperReadCRUDRepo<FromId, FromRegistered, ToId, ToRegistered>(
)
}
override suspend fun getIdsByPagination(pagination: Pagination): PaginationResult<FromId> = to.getIdsByPagination(
pagination
).let {
it.changeResultsUnchecked(
it.results.map { it.toInnerKey() }
)
}
override suspend fun count(): Long = to.count()
override suspend fun contains(id: FromId): Boolean = to.contains(id.toOutKey())

View File

@@ -16,6 +16,7 @@ abstract class AbstractAndroidCRUDRepo<ObjectType, IdType>(
protected abstract val tableName: String
protected abstract val idColumnName: String
protected abstract suspend fun Cursor.toObject(): ObjectType
protected abstract suspend fun Cursor.toId(): IdType
protected fun SQLiteDatabase.count(): Long = select(tableName).use {
it.count
}.toLong()
@@ -64,4 +65,23 @@ abstract class AbstractAndroidCRUDRepo<ObjectType, IdType>(
}
}
}
override suspend fun getIdsByPagination(pagination: Pagination): PaginationResult<IdType> {
return helper.readableTransaction {
select(
tableName,
limit = pagination.limitClause()
).use {
if (it.moveToFirst()) {
val resultList = mutableListOf(it.toId())
while (it.moveToNext()) {
resultList.add(it.toId())
}
resultList.createPaginationResult(pagination, count())
} else {
emptyList<IdType>().createPaginationResult(pagination, 0)
}
}
}
}
}

View File

@@ -22,6 +22,17 @@ abstract class AbstractExposedReadCRUDRepo<ObjectType, IdType>(
)
}
}
override suspend fun getIdsByPagination(pagination: Pagination): PaginationResult<IdType> {
return transaction(db = database) {
selectAll().paginate(pagination).map {
it.asId
}.createPaginationResult(
pagination,
selectAll().count()
)
}
}
override suspend fun getById(id: IdType): ObjectType? {
return transaction(db = database) {
select {

View File

@@ -4,9 +4,9 @@ import dev.inmo.micro_utils.repos.UpdatedValuePair
import dev.inmo.micro_utils.repos.WriteCRUDRepo
import kotlinx.coroutines.flow.*
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.statements.InsertStatement
import org.jetbrains.exposed.sql.statements.UpdateStatement
import org.jetbrains.exposed.sql.statements.*
import org.jetbrains.exposed.sql.transactions.transaction
import java.util.Objects
abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
flowsChannelsSize: Int = 0,
@@ -27,10 +27,39 @@ abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
protected abstract fun InsertStatement<Number>.asObject(value: InputValueType): ObjectType
protected abstract fun insert(value: InputValueType, it: InsertStatement<Number>)
protected abstract fun update(id: IdType, value: InputValueType, it: UpdateStatement)
/**
* @param id Can be null only if [createAndInsertId] have returned null (it can be useful when you have
* autoincrement identifier)
* @param it Will be [UpdateStatement] when it is called from [update] method or [InsertStatement] from the [create]
* one. Anyway, it is main method where you should put the logic of table filling by [value] data
*
* @see createAndInsertId
*/
protected abstract fun update(id: IdType?, value: InputValueType, it: UpdateBuilder<Int>)
/**
* Override this method to interact with [it] ([InsertStatement]) and put there new id with [IdType].
*
* By default, have null value due to the fact that in the most cases users have [autoIncrement]ing id columns
*
* @return In case when id for the model has been created new [IdType] should be returned
*/
protected open fun createAndInsertId(value: InputValueType, it: InsertStatement<Number>): IdType? = null
protected open fun insert(value: InputValueType, it: InsertStatement<Number>) {
val id = createAndInsertId(value, it)
update(id, value, it as UpdateBuilder<Int>)
}
protected open suspend fun onBeforeCreate(value: List<InputValueType>) {}
/**
* Use this method to do the something with [values]. You may change and output values in that list and return
* changed list of pairs
*/
protected open suspend fun onAfterCreate(
values: List<Pair<InputValueType, ObjectType>>
): List<ObjectType> = values.map { it.second }
private fun createWithoutNotification(value: InputValueType): ObjectType {
return transaction(database) {
insert { insert(value, it) }.asObject(value)
@@ -40,13 +69,18 @@ abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
override suspend fun create(values: List<InputValueType>): List<ObjectType> {
onBeforeCreate(values)
return transaction(db = database) {
values.map { value -> createWithoutNotification(value) }
values.map { value -> value to createWithoutNotification(value) }
}.let {
onAfterCreate(it)
}.onEach {
_newObjectsFlow.emit(it)
}
}
protected open suspend fun onBeforeUpdate(value: List<UpdatedValuePair<IdType, InputValueType>>) {}
protected open suspend fun onAfterUpdate(
value: List<UpdatedValuePair<InputValueType, ObjectType>>
): List<ObjectType> = value.map { it.second }
private fun updateWithoutNotification(id: IdType, value: InputValueType): ObjectType? {
return transaction(db = database) {
update(
@@ -54,7 +88,7 @@ abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
selectById(this, id)
}
) {
update(id, value, it)
update(id, value, it as UpdateBuilder<Int>)
}
}.let {
if (it > 0) {
@@ -71,7 +105,9 @@ abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
override suspend fun update(id: IdType, value: InputValueType): ObjectType? {
onBeforeUpdate(listOf(id to value))
return updateWithoutNotification(id, value).also {
return updateWithoutNotification(id, value).let {
onAfterUpdate(listOf(value to (it ?: return@let emptyList())))
}.firstOrNull().also {
if (it != null) {
_updatedObjectsFlow.emit(it)
}
@@ -81,9 +117,11 @@ abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
onBeforeUpdate(values)
return (
transaction(db = database) {
values.map { (id, value) -> updateWithoutNotification(id, value) }
}.filterNotNull()
).onEach {
values.mapNotNull { (id, value) -> value to (updateWithoutNotification(id, value) ?: return@mapNotNull null) }
}
).let {
onAfterUpdate(it)
}.onEach {
_updatedObjectsFlow.emit(it)
}
}
@@ -91,9 +129,7 @@ abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
override suspend fun deleteById(ids: List<IdType>) {
onBeforeDelete(ids)
transaction(db = database) {
val deleted = deleteWhere(null, null) {
selectByIds(ids)
}
val deleted = deleteWhere(null, null) { selectByIds(it, ids) }
if (deleted == ids.size) {
ids
} else {

View File

@@ -4,8 +4,9 @@ import org.jetbrains.exposed.sql.*
interface CommonExposedRepo<IdType, ObjectType> : ExposedRepo {
val ResultRow.asObject: ObjectType
val selectById: SqlExpressionBuilder.(IdType) -> Op<Boolean>
val selectByIds: SqlExpressionBuilder.(List<IdType>) -> Op<Boolean>
val ResultRow.asId: IdType
val selectById: ISqlExpressionBuilder.(IdType) -> Op<Boolean>
val selectByIds: ISqlExpressionBuilder.(List<IdType>) -> Op<Boolean>
get() = { list ->
if (list.isEmpty()) {
Op.FALSE

View File

@@ -3,8 +3,7 @@ package dev.inmo.micro_utils.repos.exposed.keyvalue
import dev.inmo.micro_utils.repos.KeyValueRepo
import kotlinx.coroutines.flow.*
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.statements.InsertStatement
import org.jetbrains.exposed.sql.statements.UpdateStatement
import org.jetbrains.exposed.sql.statements.*
import org.jetbrains.exposed.sql.transactions.transaction
abstract class AbstractExposedKeyValueRepo<Key, Value>(
@@ -20,13 +19,18 @@ abstract class AbstractExposedKeyValueRepo<Key, Value>(
override val onNewValue: Flow<Pair<Key, Value>> = _onNewValue.asSharedFlow()
override val onValueRemoved: Flow<Key> = _onValueRemoved.asSharedFlow()
protected abstract fun update(k: Key, v: Value, it: UpdateStatement)
protected abstract fun insert(k: Key, v: Value, it: InsertStatement<Number>)
protected abstract fun update(k: Key, v: Value, it: UpdateBuilder<Int>)
protected abstract fun insertKey(k: Key, v: Value, it: InsertStatement<Number>)
protected open fun insert(k: Key, v: Value, it: InsertStatement<Number>) {
insertKey(k, v, it)
update(k, v, it as UpdateBuilder<Int>)
}
override suspend fun set(toSet: Map<Key, Value>) {
transaction(database) {
toSet.mapNotNull { (k, v) ->
if (update({ selectById(k) }) { update(k, v, it) } > 0) {
if (update({ selectById(k) }) { update(k, v, it as UpdateBuilder<Int>) } > 0) {
k to v
} else {
val inserted = insert {
@@ -46,9 +50,9 @@ abstract class AbstractExposedKeyValueRepo<Key, Value>(
override suspend fun unset(toUnset: List<Key>) {
transaction(database) {
toUnset.mapNotNull {
if (deleteWhere { selectById(it) } > 0) {
it
toUnset.mapNotNull { item ->
if (deleteWhere { selectById(it, item) } > 0) {
item
} else {
null
}
@@ -62,7 +66,7 @@ abstract class AbstractExposedKeyValueRepo<Key, Value>(
transaction(database) {
toUnset.flatMap {
val keys = select { selectByValue(it) }.mapNotNull { it.asKey }
deleteWhere { selectByIds(keys) }
deleteWhere { selectByIds(it, keys) }
keys
}
}.distinct().forEach {

View File

@@ -15,8 +15,14 @@ abstract class AbstractExposedReadKeyValueRepo<Key, Value>(
CommonExposedRepo<Key, Value>,
Table(tableName ?: "") {
abstract val keyColumn: Column<*>
/**
* Same as [asId] in context of KeyValue repo
*/
abstract val ResultRow.asKey: Key
abstract val selectByValue: SqlExpressionBuilder.(Value) -> Op<Boolean>
override val ResultRow.asId: Key
get() = asKey
abstract val selectByValue: ISqlExpressionBuilder.(Value) -> Op<Boolean>
override suspend fun get(k: Key): Value? = transaction(database) {
select { selectById(k) }.limit(1).firstOrNull() ?.asObject

View File

@@ -4,6 +4,8 @@ import dev.inmo.micro_utils.repos.KeyValueRepo
import dev.inmo.micro_utils.repos.exposed.ColumnAllocator
import kotlinx.coroutines.flow.*
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.SqlExpressionBuilder.inList
import org.jetbrains.exposed.sql.transactions.transaction
open class ExposedKeyValueRepo<Key, Value>(
@@ -47,9 +49,9 @@ open class ExposedKeyValueRepo<Key, Value>(
override suspend fun unset(toUnset: List<Key>) {
transaction(database) {
toUnset.mapNotNull {
if (deleteWhere { keyColumn.eq(it) } > 0) {
it
toUnset.mapNotNull { item ->
if (deleteWhere { keyColumn.eq(item) } > 0) {
item
} else {
null
}

View File

@@ -5,57 +5,27 @@ import dev.inmo.micro_utils.repos.ReadKeyValueRepo
import dev.inmo.micro_utils.repos.exposed.*
import dev.inmo.micro_utils.repos.exposed.utils.selectPaginated
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.statements.InsertStatement
import org.jetbrains.exposed.sql.statements.UpdateBuilder
import org.jetbrains.exposed.sql.transactions.transaction
open class ExposedReadKeyValueRepo<Key, Value>(
override val database: Database,
database: Database,
keyColumnAllocator: ColumnAllocator<Key>,
valueColumnAllocator: ColumnAllocator<Value>,
tableName: String? = null
) : ReadKeyValueRepo<Key, Value>, ExposedRepo, Table(tableName ?: "") {
val keyColumn: Column<Key> = keyColumnAllocator()
) : ReadKeyValueRepo<Key, Value>, ExposedRepo, AbstractExposedReadKeyValueRepo<Key, Value>(database, tableName) {
override val keyColumn: Column<Key> = keyColumnAllocator()
val valueColumn: Column<Value> = valueColumnAllocator()
override val primaryKey: PrimaryKey = PrimaryKey(keyColumn, valueColumn)
override val ResultRow.asKey: Key
get() = get(keyColumn)
override val selectByValue: ISqlExpressionBuilder.(Value) -> Op<Boolean> = { valueColumn.eq(it) }
override val ResultRow.asObject: Value
get() = get(valueColumn)
override val selectById: ISqlExpressionBuilder.(Key) -> Op<Boolean> = { keyColumn.eq(it) }
override val primaryKey: Table.PrimaryKey
get() = PrimaryKey(keyColumn, valueColumn)
init { initTable() }
override suspend fun get(k: Key): Value? = transaction(database) {
select { keyColumn.eq(k) }.limit(1).firstOrNull() ?.getOrNull(valueColumn)
}
override suspend fun contains(key: Key): Boolean = transaction(database) {
select { keyColumn.eq(key) }.limit(1).any()
}
override suspend fun count(): Long = transaction(database) { selectAll().count() }
override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<Key> = transaction(database) {
selectAll().selectPaginated(
pagination,
keyColumn,
reversed
) {
it[keyColumn]
}
}
override suspend fun keys(v: Value, pagination: Pagination, reversed: Boolean): PaginationResult<Key> = transaction(database) {
select { valueColumn.eq(v) }.selectPaginated(
pagination,
keyColumn,
reversed
) {
it[keyColumn]
}
}
override suspend fun values(pagination: Pagination, reversed: Boolean): PaginationResult<Value> = transaction(database) {
selectAll().selectPaginated(
pagination,
keyColumn,
reversed
) {
it[valueColumn]
}
}
}

View File

@@ -49,7 +49,7 @@ abstract class AbstractExposedKeyValuesRepo<Key, Value>(
transaction(database) {
toRemove.keys.flatMap { k ->
toRemove[k] ?.mapNotNull { v ->
if (deleteWhere { selectById(k).and(selectByValue(v)) } > 0 ) {
if (deleteWhere { selectById(it, k).and(SqlExpressionBuilder.selectByValue(v)) } > 0 ) {
k to v
} else {
null
@@ -63,7 +63,7 @@ abstract class AbstractExposedKeyValuesRepo<Key, Value>(
override suspend fun clear(k: Key) {
transaction(database) {
deleteWhere { selectById(k) }
deleteWhere { selectById(it, k) }
}.also { _onDataCleared.emit(k) }
}
}

View File

@@ -17,7 +17,9 @@ abstract class AbstractExposedReadKeyValuesRepo<Key, Value>(
Table(tableName ?: "") {
abstract val keyColumn: Column<*>
abstract val ResultRow.asKey: Key
abstract val selectByValue: SqlExpressionBuilder.(Value) -> Op<Boolean>
override val ResultRow.asId: Key
get() = asKey
abstract val selectByValue: ISqlExpressionBuilder.(Value) -> Op<Boolean>
override suspend fun count(k: Key): Long = transaction(database) { select { selectById(k) }.count() }

View File

@@ -4,6 +4,7 @@ import dev.inmo.micro_utils.repos.KeyValuesRepo
import dev.inmo.micro_utils.repos.exposed.ColumnAllocator
import kotlinx.coroutines.flow.*
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.transactions.transaction
typealias ExposedOneToManyKeyValueRepo1<Key, Value> = ExposedKeyValuesRepo<Key, Value>

View File

@@ -1,10 +1,8 @@
package dev.inmo.micro_utils.repos.exposed.onetomany
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.ReadKeyValuesRepo
import dev.inmo.micro_utils.repos.exposed.*
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction
typealias ExposedReadOneToManyKeyValueRepo<Key, Value> = ExposedReadKeyValuesRepo<Key, Value>
@@ -13,54 +11,15 @@ open class ExposedReadKeyValuesRepo<Key, Value>(
keyColumnAllocator: ColumnAllocator<Key>,
valueColumnAllocator: ColumnAllocator<Value>,
tableName: String? = null
) : ReadKeyValuesRepo<Key, Value>, ExposedRepo, Table(tableName ?: "") {
val keyColumn: Column<Key> = keyColumnAllocator()
) : ReadKeyValuesRepo<Key, Value>, ExposedRepo, AbstractExposedReadKeyValuesRepo<Key, Value>(database, tableName) {
override val keyColumn: Column<Key> = keyColumnAllocator()
override val ResultRow.asKey: Key
get() = get(keyColumn)
override val selectByValue: ISqlExpressionBuilder.(Value) -> Op<Boolean> = { valueColumn.eq(it) }
override val ResultRow.asObject: Value
get() = get(valueColumn)
override val selectById: ISqlExpressionBuilder.(Key) -> Op<Boolean> = { keyColumn.eq(it) }
val valueColumn: Column<Value> = valueColumnAllocator()
init { initTable() }
override suspend fun count(k: Key): Long = transaction(database) { select { keyColumn.eq(k) }.count() }
override suspend fun count(): Long = transaction(database) { selectAll().count() }
override suspend fun get(
k: Key,
pagination: Pagination,
reversed: Boolean
): PaginationResult<Value> = transaction(database) {
select { keyColumn.eq(k) }.paginate(pagination, keyColumn, reversed).map { it[valueColumn] }
}.createPaginationResult(
pagination,
count(k)
)
override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<Key> = transaction(database) {
selectAll().paginate(pagination, keyColumn, reversed).map { it[keyColumn] }
}.createPaginationResult(
pagination,
count()
)
override suspend fun keys(
v: Value,
pagination: Pagination,
reversed: Boolean
): PaginationResult<Key> = transaction(database) {
select { valueColumn.eq(v) }.let {
it.count() to it.paginate(pagination, keyColumn, reversed).map { it[keyColumn] }
}
}.let { (count, list) ->
list.createPaginationResult(
pagination,
count
)
}
override suspend fun contains(k: Key): Boolean = transaction(database) {
select { keyColumn.eq(k) }.limit(1).any()
}
override suspend fun contains(k: Key, v: Value): Boolean = transaction(database) {
select { keyColumn.eq(k).and(valueColumn.eq(v)) }.limit(1).any()
}
}

View File

@@ -15,6 +15,13 @@ class ReadMapCRUDRepo<ObjectType, IdType>(
)
}
override suspend fun getIdsByPagination(pagination: Pagination): PaginationResult<IdType> {
return map.keys.drop(pagination.firstIndex).take(pagination.size).createPaginationResult(
pagination,
count()
)
}
override suspend fun getById(id: IdType): ObjectType? = map[id]
override suspend fun contains(id: IdType): Boolean = map.containsKey(id)

View File

@@ -28,6 +28,7 @@ class KtorCRUDRepoClient<ObjectType, IdType, InputValue> (
httpClient,
typeInfo<ObjectType>(),
typeInfo<PaginationResult<ObjectType>>(),
typeInfo<PaginationResult<IdType>>(),
contentType,
idSerializer
),

View File

@@ -19,6 +19,7 @@ class KtorReadCRUDRepoClient<ObjectType, IdType> (
private val httpClient: HttpClient,
private val objectType: TypeInfo,
private val paginationObjectType: TypeInfo,
private val paginationIdType: TypeInfo,
private val contentType: ContentType,
private val idSerializer: suspend (IdType) -> String
) : ReadCRUDRepo<ObjectType, IdType> {
@@ -27,6 +28,11 @@ class KtorReadCRUDRepoClient<ObjectType, IdType> (
) {
contentType(contentType)
}.body(paginationObjectType)
override suspend fun getIdsByPagination(pagination: Pagination): PaginationResult<IdType> = httpClient.get(
buildStandardUrl(baseUrl, getIdsByPaginationRouting, pagination.asUrlQueryParts)
) {
contentType(contentType)
}.body(paginationIdType)
override suspend fun getById(id: IdType): ObjectType? = httpClient.get(
buildStandardUrl(
@@ -72,6 +78,7 @@ inline fun <reified ObjectType, IdType> KtorReadCRUDRepoClient(
httpClient,
typeInfo<ObjectType>(),
typeInfo<PaginationResult<ObjectType>>(),
typeInfo<PaginationResult<IdType>>(),
contentType,
idSerializer
)

View File

@@ -1,5 +1,6 @@
package dev.inmo.micro_utils.repos.ktor.common.crud
const val getByPaginationRouting = "getByPagination"
const val getIdsByPaginationRouting = "getIdsByPagination"
const val getByIdRouting = "getById"
const val containsRouting = "contains"

View File

@@ -23,6 +23,11 @@ inline fun <reified ObjectType, reified IdType> Route.configureReadCRUDRepoRoute
call.respond(originalRepo.getByPagination(pagination))
}
get(getIdsByPaginationRouting) {
val pagination = call.request.queryParameters.extractPagination
call.respond(originalRepo.getIdsByPagination(pagination))
}
get(getByIdRouting) {
val id = idDeserializer(

View File

@@ -1,83 +0,0 @@
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.pagination.PaginationResult
import dev.inmo.micro_utils.pagination.extractPagination
import dev.inmo.micro_utils.repos.ReadCRUDRepo
import dev.inmo.micro_utils.repos.ktor.common.countRouting
import dev.inmo.micro_utils.repos.ktor.common.crud.*
import io.ktor.http.ContentType
import io.ktor.server.application.call
import io.ktor.server.routing.Route
import io.ktor.server.routing.get
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.serializer
fun <ObjectType, IdType> Route.configureReadCRUDRepoRoutes(
originalRepo: ReadCRUDRepo<ObjectType, IdType>,
objectsSerializer: KSerializer<ObjectType>,
objectsNullableSerializer: KSerializer<ObjectType?>,
idsSerializer: KSerializer<IdType>,
unifiedRouter: UnifiedRouter
) {
val paginationResultSerializer = PaginationResult.serializer(objectsSerializer)
get(getByPaginationRouting) {
unifiedRouter.apply {
val pagination = call.request.queryParameters.extractPagination
unianswer(
paginationResultSerializer,
originalRepo.getByPagination(pagination)
)
}
}
get(getByIdRouting) {
unifiedRouter.apply {
val id = decodeUrlQueryValueOrSendError(
"id",
idsSerializer
) ?: return@get
unianswer(
objectsNullableSerializer,
originalRepo.getById(id)
)
}
}
get(containsRouting) {
unifiedRouter.apply {
val id = decodeUrlQueryValueOrSendError(
"id",
idsSerializer
) ?: return@get
unianswer(
Boolean.serializer(),
originalRepo.contains(id)
)
}
}
get(countRouting) {
unifiedRouter.apply {
unianswer(
Long.serializer(),
originalRepo.count()
)
}
}
}
inline fun <ObjectType, IdType> Route.configureReadCRUDRepoRoutes(
originalRepo: ReadCRUDRepo<ObjectType, IdType>,
objectsSerializer: KSerializer<ObjectType>,
objectsNullableSerializer: KSerializer<ObjectType?>,
idsSerializer: KSerializer<IdType>,
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat,
serialFormatContentType: ContentType = standardKtorSerialFormatContentType
) = configureReadCRUDRepoRoutes(originalRepo, objectsSerializer, objectsNullableSerializer, idsSerializer, UnifiedRouter(serialFormat, serialFormatContentType))

View File

@@ -1,39 +0,0 @@
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.CRUDRepo
import io.ktor.http.ContentType
import io.ktor.server.routing.Route
import io.ktor.server.routing.route
import kotlinx.serialization.KSerializer
fun <ObjectType, IdType, InputValue> Route.configureCRUDRepoRoutes(
baseSubpart: String,
originalRepo: CRUDRepo<ObjectType, IdType, InputValue>,
objectsSerializer: KSerializer<ObjectType>,
objectsNullableSerializer: KSerializer<ObjectType?>,
inputsSerializer: KSerializer<InputValue>,
idsSerializer: KSerializer<IdType>,
unifiedRouter: UnifiedRouter
) {
route(baseSubpart) {
configureReadCRUDRepoRoutes(originalRepo, objectsSerializer, objectsNullableSerializer, idsSerializer, unifiedRouter)
configureWriteCRUDRepoRoutes(originalRepo, objectsSerializer, objectsNullableSerializer, inputsSerializer, idsSerializer, unifiedRouter)
}
}
fun <ObjectType, IdType, InputValue> Route.configureCRUDRepoRoutes(
baseSubpart: String,
originalRepo: CRUDRepo<ObjectType, IdType, InputValue>,
objectsSerializer: KSerializer<ObjectType>,
objectsNullableSerializer: KSerializer<ObjectType?>,
inputsSerializer: KSerializer<InputValue>,
idsSerializer: KSerializer<IdType>,
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat,
serialFormatContentType: ContentType = standardKtorSerialFormatContentType
) = configureCRUDRepoRoutes(
baseSubpart, originalRepo, objectsSerializer, objectsNullableSerializer, inputsSerializer, idsSerializer, UnifiedRouter(serialFormat, serialFormatContentType)
)

View File

@@ -1,107 +0,0 @@
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.repos.WriteCRUDRepo
import dev.inmo.micro_utils.repos.ktor.common.crud.*
import io.ktor.http.ContentType
import io.ktor.server.routing.Route
import io.ktor.server.routing.post
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.*
fun <ObjectType, IdType, InputValue> Route.configureWriteCRUDRepoRoutes(
originalRepo: WriteCRUDRepo<ObjectType, IdType, InputValue>,
objectsSerializer: KSerializer<ObjectType>,
objectsNullableSerializer: KSerializer<ObjectType?>,
inputsSerializer: KSerializer<InputValue>,
idsSerializer: KSerializer<IdType>,
unifiedRouter: UnifiedRouter
) {
val listObjectsSerializer = ListSerializer(objectsSerializer)
val listInputSerializer = ListSerializer(inputsSerializer)
val listIdsSerializer = ListSerializer(idsSerializer)
val inputUpdateSerializer = PairSerializer(
idsSerializer,
inputsSerializer
)
val listInputUpdateSerializer = ListSerializer(inputUpdateSerializer)
unifiedRouter.apply {
includeWebsocketHandling(
newObjectsFlowRouting,
originalRepo.newObjectsFlow,
objectsSerializer
)
includeWebsocketHandling(
updatedObjectsFlowRouting,
originalRepo.updatedObjectsFlow,
objectsSerializer
)
includeWebsocketHandling(
deletedObjectsIdsFlowRouting,
originalRepo.deletedObjectsIdsFlow,
idsSerializer
)
}
post(createRouting) {
unifiedRouter.apply {
unianswer(
listObjectsSerializer,
originalRepo.create(
uniload(listInputSerializer)
)
)
}
}
post(updateRouting) {
unifiedRouter.apply {
val (id, input) = uniload(inputUpdateSerializer)
unianswer(
objectsNullableSerializer,
originalRepo.update(
id, input
)
)
}
}
post(updateManyRouting) {
unifiedRouter.apply {
val updates = uniload(listInputUpdateSerializer)
unianswer(
listObjectsSerializer,
originalRepo.update(
updates
)
)
}
}
post(deleteByIdRouting) {
unifiedRouter.apply {
val ids = uniload(listIdsSerializer)
unianswer(
Unit.serializer(),
originalRepo.deleteById(
ids
)
)
}
}
}
fun <ObjectType, IdType, InputValue> Route.configureWriteCRUDRepoRoutes(
originalRepo: WriteCRUDRepo<ObjectType, IdType, InputValue>,
objectsSerializer: KSerializer<ObjectType>,
objectsNullableSerializer: KSerializer<ObjectType?>,
inputsSerializer: KSerializer<InputValue>,
idsSerializer: KSerializer<IdType>,
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat,
serialFormatContentType: ContentType = standardKtorSerialFormatContentType
) = configureWriteCRUDRepoRoutes(
originalRepo, objectsSerializer, objectsNullableSerializer, inputsSerializer, idsSerializer, UnifiedRouter(serialFormat, serialFormatContentType)
)

View File

@@ -1,46 +0,0 @@
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.KeyValueRepo
import io.ktor.http.ContentType
import io.ktor.server.routing.Route
import io.ktor.server.routing.route
import kotlinx.serialization.KSerializer
fun <K, V> Route.configureKeyValueRepoRoutes(
baseSubpart: String,
originalRepo: KeyValueRepo<K, V>,
keySerializer: KSerializer<K>,
valueSerializer: KSerializer<V>,
valueNullableSerializer: KSerializer<V?>,
unifiedRouter: UnifiedRouter
) {
route(baseSubpart) {
configureReadStandartKeyValueRepoRoutes(
originalRepo,
keySerializer,
valueSerializer,
valueNullableSerializer,
unifiedRouter
)
configureWriteKeyValueRepoRoutes(
originalRepo,
keySerializer,
valueSerializer,
unifiedRouter
)
}
}
fun <K, V> Route.configureStandartKeyValueRepoRoutes(
baseSubpart: String,
originalRepo: KeyValueRepo<K, V>,
keySerializer: KSerializer<K>,
valueSerializer: KSerializer<V>,
valueNullableSerializer: KSerializer<V?>,
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat,
serialFormatContentType: ContentType = standardKtorSerialFormatContentType
) = configureKeyValueRepoRoutes(baseSubpart, originalRepo, keySerializer, valueSerializer, valueNullableSerializer, UnifiedRouter(serialFormat, serialFormatContentType))

View File

@@ -1,107 +0,0 @@
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.pagination.PaginationResult
import dev.inmo.micro_utils.pagination.extractPagination
import dev.inmo.micro_utils.repos.ReadKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.*
import dev.inmo.micro_utils.repos.ktor.common.containsRoute
import dev.inmo.micro_utils.repos.ktor.common.countRoute
import dev.inmo.micro_utils.repos.ktor.common.key_value.*
import dev.inmo.micro_utils.repos.ktor.common.key_value.keyParameterName
import dev.inmo.micro_utils.repos.ktor.common.key_value.reversedParameterName
import io.ktor.http.ContentType
import io.ktor.server.application.call
import io.ktor.server.routing.Route
import io.ktor.server.routing.get
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.serializer
fun <K, V> Route.configureReadStandartKeyValueRepoRoutes (
originalRepo: ReadKeyValueRepo<K, V>,
keySerializer: KSerializer<K>,
valueSerializer: KSerializer<V>,
valueNullableSerializer: KSerializer<V?>,
unifiedRouter: UnifiedRouter
) {
get(getRoute) {
unifiedRouter.apply {
val key = decodeUrlQueryValueOrSendError(
keyParameterName,
keySerializer
) ?: return@get
unianswer(
valueNullableSerializer,
originalRepo.get(key)
)
}
}
get(valuesRoute) {
unifiedRouter.apply {
val parination = call.request.queryParameters.extractPagination;
val reversed = decodeUrlQueryValueOrSendError(
reversedParameterName,
Boolean.serializer()
) ?: return@get
unianswer(
PaginationResult.serializer(valueSerializer),
originalRepo.values(parination, reversed)
)
}
}
get(keysRoute) {
unifiedRouter.apply {
val parination = call.request.queryParameters.extractPagination;
val reversed = decodeUrlQueryValueOrSendError(
reversedParameterName,
Boolean.serializer()
) ?: return@get
val value = decodeUrlQueryValue(valueParameterName, valueSerializer)
unianswer(
PaginationResult.serializer(keySerializer),
value?.let { originalRepo.keys(value, parination, reversed) } ?: originalRepo.keys(parination, reversed)
)
}
}
get(containsRoute) {
unifiedRouter.apply {
val key = decodeUrlQueryValueOrSendError(
keyParameterName,
keySerializer
) ?: return@get
unianswer(
Boolean.serializer(),
originalRepo.contains(key)
)
}
}
get(countRoute) {
unifiedRouter.apply {
unianswer(
Long.serializer(),
originalRepo.count()
)
}
}
}
inline fun <K, V> Route.configureReadStandartKeyValueRepoRoutes (
originalRepo: ReadKeyValueRepo<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,70 +0,0 @@
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.repos.WriteKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.key_value.*
import io.ktor.http.ContentType
import io.ktor.server.routing.Route
import io.ktor.server.routing.post
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.*
fun <K, V> Route.configureWriteKeyValueRepoRoutes (
originalRepo: WriteKeyValueRepo<K, V>,
keySerializer: KSerializer<K>,
valueSerializer: KSerializer<V>,
unifiedRouter: UnifiedRouter
) {
val keyValueMapSerializer = MapSerializer(keySerializer, valueSerializer)
val keysListSerializer = ListSerializer(keySerializer)
val valuesListSerializer = ListSerializer(valueSerializer)
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))
}
}
post(unsetWithValuesRoute) {
unifiedRouter.apply {
val toUnset = uniload(valuesListSerializer)
unianswer(Unit.serializer(), originalRepo.unsetWithValues(toUnset))
}
}
}
fun <K, V> Route.configureWriteStandartKeyValueRepoRoutes (
originalRepo: WriteKeyValueRepo<K, V>,
keySerializer: KSerializer<K>,
valueSerializer: KSerializer<V>,
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat,
serialFormatContentType: ContentType = standardKtorSerialFormatContentType
) = configureWriteKeyValueRepoRoutes(originalRepo, keySerializer, valueSerializer, UnifiedRouter(serialFormat, serialFormatContentType))

View File

@@ -1,35 +0,0 @@
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.KeyValuesRepo
import io.ktor.http.ContentType
import io.ktor.server.routing.Route
import io.ktor.server.routing.route
import kotlinx.serialization.KSerializer
fun <Key, Value> Route.configureOneToManyKeyValueRepoRoutes(
baseSubpart: String,
originalRepo: KeyValuesRepo<Key, Value>,
keySerializer: KSerializer<Key>,
valueSerializer: KSerializer<Value>,
unifiedRouter: UnifiedRouter
) {
route(baseSubpart) {
configureOneToManyReadKeyValueRepoRoutes(originalRepo, keySerializer, valueSerializer, unifiedRouter)
configureOneToManyWriteKeyValueRepoRoutes(originalRepo, keySerializer, valueSerializer, unifiedRouter)
}
}
fun <Key, Value> Route.configureOneToManyKeyValueRepoRoutes(
baseSubpart: String,
originalRepo: KeyValuesRepo<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,129 +0,0 @@
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.pagination.PaginationResult
import dev.inmo.micro_utils.pagination.extractPagination
import dev.inmo.micro_utils.repos.ReadKeyValuesRepo
import dev.inmo.micro_utils.repos.ktor.common.keyParameterName
import dev.inmo.micro_utils.repos.ktor.common.one_to_many.*
import dev.inmo.micro_utils.repos.ktor.common.valueParameterName
import dev.inmo.micro_utils.repos.ktor.common.reversedParameterName
import io.ktor.http.ContentType
import io.ktor.server.application.call
import io.ktor.server.routing.Route
import io.ktor.server.routing.get
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.serializer
fun <Key, Value> Route.configureOneToManyReadKeyValueRepoRoutes(
originalRepo: ReadKeyValuesRepo<Key, Value>,
keySerializer: KSerializer<Key>,
valueSerializer: KSerializer<Value>,
unifiedRouter: UnifiedRouter
) {
val paginationKeyResult = PaginationResult.serializer(keySerializer)
val paginationValueResult = PaginationResult.serializer(valueSerializer)
get(getRoute) {
unifiedRouter.apply {
val pagination = call.request.queryParameters.extractPagination
val key = decodeUrlQueryValueOrSendError(
keyParameterName,
keySerializer
) ?: return@get
val reversed = decodeUrlQueryValue(
reversedParameterName,
Boolean.serializer()
) ?: false
unianswer(
paginationValueResult,
originalRepo.get(key, pagination, reversed)
)
}
}
get(keysRoute) {
unifiedRouter.apply {
val pagination = call.request.queryParameters.extractPagination
val reversed = decodeUrlQueryValue(
reversedParameterName,
Boolean.serializer()
) ?: false
val value: Value? = decodeUrlQueryValue(
valueParameterName,
valueSerializer
)
unianswer(
paginationKeyResult,
value?.let { originalRepo.keys(value, pagination, reversed) } ?: originalRepo.keys(pagination, reversed)
)
}
}
get(containsByKeyRoute) {
unifiedRouter.apply {
val key = decodeUrlQueryValueOrSendError(
keyParameterName,
keySerializer
) ?: return@get
unianswer(
Boolean.serializer(),
originalRepo.contains(key)
)
}
}
get(containsByKeyValueRoute) {
unifiedRouter.apply {
val key = decodeUrlQueryValueOrSendError(
keyParameterName,
keySerializer
) ?: return@get
val value = decodeUrlQueryValueOrSendError(
valueParameterName,
valueSerializer
) ?: return@get
unianswer(
Boolean.serializer(),
originalRepo.contains(key, value)
)
}
}
get(countByKeyRoute) {
unifiedRouter.apply {
val key = decodeUrlQueryValueOrSendError(
keyParameterName,
keySerializer
) ?: return@get
unianswer(
Long.serializer(),
originalRepo.count(key)
)
}
}
get(countRoute) {
unifiedRouter.apply {
unianswer(
Long.serializer(),
originalRepo.count()
)
}
}
}
inline fun <Key, Value> Route.configureOneToManyReadKeyValueRepoRoutes(
originalRepo: ReadKeyValuesRepo<Key, Value>,
keySerializer: KSerializer<Key>,
valueSerializer: KSerializer<Value>,
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat,
serialFormatContentType: ContentType = standardKtorSerialFormatContentType
) = configureOneToManyReadKeyValueRepoRoutes(originalRepo, keySerializer, valueSerializer, UnifiedRouter(serialFormat, serialFormatContentType))

View File

@@ -1,103 +0,0 @@
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.repos.WriteKeyValuesRepo
import dev.inmo.micro_utils.repos.ktor.common.one_to_many.*
import io.ktor.http.ContentType
import io.ktor.server.routing.Route
import io.ktor.server.routing.post
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.*
fun <Key, Value> Route.configureOneToManyWriteKeyValueRepoRoutes(
originalRepo: WriteKeyValuesRepo<Key, Value>,
keySerializer: KSerializer<Key>,
valueSerializer: KSerializer<Value>,
unifiedRouter: UnifiedRouter
) {
val keyValueSerializer = PairSerializer(keySerializer, valueSerializer)
val keyValueMapSerializer = MapSerializer(keySerializer, ListSerializer(valueSerializer))
unifiedRouter.apply {
includeWebsocketHandling(
onNewValueRoute,
originalRepo.onNewValue,
keyValueSerializer
)
includeWebsocketHandling(
onValueRemovedRoute,
originalRepo.onValueRemoved,
keyValueSerializer
)
includeWebsocketHandling(
onDataClearedRoute,
originalRepo.onDataCleared,
keySerializer
)
}
post(addRoute) {
unifiedRouter.apply {
val obj = uniload(keyValueMapSerializer)
unianswer(
Unit.serializer(),
originalRepo.add(obj)
)
}
}
post(removeRoute) {
unifiedRouter.apply {
val obj = uniload(keyValueMapSerializer)
unianswer(
Unit.serializer(),
originalRepo.remove(obj),
)
}
}
post(clearRoute) {
unifiedRouter.apply {
val key = uniload(keySerializer)
unianswer(
Unit.serializer(),
originalRepo.clear(key),
)
}
}
post(clearWithValueRoute) {
unifiedRouter.apply {
val v = uniload(valueSerializer)
unianswer(
Unit.serializer(),
originalRepo.clearWithValue(v),
)
}
}
post(setRoute) {
unifiedRouter.apply {
val obj = uniload(keyValueMapSerializer)
unianswer(
Unit.serializer(),
originalRepo.set(obj)
)
}
}
}
fun <Key, Value> Route.configureOneToManyWriteKeyValueRepoRoutes(
originalRepo: WriteKeyValuesRepo<Key, Value>,
keySerializer: KSerializer<Key>,
valueSerializer: KSerializer<Value>,
serialFormat: StandardKtorSerialFormat = standardKtorSerialFormat,
serialFormatContentType: ContentType = standardKtorSerialFormatContentType
) = configureOneToManyWriteKeyValueRepoRoutes(originalRepo, keySerializer, valueSerializer, UnifiedRouter(serialFormat, serialFormatContentType))

17
safe_wrapper/build.gradle Normal file
View File

@@ -0,0 +1,17 @@
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library"
}
apply from: "$mppProjectWithSerializationPresetPath"
kotlin {
sourceSets {
commonMain {
dependencies {
api project(":micro_utils.coroutines")
}
}
}
}

View File

@@ -0,0 +1,17 @@
package dev.inmo.micro_utils.safe_wrapper
import dev.inmo.micro_utils.coroutines.runCatchingSafely
interface SafeWrapper<T> {
fun <R> safe(block: T.() -> R): Result<R> = unsafeTarget().runCatching(block)
fun <R> unsafe(block: T.() -> R): R = unsafeTarget().block()
suspend fun <R> safeS(block: suspend T.() -> R): Result<R> = unsafeTarget().runCatchingSafely(block = block)
suspend fun <R> unsafeS(block: suspend T.() -> R): R = unsafeTarget().block()
fun unsafeTarget(): T
class Default<T>(private val t: T) : SafeWrapper<T> { override fun unsafeTarget(): T = t }
companion object {
operator fun <T> invoke(t: T) = Default(t)
}
}

View File

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

View File

@@ -4,6 +4,7 @@ String[] includes = [
":common",
":common:compose",
":matrix",
":safe_wrapper",
":crypto",
":koin",
":selector:common",
@@ -32,6 +33,8 @@ String[] includes = [
":serialization:base64",
":serialization:encapsulator",
":serialization:typed_serializer",
":startup:plugin",
":startup:launcher",
":fsm:common",
":fsm:repos:common",

View File

@@ -0,0 +1,92 @@
# Startup Plugin Launcher
This module contains tools to start your plugin system.
## Config
Base config is pretty simple:
```json
{
"plugins": [
"dev.inmo.micro_utils.startup.launcher.HelloWorldPlugin"
]
}
```
So, `"dev.inmo.micro_utils.startup.launcher.HelloWorldPlugin"` is the fully qualified name of plugin you wish to be
included in the server.
> JS note: In JS there are no opportunity to determine object type by its full name. Because of it, in JS developers
> should prefer to use `Config` in their kotlin code directly instead of json config passing. More info see in [JS](#js)
> section
## JVM
For JVM target you may use main class by path: `dev.inmo.micro_utils.startup.launcher.MainKt`
It is expected, that you will pass the main ONE argument with path to the config json. Sample of launching:
```bash
./gradlew run --args="sample.config.json"
```
Content of `sample.config.json` described in [Config](#config) section.
You may build runnable app using:
```bash
./gradlew assembleDist
```
In that case in `build/distributions` folder you will be able to find zip and tar files with all required
tools for application running (via their `bin/app_name` binary). In that case yoy will not need to pass
`--args=...` and launch will look like `./bin/app_name sample.config.json`
## JS
In JS for starting of your plugins app, you should use `PluginsStarter` in your code:
```kotlin
PluginsStarter.startPlugins(
Config(HelloWorldPlugin)
)
```
`Config` here is deserialized variant from [Config](#config) section. As was said in [Config](#config) section, in JS
there is no way to find classes/objects by their full qualifiers. Because of it you should use some way to register your
plugins in `StartPluginSerializer` or use the code like in the snippet above: there plugins will be registered
automatically.
In case you wish to register your plugins manually and run server from config, you should use one of the ways to register
plugin on start.
Sample with `EagerInitialization`: [Kotlin JS doc about lazy initialization](https://kotlinlang.org/docs/js-ir-compiler.html#incremental-compilation-for-development-binaries),
[@EagerInitialization docs](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.js/-eager-initialization/):
```kotlin
@ExperimentalStdlibApi
@EagerInitialization
val plugin = createStartupPluginAndRegister("PluginNameToUseInConfig") {
// Your plugin creation. For example:
HelloWorldPlugin
}
```
So, in that case you will be able to load plugins list as `JsonObject` from anywhere and start plugins app with it:
```kotlin
PluginsStarter.startPlugins(
jsonObject
)
```
It will load `HelloWorldPlugin` if `jsonObject` have next content:
```json
{
"plugins": [
"PluginNameToUseInConfig"
]
}
```

View File

@@ -0,0 +1,31 @@
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
id "application"
}
apply from: "$mppJsAndJavaProjectPresetPath"
kotlin {
sourceSets {
commonMain {
dependencies {
api internalProject("micro_utils.startup.plugin")
}
}
commonTest {
dependencies {
implementation libs.kt.coroutines.test
}
}
}
}
application {
mainClassName = "dev.inmo.micro_utils.startup.launcher.MainKt"
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

View File

@@ -0,0 +1,22 @@
package dev.inmo.micro_utils.startup.launcher
import dev.inmo.micro_utils.startup.plugin.StartPlugin
import kotlinx.serialization.Serializable
/**
* Contains just [List] of [StartPlugin]s. In json this config should look like:
*
* ```json
* {
* "plugins": [
* "dev.inmo.micro_utils.startup.launcher.HelloWorldPlugin"
* ]
* }
* ```
*
* In the sample above [HelloWorldPlugin] will be loaded during startup of application
*/
@Serializable
data class Config(
val plugins: List<StartPlugin>
)

View File

@@ -0,0 +1,7 @@
package dev.inmo.micro_utils.startup.launcher
import kotlinx.serialization.json.Json
val defaultJson = Json {
ignoreUnknownKeys = true
}

View File

@@ -0,0 +1,13 @@
package dev.inmo.micro_utils.startup.launcher
import dev.inmo.kslog.common.i
import dev.inmo.kslog.common.logger
import dev.inmo.micro_utils.startup.plugin.StartPlugin
import org.koin.core.Koin
object HelloWorldPlugin : StartPlugin {
override suspend fun startPlugin(koin: Koin) {
super.startPlugin(koin)
logger.i("Hello world")
}
}

View File

@@ -0,0 +1,17 @@
package dev.inmo.micro_utils.startup.launcher
import dev.inmo.micro_utils.startup.launcher.StartLauncherPlugin.setupDI
import kotlinx.serialization.json.JsonObject
import org.koin.core.KoinApplication
/**
* Will create [KoinApplication], init, load modules using [StartLauncherPlugin] and start plugins using the same base
* plugin
*
* @param rawConfig It is expected that this [JsonObject] will contain serialized [Config] ([StartLauncherPlugin] will
* deserialize it in its [StartLauncherPlugin.setupDI]
*/
@Deprecated("Fully replaced with StartLauncherPlugin#start", ReplaceWith("StartLauncherPlugin.start(rawConfig)", "dev.inmo.micro_utils.startup.launcher.StartLauncherPlugin"))
suspend fun start(rawConfig: JsonObject) {
StartLauncherPlugin.start(rawConfig)
}

View File

@@ -0,0 +1,141 @@
package dev.inmo.micro_utils.startup.launcher
import dev.inmo.kslog.common.i
import dev.inmo.kslog.common.taggedLogger
import dev.inmo.kslog.common.w
import dev.inmo.micro_utils.coroutines.runCatchingSafely
import dev.inmo.micro_utils.startup.launcher.StartLauncherPlugin.setupDI
import dev.inmo.micro_utils.startup.launcher.StartLauncherPlugin.startPlugin
import dev.inmo.micro_utils.startup.plugin.StartPlugin
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.joinAll
import kotlinx.coroutines.launch
import kotlinx.serialization.SerialFormat
import kotlinx.serialization.StringFormat
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.jsonObject
import org.koin.core.Koin
import org.koin.core.KoinApplication
import org.koin.core.context.startKoin
import org.koin.core.module.Module
import org.koin.dsl.binds
import org.koin.dsl.module
/**
* Default startup plugin. See [setupDI] and [startPlugin] for more info
*/
object StartLauncherPlugin : StartPlugin {
internal val logger = taggedLogger(this)
fun Module.setupDI(config: Config, rawJsonObject: JsonObject? = null) {
val rawJsonObject = rawJsonObject ?: defaultJson.encodeToJsonElement(Config.serializer(), config).jsonObject
single { rawJsonObject }
single { config }
single { CoroutineScope(Dispatchers.Default) }
single { defaultJson } binds arrayOf(StringFormat::class, SerialFormat::class)
includes(
config.plugins.mapNotNull {
runCatching {
module {
with(it) {
setupDI(rawJsonObject)
}
}
}.onFailure { e ->
logger.w("Unable to load DI part of $it", e)
}.getOrNull()
}
)
}
/**
* Will deserialize [Config] from [config], register it in receiver [Module] (as well as [CoroutineScope] and
* [kotlinx.serialization.json.Json])
*
* Besides, in this method will be called [StartPlugin.setupDI] on each plugin from [Config.plugins]. In case when
* some plugin will not be loaded correctly it will be reported throw the [logger]
*/
override fun Module.setupDI(config: JsonObject) {
logger.i("Koin for current module has started setup")
setupDI(
defaultJson.decodeFromJsonElement(Config.serializer(), config),
config
)
logger.i("Koin for current module has been setup")
}
/**
* Takes [CoroutineScope] and [Config] from the [koin], and call starting of each plugin from [Config.plugins]
* ASYNCHRONOUSLY. Just like in [setupDI], in case of fail in some plugin it will be reported using [logger]
*/
override suspend fun startPlugin(koin: Koin) {
logger.i("Start starting of subplugins")
val scope = koin.get<CoroutineScope>()
koin.get<Config>().plugins.map { plugin ->
scope.launch {
runCatchingSafely {
logger.i("Start loading of $plugin")
with(plugin) {
startPlugin(koin)
}
}.onFailure { e ->
logger.w("Unable to start plugin $plugin", e)
}.onSuccess {
logger.i("Complete loading of $plugin")
}
}
}.joinAll()
logger.i("Complete subplugins start")
}
/**
* Will create [KoinApplication], init, load modules using [StartLauncherPlugin] and start plugins using the same base
* plugin
*
* @param rawConfig It is expected that this [JsonObject] will contain serialized [Config] ([StartLauncherPlugin] will
* deserialize it in its [StartLauncherPlugin.setupDI]
*/
suspend fun start(rawConfig: JsonObject) {
logger.i("Start initialization")
val koinApp = KoinApplication.init()
koinApp.modules(
module {
setupDI(rawConfig)
}
)
logger.i("Modules loaded")
startKoin(koinApp)
logger.i("Koin started")
startPlugin(koinApp.koin)
logger.i("App has been setup")
}
/**
* Will create [KoinApplication], init, load modules using [StartLauncherPlugin] and start plugins using the same base
* plugin
*
* @param config In difference with other [start] method here config is already deserialized and this config will
* be converted to [JsonObject] as raw config. That means that all plugins from [config] will receive
* serialized version of [config] in [StartPlugin.setupDI] method
*/
suspend fun start(config: Config) {
logger.i("Start initialization")
val koinApp = KoinApplication.init()
logger.i("Koin app created")
koinApp.modules(
module {
setupDI(config)
}
)
startKoin(koinApp)
logger.i("Koin started")
startPlugin(koinApp.koin)
}
}

View File

@@ -0,0 +1,45 @@
import dev.inmo.micro_utils.startup.launcher.Config
import dev.inmo.micro_utils.startup.launcher.HelloWorldPlugin
import dev.inmo.micro_utils.startup.launcher.StartLauncherPlugin
import dev.inmo.micro_utils.startup.launcher.defaultJson
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.json.jsonObject
import org.koin.core.context.stopKoin
import kotlin.test.BeforeTest
import kotlin.test.Test
class StartupLaunchingTests {
@BeforeTest
fun resetGlobalKoinContext() {
runCatching { stopKoin() }
}
@Test
fun CheckThatEmptyPluginsListLeadsToEndOfMain() {
val emptyJson = defaultJson.encodeToJsonElement(
Config.serializer(),
Config(emptyList())
).jsonObject
runTest {
val job = launch {
StartLauncherPlugin.start(emptyJson)
}
job.join()
}
}
@Test
fun CheckThatHelloWorldPluginsListLeadsToEndOfMain() {
val emptyJson = defaultJson.encodeToJsonElement(
Config.serializer(),
Config(listOf(HelloWorldPlugin))
).jsonObject
runTest {
val job = launch {
StartLauncherPlugin.start(emptyJson)
}
job.join()
}
}
}

View File

@@ -0,0 +1,25 @@
package dev.inmo.micro_utils.startup.launcher
import dev.inmo.kslog.common.KSLog
import dev.inmo.kslog.common.i
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.jsonObject
@Deprecated("Useless due to including of the same functionality in StrtLauncherPlugin")
object PluginsStarter {
init {
KSLog.default = KSLog("Launcher")
}
/**
* It is expected that you have registered all the [dev.inmo.micro_utils.startup.plugin.StartPlugin]s of your JS
* app inside of [dev.inmo.micro_utils.startup.plugin.StartPluginSerializer] using its
* [dev.inmo.micro_utils.startup.plugin.StartPluginSerializer.registerPlugin] method
*/
suspend fun startPlugins(json: JsonObject) = StartLauncherPlugin.start(json)
/**
* Will convert [config] to [JsonObject] with auto registration of [dev.inmo.micro_utils.startup.plugin.StartPlugin]s
* in [dev.inmo.micro_utils.startup.plugin.StartPluginSerializer]
*/
suspend fun startPlugins(config: Config) = StartLauncherPlugin.start(config)
}

View File

@@ -0,0 +1,37 @@
package dev.inmo.micro_utils.startup.launcher
import dev.inmo.kslog.common.KSLog
import dev.inmo.kslog.common.i
import kotlinx.serialization.json.jsonObject
import java.io.File
/**
* It is expected, that [args] will contain ONE argument with path to the config json. Sample of launching:
*
* ```bash
* ./gradlew run --args="sample.config.json"
* ```
*
* Content of `sample.config.json` described in [Config] KDocs.
*
* You may build runnable app using:
*
* ```bash
* ./gradlew assembleDist
* ```
*
* In that case in `build/distributions` folder you will be able to find zip and tar files with all required
* tools for application running (via their `bin/app_name` binary). In that case yoy will not need to pass
* `--args=...` and launch will look like `./bin/app_name sample.config.json`
*/
suspend fun main(args: Array<String>) {
KSLog.default = KSLog("Launcher")
val (configPath) = args
val file = File(configPath)
KSLog.i("Start read config from ${file.absolutePath}")
val json = defaultJson.parseToJsonElement(file.readText()).jsonObject
KSLog.i("Config has been read")
StartLauncherPlugin.start(json)
}

View File

@@ -0,0 +1,25 @@
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
}
apply from: "$mppJsAndJavaProjectPresetPath"
kotlin {
sourceSets {
commonMain {
dependencies {
api libs.koin
api libs.kt.serialization
api libs.kslog
api libs.kt.reflect
api project(":micro_utils.coroutines")
}
}
jsMain {
dependencies {
api libs.uuid
}
}
}
}

View File

@@ -0,0 +1,31 @@
package dev.inmo.micro_utils.startup.plugin
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonObject
import org.koin.core.Koin
import org.koin.core.module.Module
/**
* Default plugin for start of your app
*/
@Serializable(StartPluginSerializer::class)
interface StartPlugin {
/**
* This method will be called first to configure [Koin] [Module] related to this plugin. You may use
* [org.koin.core.scope.Scope.get] in your koin definitions like [Module.single] to retrieve
* [kotlinx.coroutines.CoroutineScope], [kotlinx.serialization.json.Json] or [dev.inmo.micro_utils.startup.launcher.Config]
*/
fun Module.setupDI(config: JsonObject) {}
/**
* This method will be called after all other [StartPlugin] will [setupDI]
*
* It is allowed to lock end of this method in case you require to prevent application to end its run (for example,
* you are starting some web server)
*
* @param koin Will contains everything you will register in [setupDI] (as well as other [StartPlugin]s) and
* [kotlinx.coroutines.CoroutineScope], [kotlinx.serialization.json.Json] and [dev.inmo.micro_utils.startup.launcher.Config]
* by their types
*/
suspend fun startPlugin(koin: Koin) {}
}

View File

@@ -0,0 +1,5 @@
package dev.inmo.micro_utils.startup.plugin
import kotlinx.serialization.KSerializer
expect object StartPluginSerializer : KSerializer<StartPlugin>

View File

@@ -0,0 +1,38 @@
package dev.inmo.micro_utils.startup.plugin
import com.benasher44.uuid.uuid4
import kotlin.reflect.KClass
/**
* Creates [T] using [block], register it in [StartPluginSerializer] using its [StartPluginSerializer.registerPlugin]
* and returns created by [block] plugin
*
* @param name Will be used as a key for registration in [StartPluginSerializer] and will be passed to the [block] as
* parameter
*/
inline fun <T : StartPlugin> createStartupPluginAndRegister(
name: String = uuid4().toString(),
block: (String) -> T
): T {
val plugin = block(name)
StartPluginSerializer.registerPlugin(name, plugin)
return plugin
}
/**
* Creates [T] using [block], register it in [StartPluginSerializer] using its [StartPluginSerializer.registerPlugin]
* and returns created by [block] plugin
*/
inline fun <T : StartPlugin> createStartupPluginAndRegister(
kClass: KClass<T>,
name: String = uuid4().toString(),
block: (String) -> T
): T = createStartupPluginAndRegister("${kClass.simpleName}_$name", block)
/**
* Creates [T] using [block], register it in [StartPluginSerializer] using its [StartPluginSerializer.registerPlugin]
* and returns created by [block] plugin
*/
inline fun <reified T : StartPlugin> createStartupPluginAndRegister(
block: (String) -> T
): T = createStartupPluginAndRegister(T::class, uuid4().toString(), block)

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