temporal push for keeping history

This commit is contained in:
2020-08-05 16:21:30 +06:00
parent 8686e60fbc
commit 93f70fe874
116 changed files with 562 additions and 91 deletions
core
exposed/commons
gradle.properties
gradle/wrapper
ktor
markups
build.gradle
html
build.gradlegradle.propertiesmaven.publish.gradlepublish.gradlepublish_config.kpsb
src
jsMain
kotlin
com
insanusmokrassar
postssystem
jvmMain
kotlin
com
insanusmokrassar
postssystem
maven.publish.gradlepublish.gradlepublish_config.kpsb
src
commonMain
kotlin
com
postssystem.markups.core/src/commonMain/kotlin/com/insanusmokrassar/postssystem/markups/core
postssystem.markups.html/src
jsMain
kotlin
com
insanusmokrassar
postssystem
jvmMain
kotlin
com
insanusmokrassar
postssystem
publishing
settings.gradle
utils/repos

79
utils/repos/build.gradle Normal file

@ -0,0 +1,79 @@
buildscript {
repositories {
mavenLocal()
jcenter()
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:$gradle_bintray_plugin_version"
}
}
plugins {
id "org.jetbrains.kotlin.multiplatform" version "$kotlin_version"
id "org.jetbrains.kotlin.plugin.serialization" version "$kotlin_version"
}
project.version = "$core_version"
project.group = "com.insanusmokrassar"
apply from: "publish.gradle"
repositories {
mavenLocal()
jcenter()
mavenCentral()
maven { url "https://kotlin.bintray.com/kotlinx" }
}
kotlin {
jvm()
js()
sourceSets {
commonMain {
dependencies {
implementation kotlin('stdlib')
api "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:$kotlin_coroutines_version"
api "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$kotlin_serialisation_runtime_version"
api "com.soywiz.korlibs.klock:klock:$klockVersion"
api "com.benasher44:uuid:$uuidVersion"
}
}
commonTest {
dependencies {
implementation kotlin('test-common')
implementation kotlin('test-annotations-common')
}
}
jvmMain {
dependencies {
implementation kotlin('stdlib-jdk8')
api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
api "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$kotlin_serialisation_runtime_version"
}
}
jvmTest {
dependencies {
implementation kotlin('test-junit')
}
}
jsMain {
dependencies {
implementation kotlin('stdlib-js')
api "org.jetbrains.kotlinx:kotlinx-coroutines-core-js:$kotlin_coroutines_version"
api "org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:$kotlin_serialisation_runtime_version"
}
}
jsTest {
dependencies {
implementation kotlin('test-js')
implementation kotlin('test-junit')
}
}
}
}

@ -0,0 +1,47 @@
buildscript {
repositories {
mavenLocal()
jcenter()
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:$gradle_bintray_plugin_version"
}
}
plugins {
id "org.jetbrains.kotlin.plugin.serialization"
}
project.version = "$core_version"
project.group = "com.insanusmokrassar"
apply plugin: "java-library"
apply plugin: "kotlin"
apply from: "./publish.gradle"
repositories {
mavenLocal()
jcenter()
mavenCentral()
maven { url "https://kotlin.bintray.com/kotlinx" }
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
if ((project.hasProperty('RELEASE_MODE') && project.property('RELEASE_MODE') == "true") || System.getenv('RELEASE_MODE') == "true") {
api "com.insanusmokrassar:postssystem.utils.repos:$core_version"
api "com.insanusmokrassar:postssystem.exposed.commons:$core_version"
} else {
api project(":utils:repos")
api project(":exposed:commons")
}
testImplementation "org.xerial:sqlite-jdbc:$test_sqlite_version"
testImplementation "org.jetbrains.kotlin:kotlin-test"
testImplementation "org.jetbrains.kotlin:kotlin-test-junit"
}

@ -0,0 +1 @@
test_sqlite_version=3.28.0

