From e83c2b4e0b94e3bdc4d219aba14cff5a4cde6287 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sat, 11 Apr 2020 13:06:10 +0600 Subject: [PATCH] 0.4.0 --- gradle.properties | 2 +- postssystem.core/build.gradle | 1 + postssystem.core/gradle.properties | 1 + .../postssystem/core/content/Content.kt | 14 ++- .../core/content/api/WriteContentAPI.kt | 2 +- .../core/utils/ByteArrayAllocator.kt | 20 +++++ .../core/api/ContentSerialization.kt | 4 +- .../core/api/InMemoryContentAPI.kt | 56 ------------ .../postssystem/core/api/InMemoryPostsAPI.kt | 86 ------------------ .../core/exposed/ExposedContentAPI.kt | 90 ++++++++++++++----- .../content/BinaryContentHolderRepo.kt | 57 ++++++++++++ .../core/exposed/content/ContentHolderRepo.kt | 10 +++ .../content/SpecialContentHolderRepo.kt | 46 ++++++++++ .../exposed/content/TextContentHolderRepo.kt | 47 ++++++++++ .../exposed/ExposedContentAPICommonTests.kt | 12 ++- 15 files changed, 275 insertions(+), 173 deletions(-) create mode 100644 postssystem.core/gradle.properties create mode 100644 postssystem.core/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/utils/ByteArrayAllocator.kt delete mode 100644 postssystem.core/src/commonTest/kotlin/com/insanusmokrassar/postssystem/core/api/InMemoryContentAPI.kt delete mode 100644 postssystem.core/src/commonTest/kotlin/com/insanusmokrassar/postssystem/core/api/InMemoryPostsAPI.kt create mode 100644 postssystem.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/content/BinaryContentHolderRepo.kt create mode 100644 postssystem.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/content/ContentHolderRepo.kt create mode 100644 postssystem.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/content/SpecialContentHolderRepo.kt create mode 100644 postssystem.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/content/TextContentHolderRepo.kt diff --git a/gradle.properties b/gradle.properties index 8e402073..de5763e4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,4 +8,4 @@ uuidVersion=0.1.0 gradle_bintray_plugin_version=1.8.4 -core_version=0.3.0 +core_version=0.4.0 diff --git a/postssystem.core/build.gradle b/postssystem.core/build.gradle index 18a32160..41a32731 100644 --- a/postssystem.core/build.gradle +++ b/postssystem.core/build.gradle @@ -38,6 +38,7 @@ kotlin { dependencies { implementation kotlin('stdlib') api "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:$kotlin_coroutines_version" +// api "org.jetbrains.kotlinx:kotlinx-coroutines-io:$kotlin_io_version" api "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$kotlin_serialisation_runtime_version" api "com.soywiz.korlibs.klock:klock:$klockVersion" diff --git a/postssystem.core/gradle.properties b/postssystem.core/gradle.properties new file mode 100644 index 00000000..d2891c13 --- /dev/null +++ b/postssystem.core/gradle.properties @@ -0,0 +1 @@ +kotlin_io_version=0.27.0-eap13 \ No newline at end of file diff --git a/postssystem.core/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/content/Content.kt b/postssystem.core/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/content/Content.kt index f3bacd77..cdc12b04 100644 --- a/postssystem.core/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/content/Content.kt +++ b/postssystem.core/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/content/Content.kt @@ -1,5 +1,7 @@ package com.insanusmokrassar.postssystem.core.content +import com.insanusmokrassar.postssystem.core.utils.ByteArrayAllocator +import com.insanusmokrassar.postssystem.core.utils.ByteArrayAllocatorSerializer import kotlinx.serialization.Serializable typealias ContentId = String @@ -11,15 +13,23 @@ typealias ContentId = String sealed class Content @Serializable -data class SimpleSpecialContent( +data class SpecialContent( val internalId: ContentId ) : Content() @Serializable -data class SimpleTextContent( +data class TextContent( val text: String ) : Content() +@Serializable +data class BinaryContent( + val mimeType: String, + val originalFileName: String, + @Serializable(ByteArrayAllocatorSerializer::class) + val dataAllocator: ByteArrayAllocator +) : Content() + /** * Content which is already registered in database. Using its [id] you can retrieve all known * [com.insanusmokrassar.postssystem.core.post.RegisteredPost]s by using diff --git a/postssystem.core/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/content/api/WriteContentAPI.kt b/postssystem.core/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/content/api/WriteContentAPI.kt index ab9b4d74..4d85020e 100644 --- a/postssystem.core/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/content/api/WriteContentAPI.kt +++ b/postssystem.core/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/content/api/WriteContentAPI.kt @@ -3,6 +3,6 @@ package com.insanusmokrassar.postssystem.core.content.api import com.insanusmokrassar.postssystem.core.content.* interface WriteContentAPI { - suspend fun createContent(content: Content): RegisteredContent? + suspend fun registerContent(content: Content): RegisteredContent? suspend fun deleteContent(id: ContentId): Boolean } diff --git a/postssystem.core/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/utils/ByteArrayAllocator.kt b/postssystem.core/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/utils/ByteArrayAllocator.kt new file mode 100644 index 00000000..69d419d3 --- /dev/null +++ b/postssystem.core/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/utils/ByteArrayAllocator.kt @@ -0,0 +1,20 @@ +package com.insanusmokrassar.postssystem.core.utils + +import kotlinx.serialization.* +import kotlinx.serialization.builtins.* + +typealias ByteArrayAllocator = () -> ByteArray + +object ByteArrayAllocatorSerializer : KSerializer { + private val realSerializer = ByteArraySerializer() + override val descriptor: SerialDescriptor = realSerializer.descriptor + + override fun deserialize(decoder: Decoder): ByteArrayAllocator { + val bytes = decoder.decode(realSerializer) + return { bytes } + } + + override fun serialize(encoder: Encoder, value: ByteArrayAllocator) { + encoder.encodeSerializableValue(realSerializer, value()) + } +} diff --git a/postssystem.core/src/commonTest/kotlin/com/insanusmokrassar/postssystem/core/api/ContentSerialization.kt b/postssystem.core/src/commonTest/kotlin/com/insanusmokrassar/postssystem/core/api/ContentSerialization.kt index 8d03e596..4798c0a9 100644 --- a/postssystem.core/src/commonTest/kotlin/com/insanusmokrassar/postssystem/core/api/ContentSerialization.kt +++ b/postssystem.core/src/commonTest/kotlin/com/insanusmokrassar/postssystem/core/api/ContentSerialization.kt @@ -13,9 +13,9 @@ class ContentSerialization { @Test fun test_that_content_correctly_serializing_and_deserializing() { val contents = (0 until simpleTextTestEntries).map { - SimpleTextContent("Example$it") + TextContent("Example$it") } + (0 until simpleSpecialTestEntries).map { - SimpleSpecialContent("$it") + SpecialContent("$it") } val registeredContentFakes = contents.map { content -> diff --git a/postssystem.core/src/commonTest/kotlin/com/insanusmokrassar/postssystem/core/api/InMemoryContentAPI.kt b/postssystem.core/src/commonTest/kotlin/com/insanusmokrassar/postssystem/core/api/InMemoryContentAPI.kt deleted file mode 100644 index 9b135e41..00000000 --- a/postssystem.core/src/commonTest/kotlin/com/insanusmokrassar/postssystem/core/api/InMemoryContentAPI.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.insanusmokrassar.postssystem.core.api - -import com.insanusmokrassar.postssystem.core.content.* -import com.insanusmokrassar.postssystem.core.content.api.ContentAPI -import com.insanusmokrassar.postssystem.core.utils.generateContentId -import com.insanusmokrassar.postssystem.core.utils.pagination.* -import kotlinx.coroutines.channels.BroadcastChannel -import kotlinx.coroutines.channels.Channel -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.asFlow -import kotlinx.serialization.ImplicitReflectionSerializer - -@ImplicitReflectionSerializer -class InMemoryContentAPI( - initialContent: List = emptyList() -): ContentAPI { - private val contentCreatedBroadcastChannel = BroadcastChannel(Channel.BUFFERED) - private val contentDeletedBroadcastChannel = BroadcastChannel(Channel.BUFFERED) - - override val contentCreatedFlow: Flow - get() = contentCreatedBroadcastChannel.asFlow() - - override val contentDeletedFlow: Flow - get() = contentDeletedBroadcastChannel.asFlow() - - private val contents: MutableMap = initialContent - .associateBy(RegisteredContent::id) - .toMutableMap() - - override suspend fun createContent(content: Content): RegisteredContent? { - return RegisteredContent( - generateContentId(), - content - ).also { registeredContent -> - contents[registeredContent.id] = registeredContent - contentCreatedBroadcastChannel.send(registeredContent) - } - } - - override suspend fun getContentsIds(): Set = contents.keys.toSet() - - override suspend fun getContentById(id: ContentId): RegisteredContent? = contents[id] - - override suspend fun getContentByPagination(pagination: Pagination): PaginationResult { - return contents.values.asSequence().drop(pagination.firstIndex).take(pagination.size).toList().createPaginationResult( - pagination, - commonObjectsNumber = contents.size.toLong() - ) - } - - override suspend fun deleteContent(id: ContentId): Boolean { - return contents.remove(id)?.also { content -> - contentDeletedBroadcastChannel.send(content) - } != null - } -} \ No newline at end of file diff --git a/postssystem.core/src/commonTest/kotlin/com/insanusmokrassar/postssystem/core/api/InMemoryPostsAPI.kt b/postssystem.core/src/commonTest/kotlin/com/insanusmokrassar/postssystem/core/api/InMemoryPostsAPI.kt deleted file mode 100644 index 4d8615f6..00000000 --- a/postssystem.core/src/commonTest/kotlin/com/insanusmokrassar/postssystem/core/api/InMemoryPostsAPI.kt +++ /dev/null @@ -1,86 +0,0 @@ -package com.insanusmokrassar.postssystem.core.api - -import com.insanusmokrassar.postssystem.core.content.ContentId -import com.insanusmokrassar.postssystem.core.post.* -import com.insanusmokrassar.postssystem.core.post.api.PostsAPI -import com.insanusmokrassar.postssystem.core.utils.generatePostId -import com.insanusmokrassar.postssystem.core.utils.pagination.* -import com.soywiz.klock.DateTime -import kotlinx.coroutines.channels.BroadcastChannel -import kotlinx.coroutines.channels.Channel.Factory.BUFFERED -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.asFlow -import kotlinx.serialization.ImplicitReflectionSerializer - -/** - * Thread-unsafe sample realization of [PostsAPI] - */ -@ImplicitReflectionSerializer -class InMemoryPostsAPI( - initialPosts: List = emptyList() -) : PostsAPI { - private val posts: MutableMap = initialPosts.associateBy { it.id }.toMutableMap() - private val sortedByDatePosts: List - get() = posts.values.sortedBy { it.creationDate } - - - private val postCreatedBroadcastChannel = BroadcastChannel(BUFFERED) - override val postCreatedFlow: Flow = postCreatedBroadcastChannel.asFlow() - - private val postDeletedBroadcastChannel = BroadcastChannel(BUFFERED) - override val postDeletedFlow: Flow = postDeletedBroadcastChannel.asFlow() - - private val postUpdatedBroadcastChannel = BroadcastChannel(BUFFERED) - override val postUpdatedFlow: Flow = postUpdatedBroadcastChannel.asFlow() - - - override suspend fun createPost(post: Post): RegisteredPost? { - return SimpleRegisteredPost( - generatePostId(), - post.content - ).also { newPost -> - posts[newPost.id] = newPost - postCreatedBroadcastChannel.send(newPost) - } - } - - override suspend fun deletePost(id: PostId): Boolean { - return posts.remove(id)?.also { - postDeletedBroadcastChannel.send(it) - } != null - } - - override suspend fun updatePostContent(postId: PostId, post: Post): Boolean { - return getPostById(postId)?.also { dbPost -> - val newPost = SimpleRegisteredPost( - dbPost.id, - post.content, - dbPost.creationDate - ) - posts[newPost.id] = newPost - postUpdatedBroadcastChannel.send(newPost) - } != null - } - - override suspend fun getPostsIds(): Set = posts.keys.toSet() - override suspend fun getPostById(id: PostId): RegisteredPost? = posts[id] - override suspend fun getPostsByContent(id: ContentId): List = - posts.values.filter { post -> id in post.content } - - override suspend fun getPostsByCreatingDates(from: DateTime, to: DateTime): List = - (from .. to).let { range -> - posts.values.filter { - it.creationDate in range - } - } - - override suspend fun getPostsByPagination( - pagination: Pagination - ): PaginationResult = sortedByDatePosts.subList( - pagination.firstIndex, - pagination.lastIndex - ).createPaginationResult( - pagination, - commonObjectsNumber = posts.size.toLong() - ) -} diff --git a/postssystem.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/ExposedContentAPI.kt b/postssystem.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/ExposedContentAPI.kt index e3f5044d..3c74c501 100644 --- a/postssystem.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/ExposedContentAPI.kt +++ b/postssystem.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/ExposedContentAPI.kt @@ -2,20 +2,30 @@ package com.insanusmokrassar.postssystem.core.exposed import com.insanusmokrassar.postssystem.core.content.* import com.insanusmokrassar.postssystem.core.content.api.ContentAPI +import com.insanusmokrassar.postssystem.core.exposed.content.* import com.insanusmokrassar.postssystem.core.utils.generateContentId import com.insanusmokrassar.postssystem.core.utils.pagination.* import kotlinx.coroutines.channels.BroadcastChannel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.asFlow -import kotlinx.serialization.json.Json import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.transactions.transaction +private val Content.type + get() = when (this) { + is TextContent -> "text" + is BinaryContent -> "binary" + is SpecialContent -> "special" + } + private class ContentAPIDatabaseTable( - private val database: Database -) : Table("ContentAPI"), ContentAPI { + private val database: Database, + private val textHolder: ContentHolderRepo, + private val binaryHolder: ContentHolderRepo, + private val specialHolder: ContentHolderRepo +) : Table("ContentAPI"), ContentAPI, ContentHolderRepo { internal val idColumn = text("_id") - internal val dataColumn = text("data") + internal val typeColumn = text("type") init { transaction(database) { @@ -28,19 +38,48 @@ private class ContentAPIDatabaseTable( override val contentCreatedFlow: Flow = contentCreatedBroadcastChannel.asFlow() override val contentDeletedFlow: Flow = contentDeletedBroadcastChannel.asFlow() - override suspend fun createContent(content: Content): RegisteredContent? { + private val String.holder + get() = when (this) { + "text" -> textHolder + "binary" -> binaryHolder + "special" -> specialHolder + else -> null + } + + override suspend fun putContent(id: ContentId, content: Content) { + when (content) { + is TextContent -> textHolder.putContent(id, content) + is BinaryContent -> binaryHolder.putContent(id, content) + is SpecialContent -> specialHolder.putContent(id, content) + } + } + override suspend fun getContent(id: ContentId): Content? = transaction(database) { + select { idColumn.eq(id) }.limit(1).firstOrNull() ?.get(typeColumn) + } ?.holder ?.getContent(id) + override suspend fun removeContent(id: ContentId) { + transaction(database) { + select { idColumn.eq(id) }.limit(1).firstOrNull() ?.get(typeColumn) + } ?.holder ?.removeContent(id) + } + + override suspend fun registerContent(content: Content): RegisteredContent? { + val id = generateContentId() + val type = content.type return transaction(database) { insert { - it[idColumn] = generateContentId() - it[dataColumn] = Json.stringify(Content.serializer(), content) - }.getOrNull(idColumn) ?.let { id -> - RegisteredContent( - id, - content - ) - } + it[idColumn] = id + it[typeColumn] = type + }.getOrNull(idColumn) + } ?.let { id -> + putContent(id, content) + RegisteredContent( + id, + content + ) } ?.also { contentCreatedBroadcastChannel.send(it) + } ?: null.also { + removeContent(id) } } override suspend fun deleteContent(id: ContentId): Boolean { @@ -51,15 +90,18 @@ private class ContentAPIDatabaseTable( } > 0 }.also { if (it) { + removeContent(id) contentDeletedBroadcastChannel.send(content) } } } - private fun ResultRow.asRegisteredContent(): RegisteredContent = RegisteredContent( - get(idColumn), - Json.parse(Content.serializer(), get(dataColumn)) - ) + private fun ResultRow.asRegisteredContent(content: Content): RegisteredContent? = get(idColumn).let { + RegisteredContent( + it, + content + ) + } override suspend fun getContentsIds(): Set { return transaction(database) { @@ -67,15 +109,16 @@ private class ContentAPIDatabaseTable( }.toSet() } override suspend fun getContentById(id: ContentId): RegisteredContent? { + val content = getContent(id) ?: return null return transaction(database) { - select { idColumn.eq(id) }.firstOrNull() ?.asRegisteredContent() + select { idColumn.eq(id) }.limit(1).firstOrNull() ?.asRegisteredContent(content) } } override suspend fun getContentByPagination(pagination: Pagination): PaginationResult { return transaction(database) { - selectAll().count() to selectAll().paginate(pagination).map { it.asRegisteredContent() } + selectAll().count() to selectAll().paginate(pagination).map { it[idColumn] } }.let { (count, results) -> - results.createPaginationResult( + results.mapNotNull { RegisteredContent(it, getContent(it) ?: return@mapNotNull null) }.createPaginationResult( pagination, count ) @@ -84,5 +127,8 @@ private class ContentAPIDatabaseTable( } class ExposedContentAPI ( - database: Database -) : ContentAPI by ContentAPIDatabaseTable(database) + database: Database, + textHolder: ContentHolderRepo = TextContentHolderRepo(database), + binaryHolder: ContentHolderRepo = BinaryContentHolderRepo(database), + specialHolder: ContentHolderRepo = SpecialContentHolderRepo(database) +) : ContentAPI by ContentAPIDatabaseTable(database, textHolder, binaryHolder, specialHolder) diff --git a/postssystem.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/content/BinaryContentHolderRepo.kt b/postssystem.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/content/BinaryContentHolderRepo.kt new file mode 100644 index 00000000..85d2646c --- /dev/null +++ b/postssystem.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/content/BinaryContentHolderRepo.kt @@ -0,0 +1,57 @@ +package com.insanusmokrassar.postssystem.core.exposed.content + +import com.insanusmokrassar.postssystem.core.content.* +import org.jetbrains.exposed.sql.* +import org.jetbrains.exposed.sql.statements.api.ExposedBlob +import org.jetbrains.exposed.sql.transactions.transaction + +private class BinaryContentHolderRepoTable( + private val database: Database +) : ContentHolderRepo, Table() { + private val idColumn = text("id") + private val dataColumn = blob("data") + private val mimeColumn = text("mimeType") + private val originalFileNameColumn = text("filename") + override val primaryKey: PrimaryKey = PrimaryKey(idColumn) + + init { + transaction(database) { + SchemaUtils.createMissingTablesAndColumns(this@BinaryContentHolderRepoTable) + } + } + + override suspend fun getContent(id: ContentId): BinaryContent? = transaction(database) { + select { + idColumn.eq(id) + }.limit(1).firstOrNull() ?.let { + val bytes = it[dataColumn].bytes + BinaryContent( + it[mimeColumn], + it[originalFileNameColumn] + ) { + bytes + } + } + } + + override suspend fun removeContent(id: ContentId) { + transaction(database) { + deleteWhere { idColumn.eq(id) } + } + } + + override suspend fun putContent(id: ContentId, content: BinaryContent) { + transaction(database) { + insert { + it[idColumn] = id + it[originalFileNameColumn] = content.originalFileName + it[mimeColumn] = content.mimeType + it[dataColumn] = ExposedBlob(content.dataAllocator()) + } + } + } +} + +class BinaryContentHolderRepo( + database: Database +) : ContentHolderRepo by BinaryContentHolderRepoTable(database) \ No newline at end of file diff --git a/postssystem.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/content/ContentHolderRepo.kt b/postssystem.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/content/ContentHolderRepo.kt new file mode 100644 index 00000000..2e406c48 --- /dev/null +++ b/postssystem.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/content/ContentHolderRepo.kt @@ -0,0 +1,10 @@ +package com.insanusmokrassar.postssystem.core.exposed.content + +import com.insanusmokrassar.postssystem.core.content.Content +import com.insanusmokrassar.postssystem.core.content.ContentId + +interface ContentHolderRepo { + suspend fun getContent(id: ContentId) : T? + suspend fun removeContent(id: ContentId) + suspend fun putContent(id: ContentId, content: T) +} diff --git a/postssystem.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/content/SpecialContentHolderRepo.kt b/postssystem.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/content/SpecialContentHolderRepo.kt new file mode 100644 index 00000000..ceb4eb1b --- /dev/null +++ b/postssystem.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/content/SpecialContentHolderRepo.kt @@ -0,0 +1,46 @@ +package com.insanusmokrassar.postssystem.core.exposed.content + +import com.insanusmokrassar.postssystem.core.content.* +import org.jetbrains.exposed.sql.* +import org.jetbrains.exposed.sql.transactions.transaction + +private class SpecialContentHolderRepoTable( + private val database: Database +) : ContentHolderRepo, Table() { + private val idColumn = text("id") + private val internalIdColumn = text("internalId") + override val primaryKey: PrimaryKey = PrimaryKey(idColumn) + + init { + transaction(database) { + SchemaUtils.createMissingTablesAndColumns(this@SpecialContentHolderRepoTable) + } + } + + override suspend fun getContent(id: ContentId): SpecialContent? = transaction(database) { + select { + idColumn.eq(id) + }.limit(1).firstOrNull() ?.get(internalIdColumn) ?.let { + SpecialContent(it) + } + } + + override suspend fun removeContent(id: ContentId) { + transaction(database) { + deleteWhere { idColumn.eq(id) } + } + } + + override suspend fun putContent(id: ContentId, content: SpecialContent) { + transaction(database) { + insert { + it[idColumn] = id + it[internalIdColumn] = content.internalId + } + } + } +} + +class SpecialContentHolderRepo( + database: Database +) : ContentHolderRepo by SpecialContentHolderRepoTable(database) \ No newline at end of file diff --git a/postssystem.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/content/TextContentHolderRepo.kt b/postssystem.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/content/TextContentHolderRepo.kt new file mode 100644 index 00000000..8ae5d8bb --- /dev/null +++ b/postssystem.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/content/TextContentHolderRepo.kt @@ -0,0 +1,47 @@ +package com.insanusmokrassar.postssystem.core.exposed.content + +import com.insanusmokrassar.postssystem.core.content.ContentId +import com.insanusmokrassar.postssystem.core.content.TextContent +import org.jetbrains.exposed.sql.* +import org.jetbrains.exposed.sql.transactions.transaction + +private class TextContentHolderRepoTable( + private val database: Database +) : ContentHolderRepo, Table() { + private val idColumn = text("id") + private val textColumn = text("text") + override val primaryKey: PrimaryKey = PrimaryKey(idColumn) + + init { + transaction(database) { + SchemaUtils.createMissingTablesAndColumns(this@TextContentHolderRepoTable) + } + } + + override suspend fun getContent(id: ContentId): TextContent? = transaction(database) { + select { + idColumn.eq(id) + }.limit(1).firstOrNull() ?.get(textColumn) ?.let { + TextContent(it) + } + } + + override suspend fun removeContent(id: ContentId) { + transaction(database) { + deleteWhere { idColumn.eq(id) } + } + } + + override suspend fun putContent(id: ContentId, content: TextContent) { + transaction(database) { + insert { + it[idColumn] = id + it[textColumn] = content.text + } + } + } +} + +class TextContentHolderRepo( + database: Database +) : ContentHolderRepo by TextContentHolderRepoTable(database) \ No newline at end of file diff --git a/postssystem.exposed/src/test/kotlin/com/insanusmokrassar/postssystem/core/exposed/ExposedContentAPICommonTests.kt b/postssystem.exposed/src/test/kotlin/com/insanusmokrassar/postssystem/core/exposed/ExposedContentAPICommonTests.kt index 23feaae8..d579f6f1 100644 --- a/postssystem.exposed/src/test/kotlin/com/insanusmokrassar/postssystem/core/exposed/ExposedContentAPICommonTests.kt +++ b/postssystem.exposed/src/test/kotlin/com/insanusmokrassar/postssystem/core/exposed/ExposedContentAPICommonTests.kt @@ -1,12 +1,13 @@ package com.insanusmokrassar.postssystem.core.exposed -import com.insanusmokrassar.postssystem.core.content.SimpleTextContent +import com.insanusmokrassar.postssystem.core.content.TextContent import kotlinx.coroutines.runBlocking import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.transactions.transactionManager import java.io.File import java.sql.Connection import kotlin.test.Test +import kotlin.test.assertEquals class ExposedContentAPICommonTests { private val tempFolder = System.getProperty("java.io.tmpdir")!! @@ -20,6 +21,10 @@ class ExposedContentAPICommonTests { } val apis = databaseFiles.map { + File(it).also { + it.delete() + it.deleteOnExit() + } ExposedContentAPI( Database.Companion.connect("jdbc:sqlite:$it", driver = "org.sqlite.JDBC").also { it.transactionManager.defaultIsolationLevel = Connection.TRANSACTION_SERIALIZABLE @@ -28,9 +33,10 @@ class ExposedContentAPICommonTests { } val results = apis.mapIndexed { i, api -> - val content = runBlocking { api.createContent(SimpleTextContent(i.toString())) } + val content = runBlocking { api.registerContent(TextContent(i.toString())) } assert(content != null) - assert(runBlocking { api.getContentsIds().size == 1 }) + val ids = runBlocking { api.getContentsIds() } + assertEquals(ids.size, 1) content!! }