From bae474d314124de8f8092bf47e144694069e1a21 Mon Sep 17 00:00:00 2001 From: NathanFallet Date: Tue, 5 Mar 2024 21:08:22 +0100 Subject: [PATCH] feat: search posts and users (server side) --- .../controllers/posts/IPostsController.kt | 10 +++++ .../controllers/posts/PostsController.kt | 15 +++++++ .../controllers/users/IUsersController.kt | 10 +++++ .../controllers/users/UsersController.kt | 15 +++++++ .../database/posts/PostsDatabaseRepository.kt | 30 +++++++++++++ .../database/users/UsersDatabaseRepository.kt | 33 +++++++++++++++ .../me/nathanfallet/extopy/plugins/Koin.kt | 10 +++++ .../controllers/posts/PostsControllerTest.kt | 42 ++++++++++++------- .../controllers/users/UsersControllerTest.kt | 8 ++++ .../models/application/SearchOptions.kt | 9 ++++ 10 files changed, 167 insertions(+), 15 deletions(-) create mode 100644 extopy-commons/src/commonMain/kotlin/me/nathanfallet/extopy/models/application/SearchOptions.kt diff --git a/extopy-backend/src/commonMain/kotlin/me/nathanfallet/extopy/controllers/posts/IPostsController.kt b/extopy-backend/src/commonMain/kotlin/me/nathanfallet/extopy/controllers/posts/IPostsController.kt index 292c78d..100d6e3 100644 --- a/extopy-backend/src/commonMain/kotlin/me/nathanfallet/extopy/controllers/posts/IPostsController.kt +++ b/extopy-backend/src/commonMain/kotlin/me/nathanfallet/extopy/controllers/posts/IPostsController.kt @@ -8,6 +8,16 @@ import me.nathanfallet.ktorx.models.annotations.* interface IPostsController : IModelController { + @APIMapping + @ListModelPath + @DocumentedError(401, "auth_invalid_credentials") + suspend fun list( + call: ApplicationCall, + @QueryParameter limit: Long?, + @QueryParameter offset: Long?, + @QueryParameter search: String?, + ): List + @APIMapping @GetModelPath @DocumentedError(401, "auth_invalid_credentials") diff --git a/extopy-backend/src/commonMain/kotlin/me/nathanfallet/extopy/controllers/posts/PostsController.kt b/extopy-backend/src/commonMain/kotlin/me/nathanfallet/extopy/controllers/posts/PostsController.kt index 1d09aa5..d9183eb 100644 --- a/extopy-backend/src/commonMain/kotlin/me/nathanfallet/extopy/controllers/posts/PostsController.kt +++ b/extopy-backend/src/commonMain/kotlin/me/nathanfallet/extopy/controllers/posts/PostsController.kt @@ -2,6 +2,7 @@ package me.nathanfallet.extopy.controllers.posts import io.ktor.http.* import io.ktor.server.application.* +import me.nathanfallet.extopy.models.application.SearchOptions import me.nathanfallet.extopy.models.posts.Post import me.nathanfallet.extopy.models.posts.PostPayload import me.nathanfallet.extopy.models.users.User @@ -12,18 +13,32 @@ import me.nathanfallet.ktorx.usecases.users.IRequireUserForCallUseCase import me.nathanfallet.usecases.models.create.context.ICreateModelWithContextSuspendUseCase import me.nathanfallet.usecases.models.delete.IDeleteModelSuspendUseCase import me.nathanfallet.usecases.models.get.context.IGetModelWithContextSuspendUseCase +import me.nathanfallet.usecases.models.list.slice.context.IListSliceModelWithContextSuspendUseCase import me.nathanfallet.usecases.models.update.IUpdateModelSuspendUseCase import me.nathanfallet.usecases.pagination.Pagination class PostsController( private val requireUserForCallUseCase: IRequireUserForCallUseCase, private val createPostUseCase: ICreateModelWithContextSuspendUseCase, + private val listPostsUseCase: IListSliceModelWithContextSuspendUseCase, private val getPostUseCase: IGetModelWithContextSuspendUseCase, private val updatePostUseCase: IUpdateModelSuspendUseCase, private val deletePostUseCase: IDeleteModelSuspendUseCase, private val getPostRepliesUseCase: IGetPostRepliesUseCase, ) : IPostsController { + override suspend fun list(call: ApplicationCall, limit: Long?, offset: Long?, search: String?): List { + val user = requireUserForCallUseCase(call) as User + return listPostsUseCase( + Pagination( + limit ?: 25, + offset ?: 0, + search?.let { SearchOptions(it) } + ), + UserContext(user.id) + ) + } + override suspend fun create(call: ApplicationCall, payload: PostPayload): Post { val user = requireUserForCallUseCase(call) as User return createPostUseCase(payload, UserContext(user.id)) ?: throw ControllerException( diff --git a/extopy-backend/src/commonMain/kotlin/me/nathanfallet/extopy/controllers/users/IUsersController.kt b/extopy-backend/src/commonMain/kotlin/me/nathanfallet/extopy/controllers/users/IUsersController.kt index 4b21bdf..03939f7 100644 --- a/extopy-backend/src/commonMain/kotlin/me/nathanfallet/extopy/controllers/users/IUsersController.kt +++ b/extopy-backend/src/commonMain/kotlin/me/nathanfallet/extopy/controllers/users/IUsersController.kt @@ -10,6 +10,16 @@ import me.nathanfallet.ktorx.models.annotations.* interface IUsersController : IModelController { + @APIMapping + @ListModelPath + @DocumentedError(401, "auth_invalid_credentials") + suspend fun list( + call: ApplicationCall, + @QueryParameter limit: Long?, + @QueryParameter offset: Long?, + @QueryParameter search: String?, + ): List + @APIMapping @GetModelPath @DocumentedError(401, "auth_invalid_credentials") diff --git a/extopy-backend/src/commonMain/kotlin/me/nathanfallet/extopy/controllers/users/UsersController.kt b/extopy-backend/src/commonMain/kotlin/me/nathanfallet/extopy/controllers/users/UsersController.kt index a81d542..d97bc01 100644 --- a/extopy-backend/src/commonMain/kotlin/me/nathanfallet/extopy/controllers/users/UsersController.kt +++ b/extopy-backend/src/commonMain/kotlin/me/nathanfallet/extopy/controllers/users/UsersController.kt @@ -2,6 +2,7 @@ package me.nathanfallet.extopy.controllers.users import io.ktor.http.* import io.ktor.server.application.* +import me.nathanfallet.extopy.models.application.SearchOptions import me.nathanfallet.extopy.models.posts.Post import me.nathanfallet.extopy.models.users.UpdateUserPayload import me.nathanfallet.extopy.models.users.User @@ -10,16 +11,30 @@ import me.nathanfallet.extopy.usecases.users.IGetUserPostsUseCase import me.nathanfallet.ktorx.models.exceptions.ControllerException import me.nathanfallet.ktorx.usecases.users.IRequireUserForCallUseCase import me.nathanfallet.usecases.models.get.context.IGetModelWithContextSuspendUseCase +import me.nathanfallet.usecases.models.list.slice.context.IListSliceModelWithContextSuspendUseCase import me.nathanfallet.usecases.models.update.IUpdateModelSuspendUseCase import me.nathanfallet.usecases.pagination.Pagination class UsersController( private val requireUserForCallUseCase: IRequireUserForCallUseCase, + private val listUsersUseCase: IListSliceModelWithContextSuspendUseCase, private val getUserUseCase: IGetModelWithContextSuspendUseCase, private val updateUserUseCase: IUpdateModelSuspendUseCase, private val getUserPostsUseCase: IGetUserPostsUseCase, ) : IUsersController { + override suspend fun list(call: ApplicationCall, limit: Long?, offset: Long?, search: String?): List { + val user = requireUserForCallUseCase(call) as User + return listUsersUseCase( + Pagination( + limit ?: 25, + offset ?: 0, + search?.let { SearchOptions(it) } + ), + UserContext(user.id) + ) + } + override suspend fun get(call: ApplicationCall, id: String): User { val user = requireUserForCallUseCase(call) as User return getUserUseCase(id, UserContext(user.id)) ?: throw ControllerException( diff --git a/extopy-backend/src/commonMain/kotlin/me/nathanfallet/extopy/database/posts/PostsDatabaseRepository.kt b/extopy-backend/src/commonMain/kotlin/me/nathanfallet/extopy/database/posts/PostsDatabaseRepository.kt index 82248c1..8881a16 100644 --- a/extopy-backend/src/commonMain/kotlin/me/nathanfallet/extopy/database/posts/PostsDatabaseRepository.kt +++ b/extopy-backend/src/commonMain/kotlin/me/nathanfallet/extopy/database/posts/PostsDatabaseRepository.kt @@ -3,12 +3,14 @@ package me.nathanfallet.extopy.database.posts import kotlinx.datetime.Clock import me.nathanfallet.extopy.database.users.FollowersInUsers import me.nathanfallet.extopy.database.users.Users +import me.nathanfallet.extopy.models.application.SearchOptions import me.nathanfallet.extopy.models.posts.Post import me.nathanfallet.extopy.models.posts.PostPayload import me.nathanfallet.extopy.models.users.UserContext import me.nathanfallet.extopy.repositories.posts.IPostsRepository import me.nathanfallet.surexposed.database.IDatabase import me.nathanfallet.usecases.context.IContext +import me.nathanfallet.usecases.pagination.IPaginationOptions import me.nathanfallet.usecases.pagination.Pagination import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq @@ -23,6 +25,18 @@ class PostsDatabaseRepository( } } + override suspend fun list(pagination: Pagination, context: IContext?): List { + if (context !is UserContext) return emptyList() + return database.suspendedTransaction { + customJoin(context.userId) + .groupBy(Posts.id) + .andWhere(pagination.options) + .orderBy(Posts.published to SortOrder.DESC) + .limit(pagination.limit.toInt(), pagination.offset) + .map { Posts.toPost(it, Users.toUser(it)) } + } + } + override suspend fun listDefault(pagination: Pagination, context: UserContext): List = database.suspendedTransaction { customJoinColumnSet(context.userId) @@ -149,4 +163,20 @@ class PostsDatabaseRepository( additionalFields ) + private fun Query.andWhere(options: IPaginationOptions?): Query = when (options) { + is SearchOptions -> { + val likeString = options.search + .replace("%", "\\%") + .replace("_", "\\_") + andWhere { + likeString.split(" ") + .map { Posts.body like "%$it%" } + .fold(Op.FALSE as Op) { a, b -> a or b } + } + + } + + else -> this + } + } diff --git a/extopy-backend/src/commonMain/kotlin/me/nathanfallet/extopy/database/users/UsersDatabaseRepository.kt b/extopy-backend/src/commonMain/kotlin/me/nathanfallet/extopy/database/users/UsersDatabaseRepository.kt index a42cd6a..c7c67b0 100644 --- a/extopy-backend/src/commonMain/kotlin/me/nathanfallet/extopy/database/users/UsersDatabaseRepository.kt +++ b/extopy-backend/src/commonMain/kotlin/me/nathanfallet/extopy/database/users/UsersDatabaseRepository.kt @@ -2,6 +2,7 @@ package me.nathanfallet.extopy.database.users import kotlinx.datetime.Clock import me.nathanfallet.extopy.database.posts.Posts +import me.nathanfallet.extopy.models.application.SearchOptions import me.nathanfallet.extopy.models.users.CreateUserPayload import me.nathanfallet.extopy.models.users.UpdateUserPayload import me.nathanfallet.extopy.models.users.User @@ -9,6 +10,8 @@ import me.nathanfallet.extopy.models.users.UserContext import me.nathanfallet.extopy.repositories.users.IUsersRepository import me.nathanfallet.surexposed.database.IDatabase import me.nathanfallet.usecases.context.IContext +import me.nathanfallet.usecases.pagination.IPaginationOptions +import me.nathanfallet.usecases.pagination.Pagination import org.jetbrains.exposed.sql.* class UsersDatabaseRepository( @@ -21,6 +24,18 @@ class UsersDatabaseRepository( } } + override suspend fun list(pagination: Pagination, context: IContext?): List { + if (context !is UserContext) return emptyList() + return database.suspendedTransaction { + customJoin(context.userId) + .groupBy(Users.id) + .andWhere(pagination.options) + .orderBy(Users.joinDate to SortOrder.DESC) + .limit(pagination.limit.toInt(), pagination.offset) + .map(Users::toUser) + } + } + override suspend fun get(id: String, context: IContext?): User? { if (context !is UserContext) return null return database.suspendedTransaction { @@ -131,4 +146,22 @@ class UsersDatabaseRepository( Users.followingIn ) + private fun Query.andWhere(options: IPaginationOptions?): Query = when (options) { + is SearchOptions -> { + val likeString = options.search + .replace("%", "\\%") + .replace("_", "\\_") + andWhere { + likeString.split(" ").map { + Users.username like "%$it%" or + (Users.displayName like "%$it%") or + (Users.biography like "%$it%") + }.fold(Op.FALSE as Op) { a, b -> a or b } + } + + } + + else -> this + } + } diff --git a/extopy-backend/src/commonMain/kotlin/me/nathanfallet/extopy/plugins/Koin.kt b/extopy-backend/src/commonMain/kotlin/me/nathanfallet/extopy/plugins/Koin.kt index 6f12786..4cd5cfc 100644 --- a/extopy-backend/src/commonMain/kotlin/me/nathanfallet/extopy/plugins/Koin.kt +++ b/extopy-backend/src/commonMain/kotlin/me/nathanfallet/extopy/plugins/Koin.kt @@ -74,6 +74,8 @@ import me.nathanfallet.usecases.models.get.context.GetModelWithContextFromReposi import me.nathanfallet.usecases.models.get.context.IGetModelWithContextSuspendUseCase import me.nathanfallet.usecases.models.list.slice.IListSliceChildModelSuspendUseCase import me.nathanfallet.usecases.models.list.slice.ListSliceChildModelFromRepositorySuspendUseCase +import me.nathanfallet.usecases.models.list.slice.context.IListSliceModelWithContextSuspendUseCase +import me.nathanfallet.usecases.models.list.slice.context.ListSliceModelWithContextFromRepositorySuspendUseCase import me.nathanfallet.usecases.models.repositories.IChildModelSuspendRepository import me.nathanfallet.usecases.models.repositories.IModelSuspendRepository import me.nathanfallet.usecases.models.update.IUpdateModelSuspendUseCase @@ -168,6 +170,9 @@ fun Application.configureKoin() { // Users single { RequireUserForCallUseCase(get()) } single { GetUserForCallUseCase(get(), get(), get(named())) } + single>(named()) { + ListSliceModelWithContextFromRepositorySuspendUseCase(get()) + } single>(named()) { GetModelWithContextFromRepositorySuspendUseCase(get()) } @@ -190,6 +195,9 @@ fun Application.configureKoin() { single { ListFollowingInUserUseCase(get()) } // Posts + single>(named()) { + ListSliceModelWithContextFromRepositorySuspendUseCase(get()) + } single>(named()) { GetModelWithContextFromRepositorySuspendUseCase(get()) } @@ -248,6 +256,7 @@ fun Application.configureKoin() { get(), get(named()), get(named()), + get(named()), get() ) } @@ -269,6 +278,7 @@ fun Application.configureKoin() { get(named()), get(named()), get(named()), + get(named()), get() ) } diff --git a/extopy-backend/src/jvmTest/kotlin/me/nathanfallet/extopy/controllers/posts/PostsControllerTest.kt b/extopy-backend/src/jvmTest/kotlin/me/nathanfallet/extopy/controllers/posts/PostsControllerTest.kt index 7c5f350..dccc699 100644 --- a/extopy-backend/src/jvmTest/kotlin/me/nathanfallet/extopy/controllers/posts/PostsControllerTest.kt +++ b/extopy-backend/src/jvmTest/kotlin/me/nathanfallet/extopy/controllers/posts/PostsControllerTest.kt @@ -33,7 +33,7 @@ class PostsControllerTest { val requireUserForCallUseCase = mockk() val createPostUseCase = mockk>() val controller = PostsController( - requireUserForCallUseCase, createPostUseCase, mockk(), mockk(), mockk(), mockk() + requireUserForCallUseCase, createPostUseCase, mockk(), mockk(), mockk(), mockk(), mockk() ) val call = mockk() val payload = PostPayload("body") @@ -47,7 +47,7 @@ class PostsControllerTest { val requireUserForCallUseCase = mockk() val createPostUseCase = mockk>() val controller = PostsController( - requireUserForCallUseCase, createPostUseCase, mockk(), mockk(), mockk(), mockk() + requireUserForCallUseCase, createPostUseCase, mockk(), mockk(), mockk(), mockk(), mockk() ) val call = mockk() val payload = PostPayload("body") @@ -67,7 +67,9 @@ class PostsControllerTest { val call = mockk() coEvery { requireUserForCallUseCase(call) } returns user coEvery { getPostUseCase(post.id, UserContext(user.id)) } returns post - val controller = PostsController(requireUserForCallUseCase, mockk(), getPostUseCase, mockk(), mockk(), mockk()) + val controller = PostsController( + requireUserForCallUseCase, mockk(), mockk(), getPostUseCase, mockk(), mockk(), mockk() + ) assertEquals(post, controller.get(call, post.id)) } @@ -78,7 +80,9 @@ class PostsControllerTest { val call = mockk() coEvery { requireUserForCallUseCase(call) } returns user coEvery { getPostUseCase(post.id, UserContext(user.id)) } returns null - val controller = PostsController(requireUserForCallUseCase, mockk(), getPostUseCase, mockk(), mockk(), mockk()) + val controller = PostsController( + requireUserForCallUseCase, mockk(), mockk(), getPostUseCase, mockk(), mockk(), mockk() + ) val exception = assertFailsWith(ControllerException::class) { controller.get(call, post.id) } @@ -100,7 +104,7 @@ class PostsControllerTest { coEvery { getPostUseCase(post.id, UserContext(user.id)) } returns post coEvery { updatePostUseCase(post.id, payload) } returns updatedPost val controller = PostsController( - requireUserForCallUseCase, mockk(), getPostUseCase, updatePostUseCase, mockk(), mockk() + requireUserForCallUseCase, mockk(), mockk(), getPostUseCase, updatePostUseCase, mockk(), mockk() ) assertEquals(updatedPost, controller.update(call, post.id, payload)) } @@ -113,7 +117,9 @@ class PostsControllerTest { val payload = PostPayload("newBody") coEvery { requireUserForCallUseCase(call) } returns user coEvery { getPostUseCase(post.id, UserContext(user.id)) } returns null - val controller = PostsController(requireUserForCallUseCase, mockk(), getPostUseCase, mockk(), mockk(), mockk()) + val controller = PostsController( + requireUserForCallUseCase, mockk(), mockk(), getPostUseCase, mockk(), mockk(), mockk() + ) val exception = assertFailsWith(ControllerException::class) { controller.update(call, post.id, payload) } @@ -129,7 +135,9 @@ class PostsControllerTest { val payload = PostPayload("newBody") coEvery { requireUserForCallUseCase(call) } returns user coEvery { getPostUseCase(otherPost.id, UserContext(user.id)) } returns otherPost - val controller = PostsController(requireUserForCallUseCase, mockk(), getPostUseCase, mockk(), mockk(), mockk()) + val controller = PostsController( + requireUserForCallUseCase, mockk(), mockk(), getPostUseCase, mockk(), mockk(), mockk() + ) val exception = assertFailsWith(ControllerException::class) { controller.update(call, otherPost.id, payload) } @@ -148,7 +156,7 @@ class PostsControllerTest { coEvery { getPostUseCase(post.id, UserContext(user.id)) } returns post coEvery { updatePostUseCase(post.id, payload) } returns null val controller = PostsController( - requireUserForCallUseCase, mockk(), getPostUseCase, updatePostUseCase, mockk(), mockk() + requireUserForCallUseCase, mockk(), mockk(), getPostUseCase, updatePostUseCase, mockk(), mockk() ) val exception = assertFailsWith(ControllerException::class) { controller.update(call, post.id, payload) @@ -167,7 +175,7 @@ class PostsControllerTest { coEvery { getPostUseCase(post.id, UserContext(user.id)) } returns post coEvery { deletePostUseCase(post.id) } returns true val controller = PostsController( - requireUserForCallUseCase, mockk(), getPostUseCase, mockk(), deletePostUseCase, mockk() + requireUserForCallUseCase, mockk(), mockk(), getPostUseCase, mockk(), deletePostUseCase, mockk() ) controller.delete(call, post.id) coVerify { deletePostUseCase(post.id) } @@ -180,7 +188,9 @@ class PostsControllerTest { val call = mockk() coEvery { requireUserForCallUseCase(call) } returns user coEvery { getPostUseCase(post.id, UserContext(user.id)) } returns null - val controller = PostsController(requireUserForCallUseCase, mockk(), getPostUseCase, mockk(), mockk(), mockk()) + val controller = PostsController( + requireUserForCallUseCase, mockk(), mockk(), getPostUseCase, mockk(), mockk(), mockk() + ) val exception = assertFailsWith(ControllerException::class) { controller.delete(call, post.id) } @@ -195,7 +205,9 @@ class PostsControllerTest { val call = mockk() coEvery { requireUserForCallUseCase(call) } returns user coEvery { getPostUseCase(otherPost.id, UserContext(user.id)) } returns otherPost - val controller = PostsController(requireUserForCallUseCase, mockk(), getPostUseCase, mockk(), mockk(), mockk()) + val controller = PostsController( + requireUserForCallUseCase, mockk(), mockk(), getPostUseCase, mockk(), mockk(), mockk() + ) val exception = assertFailsWith(ControllerException::class) { controller.delete(call, otherPost.id) } @@ -213,7 +225,7 @@ class PostsControllerTest { coEvery { getPostUseCase(post.id, UserContext(user.id)) } returns post coEvery { deletePostUseCase(post.id) } returns false val controller = PostsController( - requireUserForCallUseCase, mockk(), getPostUseCase, mockk(), deletePostUseCase, mockk() + requireUserForCallUseCase, mockk(), mockk(), getPostUseCase, mockk(), deletePostUseCase, mockk() ) val exception = assertFailsWith(ControllerException::class) { controller.delete(call, post.id) @@ -231,7 +243,7 @@ class PostsControllerTest { coEvery { getPostUseCase(post.id, UserContext(user.id)) } returns post coEvery { getPostRepliesUseCase(post.id, Pagination(10, 5), UserContext(user.id)) } returns listOf(post) val controller = PostsController( - requireUserForCallUseCase, mockk(), getPostUseCase, mockk(), mockk(), getPostRepliesUseCase + requireUserForCallUseCase, mockk(), mockk(), getPostUseCase, mockk(), mockk(), getPostRepliesUseCase ) assertEquals(listOf(post), controller.listReplies(mockk(), post.id, 10, 5)) } @@ -243,7 +255,7 @@ class PostsControllerTest { coEvery { requireUserForCallUseCase(any()) } returns user coEvery { getPostUseCase(post.id, UserContext(user.id)) } returns null val controller = PostsController( - requireUserForCallUseCase, mockk(), getPostUseCase, mockk(), mockk(), mockk() + requireUserForCallUseCase, mockk(), mockk(), getPostUseCase, mockk(), mockk(), mockk() ) val exception = assertFailsWith(ControllerException::class) { controller.listReplies(mockk(), post.id, null, null) @@ -261,7 +273,7 @@ class PostsControllerTest { coEvery { getPostUseCase(post.id, UserContext(user.id)) } returns post coEvery { getPostRepliesUseCase(post.id, Pagination(25, 0), UserContext(user.id)) } returns listOf(post) val controller = PostsController( - requireUserForCallUseCase, mockk(), getPostUseCase, mockk(), mockk(), getPostRepliesUseCase + requireUserForCallUseCase, mockk(), mockk(), getPostUseCase, mockk(), mockk(), getPostRepliesUseCase ) assertEquals(listOf(post), controller.listReplies(mockk(), post.id, null, null)) } diff --git a/extopy-backend/src/jvmTest/kotlin/me/nathanfallet/extopy/controllers/users/UsersControllerTest.kt b/extopy-backend/src/jvmTest/kotlin/me/nathanfallet/extopy/controllers/users/UsersControllerTest.kt index 235bb84..f64d353 100644 --- a/extopy-backend/src/jvmTest/kotlin/me/nathanfallet/extopy/controllers/users/UsersControllerTest.kt +++ b/extopy-backend/src/jvmTest/kotlin/me/nathanfallet/extopy/controllers/users/UsersControllerTest.kt @@ -33,6 +33,7 @@ class UsersControllerTest { coEvery { getUserUseCase(targetUser.id, UserContext(user.id)) } returns targetUser val controller = UsersController( requireUserForCallUseCase, + mockk(), getUserUseCase, mockk(), mockk() @@ -49,6 +50,7 @@ class UsersControllerTest { coEvery { getUserUseCase(targetUser.id, UserContext(user.id)) } returns null val controller = UsersController( requireUserForCallUseCase, + mockk(), getUserUseCase, mockk(), mockk() @@ -77,6 +79,7 @@ class UsersControllerTest { val controller = UsersController( requireUserForCallUseCase, mockk(), + mockk(), updateUserUseCase, mockk() ) @@ -92,6 +95,7 @@ class UsersControllerTest { requireUserForCallUseCase, mockk(), mockk(), + mockk(), mockk() ) val exception = assertFailsWith(ControllerException::class) { @@ -114,6 +118,7 @@ class UsersControllerTest { val controller = UsersController( requireUserForCallUseCase, mockk(), + mockk(), updateUserUseCase, mockk() ) @@ -135,6 +140,7 @@ class UsersControllerTest { coEvery { getUserPostsUseCase(targetUser.id, Pagination(10, 5), UserContext(user.id)) } returns listOf(post) val controller = UsersController( requireUserForCallUseCase, + mockk(), getUserUseCase, mockk(), getUserPostsUseCase @@ -150,6 +156,7 @@ class UsersControllerTest { coEvery { getUserUseCase(targetUser.id, UserContext(user.id)) } returns null val controller = UsersController( requireUserForCallUseCase, + mockk(), getUserUseCase, mockk(), mockk() @@ -172,6 +179,7 @@ class UsersControllerTest { coEvery { getUserPostsUseCase(targetUser.id, Pagination(25, 0), UserContext(user.id)) } returns listOf(post) val controller = UsersController( requireUserForCallUseCase, + mockk(), getUserUseCase, mockk(), getUserPostsUseCase diff --git a/extopy-commons/src/commonMain/kotlin/me/nathanfallet/extopy/models/application/SearchOptions.kt b/extopy-commons/src/commonMain/kotlin/me/nathanfallet/extopy/models/application/SearchOptions.kt new file mode 100644 index 0000000..11541c5 --- /dev/null +++ b/extopy-commons/src/commonMain/kotlin/me/nathanfallet/extopy/models/application/SearchOptions.kt @@ -0,0 +1,9 @@ +package me.nathanfallet.extopy.models.application + +import kotlinx.serialization.Serializable +import me.nathanfallet.usecases.pagination.IPaginationOptions + +@Serializable +data class SearchOptions( + val search: String, +) : IPaginationOptions