@ -0,0 +1,53 @@
apply plugin: 'maven-publish'
task javadocsJar(type: Jar) {
classifier = 'javadoc'
}
afterEvaluate {
project.publishing.publications.all {
// rename artifacts
groupId "${project.group}"
if (it.name.contains('kotlinMultiplatform')) {
artifactId = "${project.name}"
} else {
artifactId = "${project.name}-$name"
}
}
}
publishing {
publications.all {
artifact javadocsJar
pom {
description = "Common utils for all exposed modules"
name = "PostsSystem Exposed commons"
url = "https://git.insanusmokrassar.com/PostsSystem/Core/"
scm {
developerConnection = "scm:git:[fetch=]https://git.insanusmokrassar.com/PostsSystem/Core/.git[push=]https://git.insanusmokrassar.com/PostsSystem/Core/.git"
url = "https://git.insanusmokrassar.com/PostsSystem/Core/.git"
}
developers {
developer {
id = "InsanusMokrassar"
name = "Ovsiannikov Aleksei"
email = "ovsyannikov.alexey95@gmail.com"
}
}
licenses {
license {
name = "Apache Software License 2.0"
url = "https://git.insanusmokrassar.com/PostsSystem/Core/src/master/LICENSE"
}
}
}
}
}

@ -0,0 +1,55 @@
apply plugin: 'com.jfrog.bintray'
apply from: "maven.publish.gradle"
bintray {
user = project.hasProperty('BINTRAY_USER') ? project.property('BINTRAY_USER') : System.getenv('BINTRAY_USER')
key = project.hasProperty('BINTRAY_KEY') ? project.property('BINTRAY_KEY') : System.getenv('BINTRAY_KEY')
filesSpec {
from "${buildDir}/publications/"
eachFile {
String directorySubname = it.getFile().parentFile.name
if (it.getName() == "module.json") {
if (directorySubname == "kotlinMultiplatform") {
it.setPath("${project.name}/${project.version}/${project.name}-${project.version}.module")
} else {
it.setPath("${project.name}-${directorySubname}/${project.version}/${project.name}-${directorySubname}-${project.version}.module")
}
} else {
if (directorySubname == "kotlinMultiplatform" && it.getName() == "pom-default.xml") {
it.setPath("${project.name}/${project.version}/${project.name}-${project.version}.pom")
} else {
it.exclude()
}
}
}
into "${project.group}".replace(".", "/")
}
pkg {
repo = "InsanusMokrassar"
name = "${project.name}"
vcsUrl = "https://github.com/PostsSystem/PostsSystemCore"
licenses = ["Apache-2.0"]
version {
name = "${project.version}"
released = new Date()
vcsTag = "${project.version}"
gpg {
sign = true
passphrase = project.hasProperty('signing.gnupg.passphrase') ? project.property('signing.gnupg.passphrase') : System.getenv('signing.gnupg.passphrase')
}
}
}
}
bintrayUpload.doFirst {
publications = publishing.publications.collect {
if (it.name.contains('kotlinMultiplatform')) {
null
} else {
it.name
}
} - null
}
bintrayUpload.dependsOn publishToMavenLocal

@ -0,0 +1 @@
{"bintrayConfig":{"repo":"InsanusMokrassar","packageName":"${project.name}","packageVcs":"https://github.com/PostsSystem/PostsSystemCore"},"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://git.insanusmokrassar.com/PostsSystem/Core/src/master/LICENSE"}],"mavenConfig":{"name":"PostsSystem Exposed commons","description":"Common utils for all exposed modules","url":"https://git.insanusmokrassar.com/PostsSystem/Core/","vcsUrl":"https://git.insanusmokrassar.com/PostsSystem/Core/.git","developers":[{"id":"InsanusMokrassar","name":"Ovsiannikov Aleksei","eMail":"ovsyannikov.alexey95@gmail.com"}]},"type":"Multiplatform"}

