diff --git a/postssystem.core.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/ExposedContentAPI.kt b/postssystem.core.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/ExposedContentRepo.kt similarity index 81% rename from postssystem.core.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/ExposedContentAPI.kt rename to postssystem.core.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/ExposedContentRepo.kt index 1bc3e0d8..b6a3b873 100644 --- a/postssystem.core.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/ExposedContentAPI.kt +++ b/postssystem.core.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/ExposedContentRepo.kt @@ -1,15 +1,17 @@ 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.content.api.ContentRepo import com.insanusmokrassar.postssystem.core.exposed.content.* import com.insanusmokrassar.postssystem.core.utils.generateContentId import com.insanusmokrassar.postssystem.core.utils.pagination.* +import com.insanusmokrassar.postssystem.exposed.commons.paginate 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.experimental.newSuspendedTransaction import org.jetbrains.exposed.sql.transactions.transaction private val Content.type @@ -19,18 +21,20 @@ private val Content.type is SpecialContent -> "special" } -private class ContentAPIDatabaseTable( +private class ContentRepoDatabaseTable( private val database: Database, private val textHolder: ContentHolderRepo, private val binaryHolder: ContentHolderRepo, private val specialHolder: ContentHolderRepo -) : Table("ContentAPI"), ContentAPI, ContentHolderRepo { +) : Table("ContentRepo"), ContentRepo, ContentHolderRepo { internal val idColumn = text("_id") internal val typeColumn = text("type") init { - transaction(database) { - SchemaUtils.createMissingTablesAndColumns(this@ContentAPIDatabaseTable) + transaction( + db = database + ) { + SchemaUtils.createMissingTablesAndColumns(this@ContentRepoDatabaseTable) } } @@ -54,11 +58,16 @@ private class ContentAPIDatabaseTable( is SpecialContent -> specialHolder.putContent(id, content) } } - override suspend fun getContent(id: ContentId): Content? = transaction(database) { + override suspend fun getContent(id: ContentId): Content? = newSuspendedTransaction( + db = database + ) { select { idColumn.eq(id) }.limit(1).firstOrNull() ?.get(typeColumn) } ?.holder ?.getContent(id) + override suspend fun removeContent(id: ContentId) { - transaction(database) { + newSuspendedTransaction( + db = database + ) { select { idColumn.eq(id) }.limit(1).firstOrNull() ?.get(typeColumn) } ?.holder ?.removeContent(id) } @@ -66,7 +75,9 @@ private class ContentAPIDatabaseTable( override suspend fun registerContent(content: Content): RegisteredContent? { val id = generateContentId() val type = content.type - return transaction(database) { + return newSuspendedTransaction( + db = database + ) { insert { it[idColumn] = id it[typeColumn] = type @@ -85,7 +96,9 @@ private class ContentAPIDatabaseTable( } override suspend fun deleteContent(id: ContentId): Boolean { val content = getContentById(id) ?: return false - return transaction(database) { + return newSuspendedTransaction( + db = database + ) { deleteWhere { idColumn.eq(id) } > 0 @@ -105,18 +118,24 @@ private class ContentAPIDatabaseTable( } override suspend fun getContentsIds(): Set { - return transaction(database) { + return newSuspendedTransaction( + db = database + ) { selectAll().map { it[idColumn] } }.toSet() } override suspend fun getContentById(id: ContentId): RegisteredContent? { val content = getContent(id) ?: return null - return transaction(database) { + return newSuspendedTransaction( + db = database + ) { select { idColumn.eq(id) }.limit(1).firstOrNull() ?.asRegisteredContent(content) } } override suspend fun getContentByPagination(pagination: Pagination): PaginationResult { - return transaction(database) { + return newSuspendedTransaction( + db = database + ) { selectAll().count() to selectAll().paginate(pagination).map { it[idColumn] } }.let { (count, results) -> results.mapNotNull { RegisteredContent(it, getContent(it) ?: return@mapNotNull null) }.createPaginationResult( @@ -127,9 +146,9 @@ private class ContentAPIDatabaseTable( } } -class ExposedContentAPI ( +class ExposedContentRepo ( database: Database, textHolder: ContentHolderRepo = TextContentHolderRepo(database), binaryHolder: ContentHolderRepo = BinaryContentHolderRepo(database), specialHolder: ContentHolderRepo = SpecialContentHolderRepo(database) -) : ContentAPI by ContentAPIDatabaseTable(database, textHolder, binaryHolder, specialHolder) +) : ContentRepo by ContentRepoDatabaseTable(database, textHolder, binaryHolder, specialHolder) diff --git a/postssystem.core.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/ExposedPostsRepo.kt b/postssystem.core.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/ExposedPostsRepo.kt index 8377ec00..68f717d8 100644 --- a/postssystem.core.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/ExposedPostsRepo.kt +++ b/postssystem.core.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/ExposedPostsRepo.kt @@ -5,40 +5,50 @@ import com.insanusmokrassar.postssystem.core.post.* import com.insanusmokrassar.postssystem.core.post.repo.PostsRepo import com.insanusmokrassar.postssystem.core.utils.generatePostId import com.insanusmokrassar.postssystem.core.utils.pagination.* +import com.insanusmokrassar.postssystem.exposed.commons.paginate import com.soywiz.klock.* 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.experimental.newSuspendedTransaction import org.jetbrains.exposed.sql.transactions.transaction -private class PostsAPIContentRelations( +private class PostsRepoContentRelations( private val database: Database ) : Table() { private val postIdColumn = text("postId") private val contentIdColumn = text("contentId") init { - transaction(database) { - SchemaUtils.createMissingTablesAndColumns(this@PostsAPIContentRelations) + transaction ( + db = database + ) { + SchemaUtils.createMissingTablesAndColumns(this@PostsRepoContentRelations) } } - fun getPostContents(postId: PostId): List { - return transaction(database) { + suspend fun getPostContents(postId: PostId): List { + return newSuspendedTransaction( + db = database + ) { select { postIdColumn.eq(postId) }.map { it[contentIdColumn] } } } - fun getContentPosts(contentId: ContentId): List { - return transaction(database) { + suspend fun getContentPosts(contentId: ContentId): List { + return newSuspendedTransaction( + db = database + ) { select { contentIdColumn.eq(contentId) }.map { it[postIdColumn] } } } - fun linkPostAndContents(postId: PostId, vararg contentIds: ContentId) { - transaction(database) { + suspend fun linkPostAndContents(postId: PostId, vararg contentIds: ContentId) { + newSuspendedTransaction( + db = database + ) { val leftToPut = contentIds.toSet() - getPostContents(postId) leftToPut.forEach { contentId -> insert { @@ -48,8 +58,10 @@ private class PostsAPIContentRelations( } } } - fun unlinkPostAndContents(postId: PostId, vararg contentIds: ContentId): Boolean { - return transaction(database) { + suspend fun unlinkPostAndContents(postId: PostId, vararg contentIds: ContentId): Boolean { + return newSuspendedTransaction( + db = database + ) { deleteWhere { postIdColumn.eq(postId).and(contentIdColumn.inList(contentIds.toList())) } > 0 @@ -62,7 +74,7 @@ private val dateTimeFormat = DateFormat("EEE, dd MMM yyyy HH:mm:ss z") private class PostsRepoDatabaseTable( private val database: Database ) : PostsRepo, Table() { - private val contentsTable = PostsAPIContentRelations(database) + private val contentsTable = PostsRepoContentRelations(database) private val idColumn = text("postId") private val creationDateColumn = text("creationDate").default( @@ -80,12 +92,14 @@ private class PostsRepoDatabaseTable( override val postUpdatedFlow: Flow = postUpdatedBroadcastChannel.asFlow() init { - transaction(database) { + transaction ( + db = database + ) { SchemaUtils.createMissingTablesAndColumns(this@PostsRepoDatabaseTable) } } - private fun ResultRow.toRegisteredPost(): RegisteredPost = get(idColumn).let { id -> + private suspend fun ResultRow.toRegisteredPost(): RegisteredPost = get(idColumn).let { id -> SimpleRegisteredPost( id, contentsTable.getPostContents(id), @@ -95,7 +109,9 @@ private class PostsRepoDatabaseTable( override suspend fun createPost(post: Post): RegisteredPost? { val id = generatePostId() - return transaction(database) { + return newSuspendedTransaction( + db = database + ) { insert { it[idColumn] = id } @@ -108,7 +124,9 @@ private class PostsRepoDatabaseTable( override suspend fun deletePost(id: PostId): Boolean { val post = getPostById(id) ?: return false - return (transaction(database) { + return (newSuspendedTransaction( + db = database + ) { deleteWhere { idColumn.eq(id) } } > 0).also { if (it) { @@ -119,7 +137,9 @@ private class PostsRepoDatabaseTable( } override suspend fun updatePostContent(postId: PostId, post: Post): Boolean { - return transaction(database) { + return newSuspendedTransaction( + db = database + ) { val alreadyLinked = contentsTable.getPostContents(postId) val toRemove = alreadyLinked - post.content val toInsert = post.content - alreadyLinked @@ -135,32 +155,42 @@ private class PostsRepoDatabaseTable( } } override suspend fun getPostsIds(): Set { - return transaction(database) { + return newSuspendedTransaction( + db = database + ) { selectAll().map { it[idColumn] }.toSet() } } override suspend fun getPostById(id: PostId): RegisteredPost? { - return transaction(database) { + return newSuspendedTransaction( + db = database + ) { select { idColumn.eq(id) }.firstOrNull() ?.toRegisteredPost() } } override suspend fun getPostsByContent(id: ContentId): List { - return transaction(database) { + return newSuspendedTransaction( + db = database + ) { val postsIds = contentsTable.getContentPosts(id) select { idColumn.inList(postsIds) }.map { it.toRegisteredPost() } } } override suspend fun getPostsByCreatingDates(from: DateTime, to: DateTime): List { - return transaction(database) { + return newSuspendedTransaction( + db = database + ) { select { creationDateColumn.between(from, to) }.map { it.toRegisteredPost() } } } override suspend fun getPostsByPagination(pagination: Pagination): PaginationResult { - return transaction(database) { + return newSuspendedTransaction( + db = database + ) { val posts = selectAll().paginate(pagination).orderBy(creationDateColumn).map { it.toRegisteredPost() } diff --git a/postssystem.core.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/QueryExtensions.kt b/postssystem.core.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/QueryExtensions.kt deleted file mode 100644 index dfc320c7..00000000 --- a/postssystem.core.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/QueryExtensions.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.insanusmokrassar.postssystem.core.exposed - -import com.insanusmokrassar.postssystem.core.utils.pagination.Pagination -import com.insanusmokrassar.postssystem.core.utils.pagination.firstIndex -import org.jetbrains.exposed.sql.Query - -fun Query.paginate(pagination: Pagination) = limit(pagination.size, pagination.firstIndex.toLong()) diff --git a/postssystem.core.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/content/BinaryContentHolderRepo.kt b/postssystem.core.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/content/BinaryContentHolderRepo.kt index e6ac3bb7..8fff94ea 100644 --- a/postssystem.core.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/content/BinaryContentHolderRepo.kt +++ b/postssystem.core.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/content/BinaryContentHolderRepo.kt @@ -4,6 +4,7 @@ import com.insanusmokrassar.postssystem.core.content.BinaryContent import com.insanusmokrassar.postssystem.core.content.ContentId import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.statements.api.ExposedBlob +import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction import org.jetbrains.exposed.sql.transactions.transaction private class BinaryContentHolderRepoTable( @@ -16,12 +17,16 @@ private class BinaryContentHolderRepoTable( override val primaryKey: PrimaryKey = PrimaryKey(idColumn) init { - transaction(database) { + transaction ( + db = database + ) { SchemaUtils.createMissingTablesAndColumns(this@BinaryContentHolderRepoTable) } } - override suspend fun getContent(id: ContentId): BinaryContent? = transaction(database) { + override suspend fun getContent(id: ContentId): BinaryContent? = newSuspendedTransaction ( + db = database + ) { select { idColumn.eq(id) }.limit(1).firstOrNull() ?.let { @@ -36,13 +41,17 @@ private class BinaryContentHolderRepoTable( } override suspend fun removeContent(id: ContentId) { - transaction(database) { + newSuspendedTransaction( + db = database + ) { deleteWhere { idColumn.eq(id) } } } override suspend fun putContent(id: ContentId, content: BinaryContent) { - transaction(database) { + newSuspendedTransaction( + db = database + ) { insert { it[idColumn] = id it[originalFileNameColumn] = content.originalFileName diff --git a/postssystem.core.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/content/SpecialContentHolderRepo.kt b/postssystem.core.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/content/SpecialContentHolderRepo.kt index b5821e84..f15eeb62 100644 --- a/postssystem.core.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/content/SpecialContentHolderRepo.kt +++ b/postssystem.core.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/content/SpecialContentHolderRepo.kt @@ -3,6 +3,7 @@ package com.insanusmokrassar.postssystem.core.exposed.content import com.insanusmokrassar.postssystem.core.content.ContentId import com.insanusmokrassar.postssystem.core.content.SpecialContent import org.jetbrains.exposed.sql.* +import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction import org.jetbrains.exposed.sql.transactions.transaction private class SpecialContentHolderRepoTable( @@ -13,12 +14,16 @@ private class SpecialContentHolderRepoTable( override val primaryKey: PrimaryKey = PrimaryKey(idColumn) init { - transaction(database) { + transaction( + db = database + ) { SchemaUtils.createMissingTablesAndColumns(this@SpecialContentHolderRepoTable) } } - override suspend fun getContent(id: ContentId): SpecialContent? = transaction(database) { + override suspend fun getContent(id: ContentId): SpecialContent? = newSuspendedTransaction( + db = database + ) { select { idColumn.eq(id) }.limit(1).firstOrNull() ?.get(internalIdColumn) ?.let { @@ -27,13 +32,17 @@ private class SpecialContentHolderRepoTable( } override suspend fun removeContent(id: ContentId) { - transaction(database) { + newSuspendedTransaction( + db = database + ) { deleteWhere { idColumn.eq(id) } } } override suspend fun putContent(id: ContentId, content: SpecialContent) { - transaction(database) { + newSuspendedTransaction( + db = database + ) { insert { it[idColumn] = id it[internalIdColumn] = content.internalId diff --git a/postssystem.core.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/content/TextContentHolderRepo.kt b/postssystem.core.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/content/TextContentHolderRepo.kt index 8ae5d8bb..f2d395aa 100644 --- a/postssystem.core.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/content/TextContentHolderRepo.kt +++ b/postssystem.core.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/content/TextContentHolderRepo.kt @@ -3,6 +3,7 @@ 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.experimental.newSuspendedTransaction import org.jetbrains.exposed.sql.transactions.transaction private class TextContentHolderRepoTable( @@ -13,12 +14,16 @@ private class TextContentHolderRepoTable( override val primaryKey: PrimaryKey = PrimaryKey(idColumn) init { - transaction(database) { + transaction( + db = database + ) { SchemaUtils.createMissingTablesAndColumns(this@TextContentHolderRepoTable) } } - override suspend fun getContent(id: ContentId): TextContent? = transaction(database) { + override suspend fun getContent(id: ContentId): TextContent? = newSuspendedTransaction( + db = database + ) { select { idColumn.eq(id) }.limit(1).firstOrNull() ?.get(textColumn) ?.let { @@ -27,13 +32,17 @@ private class TextContentHolderRepoTable( } override suspend fun removeContent(id: ContentId) { - transaction(database) { + newSuspendedTransaction( + db = database + ) { deleteWhere { idColumn.eq(id) } } } override suspend fun putContent(id: ContentId, content: TextContent) { - transaction(database) { + newSuspendedTransaction( + db = database + ) { insert { it[idColumn] = id it[textColumn] = content.text diff --git a/postssystem.core.exposed/src/test/kotlin/com/insanusmokrassar/postssystem/core/exposed/ExposedContentAPICommonTests.kt b/postssystem.core.exposed/src/test/kotlin/com/insanusmokrassar/postssystem/core/exposed/ExposedContentAPICommonTests.kt index d579f6f1..fc55964d 100644 --- a/postssystem.core.exposed/src/test/kotlin/com/insanusmokrassar/postssystem/core/exposed/ExposedContentAPICommonTests.kt +++ b/postssystem.core.exposed/src/test/kotlin/com/insanusmokrassar/postssystem/core/exposed/ExposedContentAPICommonTests.kt @@ -9,7 +9,7 @@ import java.sql.Connection import kotlin.test.Test import kotlin.test.assertEquals -class ExposedContentAPICommonTests { +class ExposedContentRepoCommonTests { private val tempFolder = System.getProperty("java.io.tmpdir")!! @Test @@ -25,7 +25,7 @@ class ExposedContentAPICommonTests { it.delete() it.deleteOnExit() } - ExposedContentAPI( + ExposedContentRepo( Database.Companion.connect("jdbc:sqlite:$it", driver = "org.sqlite.JDBC").also { it.transactionManager.defaultIsolationLevel = Connection.TRANSACTION_SERIALIZABLE } diff --git a/postssystem.core.publishing.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/publishing/exposed/ExposedPublishingKeysRepo.kt b/postssystem.core.publishing.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/publishing/exposed/ExposedPublishingKeysRepo.kt index 57de6726..dfe4e6a2 100644 --- a/postssystem.core.publishing.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/publishing/exposed/ExposedPublishingKeysRepo.kt +++ b/postssystem.core.publishing.exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/publishing/exposed/ExposedPublishingKeysRepo.kt @@ -15,7 +15,9 @@ class ExposedPublishingKeysRepo( override val primaryKey: PrimaryKey = PrimaryKey(postIdColumn, triggerControlKeyColumn) init { - transaction(database) { + newSuspendedTransaction( + db = database + ) { SchemaUtils.createMissingTablesAndColumns(this@ExposedPublishingKeysRepo) } } diff --git a/postssystem.core/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/content/api/ContentAPI.kt b/postssystem.core/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/content/api/ContentRepo.kt similarity index 81% rename from postssystem.core/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/content/api/ContentAPI.kt rename to postssystem.core/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/content/api/ContentRepo.kt index 2c970c2e..fd9bfbd5 100644 --- a/postssystem.core/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/content/api/ContentAPI.kt +++ b/postssystem.core/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/content/api/ContentRepo.kt @@ -3,7 +3,7 @@ package com.insanusmokrassar.postssystem.core.content.api import com.insanusmokrassar.postssystem.core.content.RegisteredContent import kotlinx.coroutines.flow.Flow -interface ContentAPI : ReadContentAPI, WriteContentAPI { +interface ContentRepo : ReadContentRepo, WriteContentRepo { val contentCreatedFlow: Flow val contentDeletedFlow: Flow } diff --git a/postssystem.core/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/content/api/ReadContentAPI.kt b/postssystem.core/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/content/api/ReadContentRepo.kt similarity index 97% rename from postssystem.core/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/content/api/ReadContentAPI.kt rename to postssystem.core/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/content/api/ReadContentRepo.kt index 7219b59b..199ba2dc 100644 --- a/postssystem.core/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/content/api/ReadContentAPI.kt +++ b/postssystem.core/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/content/api/ReadContentRepo.kt @@ -8,7 +8,7 @@ import com.insanusmokrassar.postssystem.core.utils.pagination.PaginationResult /** * Simple read API by different properties of [com.insanusmokrassar.postssystem.core.content.Content]. */ -interface ReadContentAPI { +interface ReadContentRepo { /** * @return [Set] of [ContentId] wich currently known in this instance of API */ 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/WriteContentRepo.kt similarity index 89% rename from postssystem.core/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/content/api/WriteContentAPI.kt rename to postssystem.core/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/content/api/WriteContentRepo.kt index 4d85020e..8f5e995a 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/WriteContentRepo.kt @@ -2,7 +2,7 @@ package com.insanusmokrassar.postssystem.core.content.api import com.insanusmokrassar.postssystem.core.content.* -interface WriteContentAPI { +interface WriteContentRepo { suspend fun registerContent(content: Content): RegisteredContent? suspend fun deleteContent(id: ContentId): Boolean }