@ -0,0 +1,13 @@
package com.insanusmokrassar.postssystem.utils.repos.exposed
import kotlinx.coroutines.channels.Channel
abstract class AbstractExposedCRUDRepo<ObjectType, IdType, InputValueType>(
flowsChannelsSize: Int = Channel.BUFFERED,
databaseName: String = ""
) :
AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
flowsChannelsSize,
databaseName
),
ExposedCRUDRepo<ObjectType, IdType>

@ -0,0 +1,37 @@
package com.insanusmokrassar.postssystem.utils.repos.exposed
import com.insanusmokrassar.postssystem.exposed.commons.paginate
import com.insanusmokrassar.postssystem.utils.repos.ReadStandardCRUDRepo
import com.insanusmokrassar.postssystem.utils.repos.pagination.*
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction
abstract class AbstractExposedReadCRUDRepo<ObjectType, IdType>(
tableName: String
) :
ReadStandardCRUDRepo<ObjectType, IdType>,
ExposedCRUDRepo<ObjectType, IdType>,
Table(tableName)
{
override suspend fun getByPagination(pagination: Pagination): PaginationResult<ObjectType> {
return transaction(db = database) {
selectAll().paginate(pagination).map {
it.asObject
}.createPaginationResult(
pagination,
selectAll().count()
)
}
}
override suspend fun getById(id: IdType): ObjectType? {
return transaction(db = database) {
select {
selectById(id)
}.limit(1).firstOrNull() ?.asObject
}
}
override suspend fun contains(id: IdType): Boolean = transaction(db = database) {
select { selectById(id) }.limit(1).any()
}
}

@ -0,0 +1,105 @@
package com.insanusmokrassar.postssystem.utils.repos.exposed
import com.insanusmokrassar.postssystem.utils.repos.UpdatedValuePair
import com.insanusmokrassar.postssystem.utils.repos.WriteStandardCRUDRepo
import kotlinx.coroutines.channels.BroadcastChannel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.asFlow
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.statements.InsertStatement
import org.jetbrains.exposed.sql.statements.UpdateStatement
import org.jetbrains.exposed.sql.transactions.transaction
abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
flowsChannelsSize: Int = 64,
databaseName: String = ""
) :
AbstractExposedReadCRUDRepo<ObjectType, IdType>(databaseName),
ExposedCRUDRepo<ObjectType, IdType>,
WriteStandardCRUDRepo<ObjectType, IdType, InputValueType>
{
protected val newObjectsChannel = BroadcastChannel<ObjectType>(flowsChannelsSize)
protected val updateObjectsChannel = BroadcastChannel<ObjectType>(flowsChannelsSize)
protected val deleteObjectsIdsChannel = BroadcastChannel<IdType>(flowsChannelsSize)
override val newObjectsFlow: Flow<ObjectType> = newObjectsChannel.asFlow()
override val updatedObjectsFlow: Flow<ObjectType> = updateObjectsChannel.asFlow()
override val deletedObjectsIdsFlow: Flow<IdType> = deleteObjectsIdsChannel.asFlow()
abstract val InsertStatement<Number>.asObject: ObjectType
abstract val selectByIds: SqlExpressionBuilder.(Array<out IdType>) -> Op<Boolean>
protected abstract fun insert(value: InputValueType, it: InsertStatement<Number>)
protected abstract fun update(id: IdType, value: InputValueType, it: UpdateStatement)
protected open suspend fun onBeforeCreate(vararg value: InputValueType) {}
private fun createWithoutNotification(value: InputValueType): ObjectType {
return transaction(database) {
insert { insert(value, it) }.asObject
}
}
override suspend fun create(vararg values: InputValueType): List<ObjectType> {
onBeforeCreate(*values)
return transaction(db = database) {
values.map { value -> createWithoutNotification(value) }
}.also {
it.forEach {
newObjectsChannel.send(it)
}
}
}
protected open suspend fun onBeforeUpdate(vararg value: UpdatedValuePair<IdType, InputValueType>) {}
private fun updateWithoutNotification(id: IdType, value: InputValueType): ObjectType? {
return transaction(db = database) {
update(
{
selectById(this, id)
}
) {
update(id, value, it)
}
}.let {
if (it > 0) {
select {
selectById(this, id)
}.limit(1).firstOrNull() ?.asObject
} else {
null
}
}
}
override suspend fun update(id: IdType, value: InputValueType): ObjectType? {
onBeforeUpdate(id to value)
return updateWithoutNotification(id, value).also {
if (it != null) {
updateObjectsChannel.send(it)
}
}
}
override suspend fun update(vararg values: UpdatedValuePair<IdType, InputValueType>): List<ObjectType> {
onBeforeUpdate(*values)
return (
transaction(db = database) {
values.map { (id, value) -> updateWithoutNotification(id, value) }
}.filter {
it != null
} as List<ObjectType>
).also {
it.forEach {
updateObjectsChannel.send(it)
}
}
}
protected open suspend fun onBeforeDelete(vararg ids: IdType) {}
override suspend fun deleteById(vararg ids: IdType) {
onBeforeDelete(*ids)
transaction(db = database) {
deleteWhere(null, null) {
selectByIds(ids)
}
}
}
}

@ -0,0 +1,10 @@
package com.insanusmokrassar.postssystem.utils.repos.exposed
import org.jetbrains.exposed.sql.*
interface ExposedCRUDRepo<ObjectType, IdType> {
val database: Database
val ResultRow.asObject: ObjectType
val selectById: SqlExpressionBuilder.(IdType) -> Op<Boolean>
}

@ -0,0 +1,8 @@
package com.insanusmokrassar.postssystem.utils.repos.exposed
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction
fun Table.initTable(database: Database) {
transaction(database) { SchemaUtils.createMissingTablesAndColumns(this@initTable) }
}

@ -0,0 +1,48 @@
package com.insanusmokrassar.postssystem.utils.repos.exposed.keyvalue
import com.insanusmokrassar.postssystem.utils.repos.StandardKeyValueRepo
import kotlinx.coroutines.channels.BroadcastChannel
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.asFlow
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction
abstract class AbstractExposedKeyValueRepo<Key, Value>(
database: Database,
keyColumn: Column<Key>,
valueColumn: Column<Value>
) : StandardKeyValueRepo<Key, Value>, AbstractExposedReadKeyValueRepo<Key, Value>(
database,
keyColumn,
valueColumn
) {
private val onNewValueChannel = BroadcastChannel<Pair<Key, Value>>(Channel.BUFFERED)
private val onValueRemovedChannel = BroadcastChannel<Key>(Channel.BUFFERED)
override val onNewValue: Flow<Pair<Key, Value>> = onNewValueChannel.asFlow()
override val onValueRemoved: Flow<Key> = onValueRemovedChannel.asFlow()
override suspend fun set(k: Key, v: Value) {
transaction(db = database) {
if (select { keyColumn.eq(k) }.limit(1).any()) {
update({ keyColumn.eq(k) }) {
it[valueColumn] = v
}
} else {
insert {
it[keyColumn] = k
it[valueColumn] = v
}
}
}
onNewValueChannel.send(k to v)
}
override suspend fun unset(k: Key) {
transaction(db = database) {
deleteWhere { keyColumn.eq(k) }
}
onValueRemovedChannel.send(k)
}
}

@ -0,0 +1,37 @@
package com.insanusmokrassar.postssystem.utils.repos.exposed.keyvalue
import com.insanusmokrassar.postssystem.exposed.commons.paginate
import com.insanusmokrassar.postssystem.utils.repos.StandardReadKeyValueRepo
import com.insanusmokrassar.postssystem.utils.repos.pagination.*
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction
abstract class AbstractExposedReadKeyValueRepo<Key, Value>(
protected val database: Database,
protected val keyColumn: Column<Key>,
protected val valueColumn: Column<Value>
) : StandardReadKeyValueRepo<Key, Value>, Table() {
override val primaryKey: PrimaryKey = PrimaryKey(keyColumn, valueColumn)
override suspend fun get(k: Key): Value? = transaction(db = database) {
select { keyColumn.eq(k) }.limit(1).firstOrNull() ?.getOrNull(valueColumn)
}
override suspend fun contains(key: Key): Boolean = transaction(db = database) {
select { keyColumn.eq(key) }.limit(1).any()
}
override suspend fun count(): Long = transaction(db = database) { selectAll().count() }
override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<Key> = transaction(db = database) {
selectAll().paginate(pagination, keyColumn to if (reversed) SortOrder.DESC else SortOrder.ASC).map {
it[keyColumn]
}
}.createPaginationResult(pagination, count())
override suspend fun values(pagination: Pagination, reversed: Boolean): PaginationResult<Value> = transaction(db = database) {
selectAll().paginate(pagination, keyColumn to if (reversed) SortOrder.DESC else SortOrder.ASC).map {
it[valueColumn]
}
}.createPaginationResult(pagination, count())
}

@ -0,0 +1,34 @@
package com.insanusmokrassar.postssystem.utils.repos.exposed.onetomany
import com.insanusmokrassar.budgetmanager.core.utils.repo.onetomany.AbstractOneToManyExposedReadKeyValueRepo
import com.insanusmokrassar.budgetmanager.core.utils.repo.onetomany.ColumnAllocator
import com.insanusmokrassar.postssystem.utils.repos.OneToManyKeyValueRepo
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction
abstract class AbstractOneToManyExposedKeyValueRepo<Key, Value>(
keyColumnAllocator: ColumnAllocator<Key>,
valueColumnAllocator: ColumnAllocator<Value>,
database: Database
) : OneToManyKeyValueRepo<Key, Value>, AbstractOneToManyExposedReadKeyValueRepo<Key, Value>(
keyColumnAllocator,
valueColumnAllocator,
database
) {
override suspend fun add(k: Key, v: Value) {
transaction(db = database) {
insert {
it[keyColumn] = k
it[valueColumn] = v
}
}
}
override suspend fun remove(k: Key, v: Value) {
transaction(db = database) { deleteWhere { keyColumn.eq(k).and(valueColumn.eq(v)) } }
}
override suspend fun clear(k: Key) {
transaction(db = database) { deleteWhere { keyColumn.eq(k) } }
}
}

@ -0,0 +1,48 @@
package com.insanusmokrassar.budgetmanager.core.utils.repo.onetomany
import com.insanusmokrassar.postssystem.exposed.commons.paginate
import com.insanusmokrassar.postssystem.utils.repos.OneToManyReadKeyValueRepo
import com.insanusmokrassar.postssystem.utils.repos.pagination.*
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction
typealias ColumnAllocator<T> = Table.() -> Column<T>
abstract class AbstractOneToManyExposedReadKeyValueRepo<Key, Value>(
keyColumnAllocator: ColumnAllocator<Key>,
valueColumnAllocator: ColumnAllocator<Value>,
protected val database: Database
) : OneToManyReadKeyValueRepo<Key, Value>, Table() {
protected val keyColumn: Column<Key> = keyColumnAllocator()
protected val valueColumn: Column<Value> = valueColumnAllocator()
override suspend fun count(k: Key): Long = transaction(db = database) { select { keyColumn.eq(k) }.count() }
override suspend fun count(): Long = transaction(db = database) { selectAll().count() }
override suspend fun get(
k: Key,
pagination: Pagination,
reversed: Boolean
): PaginationResult<Value> = transaction(db = 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(db = database) {
selectAll().paginate(pagination, keyColumn, reversed).map { it[keyColumn] }
}.createPaginationResult(
pagination,
count()
)
override suspend fun contains(k: Key): Boolean = transaction(db = database) {
select { keyColumn.eq(k) }.limit(1).any()
}
override suspend fun contains(k: Key, v: Value): Boolean = transaction(db = database) {
select { keyColumn.eq(k).and(valueColumn.eq(v)) }.limit(1).any()
}
}

@ -0,0 +1,60 @@
apply plugin: 'maven-publish'
task javadocsJar(type: Jar) {
classifier = 'javadoc'
}
afterEvaluate {
project.publishing.publications.all {
// rename artifacts
groupId "${project.group}"
if (it.name.contains('kotlinMultiplatform')) {
artifactId = "${project.name}"
} else {
artifactId = "${project.name}-$name"
}
}
}
publishing {
publications.all {
artifact javadocsJar
pom {
description = "PostsSystem Core Repos utility subproject"
name = "PostsSystem Core Repos"
url = "https://git.insanusmokrassar.com/PostsSystem/PostsSystemCore"
scm {
developerConnection = "scm:git:[fetch=]https://git.insanusmokrassar.com/PostsSystem/PostsSystemCore.git[push=]https://git.insanusmokrassar.com/PostsSystem/PostsSystemCore.git"
url = "https://git.insanusmokrassar.com/PostsSystem/PostsSystemCore.git"
}
developers {
developer {
id = "InsanusMokrassar"
name = "Ovsiannikov Aleksei"
email = "ovsyannikov.alexey95@gmail.com"
}
developer {
id = "mi-ast"
name = "Michail Astafiev"
email = "astaf65@gmail.com"
}
}
licenses {
license {
name = "Apache Software License 2.0"
url = "https://git.insanusmokrassar.com/PostsSystem/PostsSystemCore/src/master/LICENSE"
}
}
}
}
}

@ -0,0 +1,55 @@
apply plugin: 'com.jfrog.bintray'
apply from: "maven.publish.gradle"
bintray {
user = project.hasProperty('BINTRAY_USER') ? project.property('BINTRAY_USER') : System.getenv('BINTRAY_USER')
key = project.hasProperty('BINTRAY_KEY') ? project.property('BINTRAY_KEY') : System.getenv('BINTRAY_KEY')
filesSpec {
from "${buildDir}/publications/"
eachFile {
String directorySubname = it.getFile().parentFile.name
if (it.getName() == "module.json") {
if (directorySubname == "kotlinMultiplatform") {
it.setPath("${project.name}/${project.version}/${project.name}-${project.version}.module")
} else {
it.setPath("${project.name}-${directorySubname}/${project.version}/${project.name}-${directorySubname}-${project.version}.module")
}
} else {
if (directorySubname == "kotlinMultiplatform" && it.getName() == "pom-default.xml") {
it.setPath("${project.name}/${project.version}/${project.name}-${project.version}.pom")
} else {
it.exclude()
}
}
}
into "${project.group}".replace(".", "/")
}
pkg {
repo = "InsanusMokrassar"
name = "${project.name}"
vcsUrl = "https://github.com/PostsSystem/PostsSystemCore"
licenses = ["Apache-2.0"]
version {
name = "${project.version}"
released = new Date()
vcsTag = "${project.version}"
gpg {
sign = true
passphrase = project.hasProperty('signing.gnupg.passphrase') ? project.property('signing.gnupg.passphrase') : System.getenv('signing.gnupg.passphrase')
}
}
}
}
bintrayUpload.doFirst {
publications = publishing.publications.collect {
if (it.name.contains('kotlinMultiplatform')) {
null
} else {
it.name
}
} - null
}
bintrayUpload.dependsOn publishToMavenLocal

@ -0,0 +1 @@
{"bintrayConfig":{"repo":"InsanusMokrassar","packageName":"${project.name}","packageVcs":"https://github.com/PostsSystem/PostsSystemCore"},"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://git.insanusmokrassar.com/PostsSystem/PostsSystemCore/src/master/LICENSE"}],"mavenConfig":{"name":"PostsSystem Core Repos","description":"PostsSystem Core Repos utility subproject","url":"https://git.insanusmokrassar.com/PostsSystem/PostsSystemCore","vcsUrl":"https://git.insanusmokrassar.com/PostsSystem/PostsSystemCore.git","developers":[{"id":"InsanusMokrassar","name":"Ovsiannikov Aleksei","eMail":"ovsyannikov.alexey95@gmail.com"},{"id":"mi-ast","name":"Michail Astafiev","eMail":"astaf65@gmail.com"}]},"type":"Multiplatform"}

@ -0,0 +1,5 @@
package com.insanusmokrassar.postssystem.utils.repos
import com.benasher44.uuid.uuid4
fun generateId() = uuid4().toString()

@ -0,0 +1,23 @@
package com.insanusmokrassar.postssystem.utils.repos
import com.insanusmokrassar.postssystem.utils.repos.pagination.Pagination
import com.insanusmokrassar.postssystem.utils.repos.pagination.PaginationResult
interface OneToManyReadKeyValueRepo<Key, Value> : Repo {
suspend fun get(k: Key, pagination: Pagination, reversed: Boolean = false): PaginationResult<Value>
suspend fun keys(pagination: Pagination, reversed: Boolean = false): PaginationResult<Key>
suspend fun contains(k: Key): Boolean
suspend fun contains(k: Key, v: Value): Boolean
suspend fun count(k: Key): Long
suspend fun count(): Long
}
interface OneToManyWriteKeyValueRepo<Key, Value> :
Repo {
suspend fun add(k: Key, v: Value)
suspend fun remove(k: Key, v: Value)
suspend fun clear(k: Key)
}
interface OneToManyKeyValueRepo<Key, Value> : OneToManyReadKeyValueRepo<Key, Value>,
OneToManyWriteKeyValueRepo<Key, Value>

@ -0,0 +1,4 @@
package com.insanusmokrassar.postssystem.utils.repos
interface Repo {
}

@ -0,0 +1,31 @@
package com.insanusmokrassar.postssystem.utils.repos
import com.insanusmokrassar.postssystem.utils.repos.pagination.Pagination
import com.insanusmokrassar.postssystem.utils.repos.pagination.PaginationResult
import kotlinx.coroutines.flow.Flow
interface ReadStandardCRUDRepo<ObjectType, IdType> : Repo {
suspend fun getByPagination(pagination: Pagination): PaginationResult<ObjectType>
suspend fun getById(id: IdType): ObjectType?
suspend fun contains(id: IdType): Boolean
}
typealias UpdatedValuePair<IdType, ValueType> = Pair<IdType, ValueType>
val <IdType> UpdatedValuePair<IdType, *>.id
get() = first
val <ValueType> UpdatedValuePair<*, ValueType>.value
get() = second
interface WriteStandardCRUDRepo<ObjectType, IdType, InputValueType> : Repo {
val newObjectsFlow: Flow<ObjectType>
val updatedObjectsFlow: Flow<ObjectType>
val deletedObjectsIdsFlow: Flow<IdType>
suspend fun create(vararg values: InputValueType): List<ObjectType>
suspend fun update(id: IdType, value: InputValueType): ObjectType?
suspend fun update(vararg values: UpdatedValuePair<IdType, InputValueType>): List<ObjectType>
suspend fun deleteById(vararg ids: IdType)
}
interface StandardCRUDRepo<ObjectType, IdType, InputValueType> : ReadStandardCRUDRepo<ObjectType, IdType>,
WriteStandardCRUDRepo<ObjectType, IdType, InputValueType>

@ -0,0 +1,24 @@
package com.insanusmokrassar.postssystem.utils.repos
import com.insanusmokrassar.postssystem.utils.repos.pagination.Pagination
import com.insanusmokrassar.postssystem.utils.repos.pagination.PaginationResult
import kotlinx.coroutines.flow.Flow
interface StandardReadKeyValueRepo<Key, Value> : Repo {
suspend fun get(k: Key): Value?
suspend fun values(pagination: Pagination, reversed: Boolean): PaginationResult<Value>
suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<Key>
suspend fun contains(key: Key): Boolean
suspend fun count(): Long
}
interface StandardWriteKeyValueRepo<Key, Value> : Repo {
val onNewValue: Flow<Pair<Key, Value>>
val onValueRemoved: Flow<Key>
suspend fun set(k: Key, v: Value)
suspend fun unset(k: Key)
}
interface StandardKeyValueRepo<Key, Value> : StandardReadKeyValueRepo<Key, Value>,
StandardWriteKeyValueRepo<Key, Value>

@ -0,0 +1,61 @@
package com.insanusmokrassar.postssystem.utils.repos.pagination
import kotlin.math.ceil
/**
* Base interface of pagination
*
* If you want to request something, you should use [SimplePagination]. If you need to return some result including
* pagination - [PaginationResult]
*/
interface Pagination {
/**
* Started with 0.
* Number of page inside of pagination. Offset can be calculated as [page] * [size]
*/
val page: Int
/**
* Can be 0, but can't be < 0
* Size of current page. Offset can be calculated as [page] * [size]
*/
val size: Int
}
/**
* First number in index of objects. It can be used as offset for databases or other data sources
*/
val Pagination.firstIndex: Int
get() = page * size
/**
* Last exclusive number in index of objects
*
* For [Pagination] with [Pagination.size] == 10 and [Pagination.page] == 1
* you will retrieve [Pagination.firstIndex] == 10 and [Pagination.lastExclusiveIndex] == 20.
*/
val Pagination.lastExclusiveIndex: Int
get() = firstIndex + size
/**
* Last number in index of objects. In fact, one [Pagination] object represent data in next range:
*
* [[firstIndex], [lastIndex]]; That means, that for [Pagination] with [Pagination.size] == 10 and [Pagination.page] == 1
* you will retrieve [Pagination.firstIndex] == 10 and [Pagination.lastIndex] == 19.
*/
val Pagination.lastIndex: Int
get() = lastExclusiveIndex - 1
/**
* Calculates pages count for given [datasetSize]
*/
fun calculatePagesNumber(datasetSize: Long, pageSize: Int): Int {
return ceil(datasetSize.toDouble() / pageSize).toInt()
}
/**
* Calculates pages count for given [datasetSize]
*/
fun calculatePagesNumber(datasetSize: Int, pageSize: Int): Int =
calculatePagesNumber(
datasetSize.toLong(),
pageSize
)

@ -0,0 +1,21 @@
package com.insanusmokrassar.postssystem.utils.repos.pagination
import kotlinx.serialization.Serializable
@Serializable
data class PaginationResult<T>(
override val page: Int,
val pagesNumber: Int,
val results: List<T>,
override val size: Int
) : Pagination
fun <T> List<T>.createPaginationResult(
pagination: Pagination,
commonObjectsNumber: Long
) = PaginationResult(
pagination.page,
calculatePagesNumber(commonObjectsNumber, pagination.size),
this,
pagination.size
)

@ -0,0 +1,28 @@
package com.insanusmokrassar.postssystem.utils.repos.pagination
import kotlinx.serialization.Serializable
const val defaultSmallPageSize = 2
const val defaultMediumPageSize = 5
const val defaultLargePageSize = 10
const val defaultExtraLargePageSize = 15
@Suppress("NOTHING_TO_INLINE", "FunctionName")
inline fun FirstPagePagination(size: Int = defaultMediumPageSize) =
SimplePagination(
page = 0,
size = size
)
@Suppress("NOTHING_TO_INLINE")
inline fun Pagination.nextPage() =
SimplePagination(
page + 1,
size
)
@Serializable
data class SimplePagination(
override val page: Int,
override val size: Int
) : Pagination