From 613afdffef104b26829816cdf5bc22879d2f022f Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 29 Jan 2024 16:10:17 +0900 Subject: [PATCH 01/42] =?UTF-8?q?feat:=20=E3=83=9A=E3=83=BC=E3=82=B8?= =?UTF-8?q?=E3=83=8D=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=AE=E7=B5=B1?= =?UTF-8?q?=E4=B8=80=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exposed/ExposedPaginationExtension.kt | 19 ++++++++++ .../infrastructure/exposed/Page.kt | 36 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtension.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/Page.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtension.kt b/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtension.kt new file mode 100644 index 00000000..48047aac --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtension.kt @@ -0,0 +1,19 @@ +package dev.usbharu.hideout.application.infrastructure.exposed + +import org.jetbrains.exposed.sql.ExpressionWithColumnType +import org.jetbrains.exposed.sql.Query +import org.jetbrains.exposed.sql.SortOrder +import org.jetbrains.exposed.sql.andWhere + +fun Query.pagination(page: Page, exp: ExpressionWithColumnType): Query { + if (page.minId != null) { + page.maxId?.let { andWhere { exp.lessEq(it) } } + page.minId?.let { andWhere { exp.greaterEq(it) } } + } else { + page.maxId?.let { andWhere { exp.lessEq(it) } } + page.sinceId?.let { andWhere { exp.greaterEq(it) } } + this.orderBy(exp, SortOrder.DESC) + } + page.limit?.let { limit(it) } + return this +} diff --git a/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/Page.kt b/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/Page.kt new file mode 100644 index 00000000..33902372 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/Page.kt @@ -0,0 +1,36 @@ +package dev.usbharu.hideout.application.infrastructure.exposed + +sealed class Page { + abstract val maxId: Long? + abstract val sinceId: Long? + abstract val minId: Long? + abstract val limit: Int? + + data class PageByMaxId( + override val maxId: Long?, + override val sinceId: Long?, + override val limit: Int? + ) : Page() { + override val minId: Long? = null + } + + data class PageByMinId( + override val maxId: Long?, + override val minId: Long?, + override val limit: Int? + ) : Page() { + override val sinceId: Long? = null + } + + companion object { + fun of(maxId: Long?, sinceId: Long?, minId: Long?, limit: Int?): Page = if (minId != null) { + PageByMinId( + maxId, minId, limit + ) + } else { + PageByMaxId( + maxId, sinceId, limit + ) + } + } +} From ba6e21decd88619b1cb18326fb6d1446b82416d7 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 29 Jan 2024 17:59:02 +0900 Subject: [PATCH 02/42] =?UTF-8?q?feat:=20=E3=83=9A=E3=83=BC=E3=82=B8?= =?UTF-8?q?=E3=83=8D=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3=E6=83=85=E5=A0=B1?= =?UTF-8?q?=E3=82=92=E6=8C=81=E3=81=A4PaginationList=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../infrastructure/exposed/PaginationList.kt | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/PaginationList.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/PaginationList.kt b/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/PaginationList.kt new file mode 100644 index 00000000..9bb2239b --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/PaginationList.kt @@ -0,0 +1,22 @@ +package dev.usbharu.hideout.application.infrastructure.exposed + +class PaginationList(list: List, val next: ID?, val prev: ID?) : List by list + +fun PaginationList.toHttpHeader( + nextBlock: (string: String) -> String, + prevBlock: (string: String) -> String +): String? { + val mutableListOf = mutableListOf() + if (next != null) { + mutableListOf.add("<${nextBlock(nextBlock.toString())}>; rel=\"next\";") + } + if (prev != null) { + mutableListOf.add("<${prevBlock(prevBlock.toString())}>; rel=\"prev\";") + } + + if (mutableListOf.isEmpty()) { + return null + } + + return mutableListOf.joinToString(",") +} From deb2d5b0b569dc44d79263fe419a821f355c2d15 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 29 Jan 2024 17:59:46 +0900 Subject: [PATCH 03/42] =?UTF-8?q?refactor:=20RelationshipRepository?= =?UTF-8?q?=E3=82=92Pagination=20API=E3=81=AB=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exposed/ExposedPaginationExtension.kt | 22 +++++++-- .../relationship/RelationshipRepository.kt | 16 +++++++ .../RelationshipRepositoryImpl.kt | 41 +++++++++++++++++ .../account/MastodonAccountApiController.kt | 46 +++++++++++-------- .../service/account/AccountApiService.kt | 36 +++++++++++++++ 5 files changed, 138 insertions(+), 23 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtension.kt b/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtension.kt index 48047aac..796ab0c6 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtension.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtension.kt @@ -1,5 +1,6 @@ package dev.usbharu.hideout.application.infrastructure.exposed +import org.jetbrains.exposed.dao.id.EntityID import org.jetbrains.exposed.sql.ExpressionWithColumnType import org.jetbrains.exposed.sql.Query import org.jetbrains.exposed.sql.SortOrder @@ -7,11 +8,24 @@ import org.jetbrains.exposed.sql.andWhere fun Query.pagination(page: Page, exp: ExpressionWithColumnType): Query { if (page.minId != null) { - page.maxId?.let { andWhere { exp.lessEq(it) } } - page.minId?.let { andWhere { exp.greaterEq(it) } } + page.maxId?.let { andWhere { exp.less(it) } } + page.minId?.let { andWhere { exp.greater(it) } } } else { - page.maxId?.let { andWhere { exp.lessEq(it) } } - page.sinceId?.let { andWhere { exp.greaterEq(it) } } + page.maxId?.let { andWhere { exp.less(it) } } + page.sinceId?.let { andWhere { exp.greater(it) } } + this.orderBy(exp, SortOrder.DESC) + } + page.limit?.let { limit(it) } + return this +} + +fun Query.pagination(page: Page, exp: ExpressionWithColumnType>): Query { + if (page.minId != null) { + page.maxId?.let { andWhere { exp.less(it) } } + page.minId?.let { andWhere { exp.greater(it) } } + } else { + page.maxId?.let { andWhere { exp.less(it) } } + page.sinceId?.let { andWhere { exp.greater(it) } } this.orderBy(exp, SortOrder.DESC) } page.limit?.let { limit(it) } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt index 843e9eac..fa6666c4 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt @@ -1,5 +1,8 @@ package dev.usbharu.hideout.core.domain.model.relationship +import dev.usbharu.hideout.application.infrastructure.exposed.Page +import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList + /** * [Relationship]の永続化 * @@ -43,6 +46,13 @@ interface RelationshipRepository { ignoreFollowRequest: Boolean ): List + suspend fun findByTargetIdAndFollowRequestAndIgnoreFollowRequest( + targetIdLong: Long, + followRequest: Boolean, + ignoreFollowRequest: Boolean, + page: Page.PageByMaxId + ): PaginationList + @Suppress("FunctionMaxLength") suspend fun findByActorIdAntMutingAndMaxIdAndSinceId( actorId: Long, @@ -51,4 +61,10 @@ interface RelationshipRepository { sinceId: Long?, limit: Int ): List + + suspend fun findByActorIdAndMuting( + actorId: Long, + muting: Boolean, + page: Page.PageByMaxId + ): PaginationList } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt index 6aa2fd70..b2aa6bd5 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt @@ -1,5 +1,8 @@ package dev.usbharu.hideout.core.domain.model.relationship +import dev.usbharu.hideout.application.infrastructure.exposed.Page +import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList +import dev.usbharu.hideout.application.infrastructure.exposed.pagination import dev.usbharu.hideout.core.infrastructure.exposedrepository.AbstractRepository import dev.usbharu.hideout.core.infrastructure.exposedrepository.Actors import org.jetbrains.exposed.dao.id.LongIdTable @@ -97,6 +100,26 @@ class RelationshipRepositoryImpl : RelationshipRepository, AbstractRepository() return@query query.map { it.toRelationships() } } + override suspend fun findByTargetIdAndFollowRequestAndIgnoreFollowRequest( + targetId: Long, + followRequest: Boolean, + ignoreFollowRequest: Boolean, + page: Page.PageByMaxId + ): PaginationList = query { + val query = Relationships.select { + Relationships.targetActorId.eq(targetId).and(Relationships.followRequest.eq(followRequest)) + .and(Relationships.ignoreFollowRequestFromTarget.eq(ignoreFollowRequest)) + } + + val resultRowList = query.pagination(page, Relationships.id).toList() + + return@query PaginationList( + query.map { it.toRelationships() }, + resultRowList.lastOrNull()?.getOrNull(Relationships.id)?.value, + resultRowList.firstOrNull()?.getOrNull(Relationships.id)?.value + ) + } + override suspend fun findByActorIdAntMutingAndMaxIdAndSinceId( actorId: Long, muting: Boolean, @@ -119,6 +142,24 @@ class RelationshipRepositoryImpl : RelationshipRepository, AbstractRepository() return@query query.map { it.toRelationships() } } + override suspend fun findByActorIdAndMuting( + actorId: Long, + muting: Boolean, + page: Page.PageByMaxId + ): PaginationList = query { + val query = Relationships.select { + Relationships.actorId.eq(actorId).and(Relationships.muting.eq(muting)) + } + + val resultRowList = query.pagination(page, Relationships.id).toList() + + return@query PaginationList( + query.map { it.toRelationships() }, + resultRowList.lastOrNull()?.getOrNull(Relationships.id)?.value, + resultRowList.firstOrNull()?.getOrNull(Relationships.id)?.value + ) + } + companion object { private val logger = LoggerFactory.getLogger(RelationshipRepositoryImpl::class.java) } diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt index 463e1b06..269943d2 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt @@ -1,6 +1,9 @@ package dev.usbharu.hideout.mastodon.interfaces.api.account +import dev.usbharu.hideout.application.config.ApplicationConfig import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.application.infrastructure.exposed.Page +import dev.usbharu.hideout.application.infrastructure.exposed.toHttpHeader import dev.usbharu.hideout.controller.mastodon.generated.AccountApi import dev.usbharu.hideout.core.infrastructure.springframework.security.LoginUserContextHolder import dev.usbharu.hideout.core.service.user.UserCreateDto @@ -19,12 +22,12 @@ import java.net.URI class MastodonAccountApiController( private val accountApiService: AccountApiService, private val transaction: Transaction, - private val loginUserContextHolder: LoginUserContextHolder + private val loginUserContextHolder: LoginUserContextHolder, + private val applicationConfig: ApplicationConfig ) : AccountApi { override suspend fun apiV1AccountsIdFollowPost( - id: String, - followRequestBody: FollowRequestBody? + id: String, followRequestBody: FollowRequestBody? ): ResponseEntity { val userid = loginUserContextHolder.getLoginUserId() @@ -35,17 +38,11 @@ class MastodonAccountApiController( ResponseEntity.ok(accountApiService.account(id.toLong())) override suspend fun apiV1AccountsVerifyCredentialsGet(): ResponseEntity = ResponseEntity( - accountApiService.verifyCredentials(loginUserContextHolder.getLoginUserId()), - HttpStatus.OK + accountApiService.verifyCredentials(loginUserContextHolder.getLoginUserId()), HttpStatus.OK ) override suspend fun apiV1AccountsPost( - username: String, - password: String, - email: String?, - agreement: Boolean?, - locale: Boolean?, - reason: String? + username: String, password: String, email: String?, agreement: Boolean?, locale: Boolean?, reason: String? ): ResponseEntity { transaction.transaction { accountApiService.registerAccount(UserCreateDto(username, username, "", password)) @@ -85,8 +82,7 @@ class MastodonAccountApiController( } override fun apiV1AccountsRelationshipsGet( - id: List?, - withSuspended: Boolean + id: List?, withSuspended: Boolean ): ResponseEntity> = runBlocking { val userid = loginUserContextHolder.getLoginUserId() @@ -128,8 +124,7 @@ class MastodonAccountApiController( return ResponseEntity.ok(removeFromFollowers) } - override suspend fun apiV1AccountsUpdateCredentialsPatch(updateCredentials: UpdateCredentials?): - ResponseEntity { + override suspend fun apiV1AccountsUpdateCredentialsPatch(updateCredentials: UpdateCredentials?): ResponseEntity { val userid = loginUserContextHolder.getLoginUserId() val removeFromFollowers = accountApiService.updateProfile(userid, updateCredentials) @@ -157,10 +152,23 @@ class MastodonAccountApiController( runBlocking { val userid = loginUserContextHolder.getLoginUserId() - val accountFlow = - accountApiService.followRequests(userid, maxId?.toLong(), sinceId?.toLong(), limit ?: 20, false) - .asFlow() - ResponseEntity.ok(accountFlow) + val followRequests = accountApiService.followRequests( + userid, false, Page.PageByMaxId( + maxId?.toLongOrNull(), sinceId?.toLongOrNull(), limit?.coerceIn(0, 80) ?: 40 + ) + + ) + + val httpHeader = followRequests.toHttpHeader( + { "${applicationConfig.url}/api/v1/follow_requests?max_id=$it" }, + { "${applicationConfig.url}/api/v1/follow_requests?min_id=$it" }, + ) + + if (httpHeader != null) { + return@runBlocking ResponseEntity.ok().header("Link", httpHeader).body(followRequests.asFlow()) + } + + ResponseEntity.ok(followRequests.asFlow()) } override suspend fun apiV1AccountsIdMutePost(id: String): ResponseEntity { diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt index a69e0474..3139576e 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt @@ -1,6 +1,8 @@ package dev.usbharu.hideout.mastodon.service.account import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.application.infrastructure.exposed.Page +import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository import dev.usbharu.hideout.core.service.media.MediaService import dev.usbharu.hideout.core.service.relationship.RelationshipService @@ -58,11 +60,18 @@ interface AccountApiService { withIgnore: Boolean ): List + suspend fun followRequests( + loginUser: Long, + withIgnore: Boolean, + pageByMaxId: Page.PageByMaxId + ): PaginationList + suspend fun acceptFollowRequest(loginUser: Long, target: Long): Relationship suspend fun rejectFollowRequest(loginUser: Long, target: Long): Relationship suspend fun mute(userid: Long, target: Long): Relationship suspend fun unmute(userid: Long, target: Long): Relationship suspend fun mutesAccount(userid: Long, maxId: Long?, sinceId: Long?, limit: Int): List + suspend fun mutesAccount(userid: Long, pageByMaxId: Page.PageByMaxId): PaginationList } @Service @@ -236,6 +245,23 @@ class AccountApiServiceImpl( return@transaction accountService.findByIds(actorIdList) } + override suspend fun followRequests( + loginUser: Long, + withIgnore: Boolean, + pageByMaxId: Page.PageByMaxId + ): PaginationList = transaction.transaction { + val request = + relationshipRepository.findByTargetIdAndFollowRequestAndIgnoreFollowRequest( + loginUser, + true, + withIgnore, + pageByMaxId + ) + val actorIds = request.map { it.actorId } + + return@transaction PaginationList(accountService.findByIds(actorIds), request.next, request.prev) + } + override suspend fun acceptFollowRequest(loginUser: Long, target: Long): Relationship = transaction.transaction { relationshipService.acceptFollowRequest(loginUser, target) @@ -267,6 +293,16 @@ class AccountApiServiceImpl( return accountService.findByIds(mutedAccounts.map { it.targetActorId }) } + override suspend fun mutesAccount(userid: Long, pageByMaxId: Page.PageByMaxId): PaginationList { + val mutedAccounts = relationshipRepository.findByActorIdAndMuting(userid, true, pageByMaxId) + + return PaginationList( + accountService.findByIds(mutedAccounts.map { it.targetActorId }), + mutedAccounts.next, + mutedAccounts.prev + ) + } + private fun from(account: Account): CredentialAccount { return CredentialAccount( id = account.id, From 69c8a18bd1a21181cf7ac5300b9b0854cc0fa89c Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 29 Jan 2024 18:03:48 +0900 Subject: [PATCH 04/42] =?UTF-8?q?refactor:=20Kotlin/JVM=E3=81=AE=E5=88=B6?= =?UTF-8?q?=E9=99=90=E3=81=AE=E3=81=9F=E3=82=81=E3=81=AE=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exposed/ExposedPaginationExtension.kt | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtension.kt b/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtension.kt index 796ab0c6..f00d9dcb 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtension.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtension.kt @@ -1,25 +1,11 @@ package dev.usbharu.hideout.application.infrastructure.exposed -import org.jetbrains.exposed.dao.id.EntityID import org.jetbrains.exposed.sql.ExpressionWithColumnType import org.jetbrains.exposed.sql.Query import org.jetbrains.exposed.sql.SortOrder import org.jetbrains.exposed.sql.andWhere -fun Query.pagination(page: Page, exp: ExpressionWithColumnType): Query { - if (page.minId != null) { - page.maxId?.let { andWhere { exp.less(it) } } - page.minId?.let { andWhere { exp.greater(it) } } - } else { - page.maxId?.let { andWhere { exp.less(it) } } - page.sinceId?.let { andWhere { exp.greater(it) } } - this.orderBy(exp, SortOrder.DESC) - } - page.limit?.let { limit(it) } - return this -} - -fun Query.pagination(page: Page, exp: ExpressionWithColumnType>): Query { +fun Query.pagination(page: Page, exp: ExpressionWithColumnType): Query { if (page.minId != null) { page.maxId?.let { andWhere { exp.less(it) } } page.minId?.let { andWhere { exp.greater(it) } } From f616f72415057f878ee82453854fc47402fdc3a8 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 29 Jan 2024 18:59:37 +0900 Subject: [PATCH 05/42] =?UTF-8?q?refactor:=20Pagination=20API=E3=82=92?= =?UTF-8?q?=E4=BD=BF=E3=81=86=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../account/MastodonAccountApiController.kt | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt index 269943d2..6d69423a 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt @@ -191,9 +191,21 @@ class MastodonAccountApiController( runBlocking { val userid = loginUserContextHolder.getLoginUserId() - val unmute = - accountApiService.mutesAccount(userid, maxId?.toLong(), sinceId?.toLong(), limit ?: 20).asFlow() + val mutes = + accountApiService.mutesAccount( + userid, + Page.PageByMaxId(maxId?.toLongOrNull(), sinceId?.toLongOrNull(), limit?.coerceIn(0, 80) ?: 40) + ) - return@runBlocking ResponseEntity.ok(unmute) + val httpHeader = mutes.toHttpHeader( + { "${applicationConfig.url}/api/v1/mutes?max_id=$it" }, + { "${applicationConfig.url}/api/v1/mutes?since_id=$it" }, + ) + + if (httpHeader != null) { + return@runBlocking ResponseEntity.ok().header("Link", httpHeader).body(mutes.asFlow()) + } + + return@runBlocking ResponseEntity.ok(mutes.asFlow()) } } From 0a19ef572c8230616371f0622edc96532313c975 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 29 Jan 2024 19:04:04 +0900 Subject: [PATCH 06/42] =?UTF-8?q?refactor:=20=E4=B8=8D=E8=A6=81=E3=81=AB?= =?UTF-8?q?=E3=81=AA=E3=81=A3=E3=81=9F=E9=96=A2=E6=95=B0=E3=82=92=E5=89=8A?= =?UTF-8?q?=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../relationship/RelationshipRepository.kt | 19 -------- .../RelationshipRepositoryImpl.kt | 46 ------------------- .../service/account/AccountApiService.kt | 36 --------------- 3 files changed, 101 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt index fa6666c4..a80b7132 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt @@ -36,16 +36,6 @@ interface RelationshipRepository { suspend fun findByTargetIdAndFollowing(targetId: Long, following: Boolean): List - @Suppress("LongParameterList", "FunctionMaxLength") - suspend fun findByTargetIdAndFollowRequestAndIgnoreFollowRequest( - maxId: Long?, - sinceId: Long?, - limit: Int, - targetId: Long, - followRequest: Boolean, - ignoreFollowRequest: Boolean - ): List - suspend fun findByTargetIdAndFollowRequestAndIgnoreFollowRequest( targetIdLong: Long, followRequest: Boolean, @@ -53,15 +43,6 @@ interface RelationshipRepository { page: Page.PageByMaxId ): PaginationList - @Suppress("FunctionMaxLength") - suspend fun findByActorIdAntMutingAndMaxIdAndSinceId( - actorId: Long, - muting: Boolean, - maxId: Long?, - sinceId: Long?, - limit: Int - ): List - suspend fun findByActorIdAndMuting( actorId: Long, muting: Boolean, diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt index b2aa6bd5..5ee4c5f1 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt @@ -76,30 +76,6 @@ class RelationshipRepositoryImpl : RelationshipRepository, AbstractRepository() .map { it.toRelationships() } } - override suspend fun findByTargetIdAndFollowRequestAndIgnoreFollowRequest( - maxId: Long?, - sinceId: Long?, - limit: Int, - targetId: Long, - followRequest: Boolean, - ignoreFollowRequest: Boolean - ): List = query { - val query = Relationships.select { - Relationships.targetActorId.eq(targetId).and(Relationships.followRequest.eq(followRequest)) - .and(Relationships.ignoreFollowRequestFromTarget.eq(ignoreFollowRequest)) - }.limit(limit) - - if (maxId != null) { - query.andWhere { Relationships.id lessEq maxId } - } - - if (sinceId != null) { - query.andWhere { Relationships.id greaterEq sinceId } - } - - return@query query.map { it.toRelationships() } - } - override suspend fun findByTargetIdAndFollowRequestAndIgnoreFollowRequest( targetId: Long, followRequest: Boolean, @@ -120,28 +96,6 @@ class RelationshipRepositoryImpl : RelationshipRepository, AbstractRepository() ) } - override suspend fun findByActorIdAntMutingAndMaxIdAndSinceId( - actorId: Long, - muting: Boolean, - maxId: Long?, - sinceId: Long?, - limit: Int - ): List = query { - val query = Relationships.select { - Relationships.actorId.eq(actorId).and(Relationships.muting.eq(muting)) - }.limit(limit) - - if (maxId != null) { - query.andWhere { Relationships.id lessEq maxId } - } - - if (sinceId != null) { - query.andWhere { Relationships.id greaterEq sinceId } - } - - return@query query.map { it.toRelationships() } - } - override suspend fun findByActorIdAndMuting( actorId: Long, muting: Boolean, diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt index 3139576e..5357effb 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt @@ -52,13 +52,6 @@ interface AccountApiService { suspend fun unfollow(userid: Long, target: Long): Relationship suspend fun removeFromFollowers(userid: Long, target: Long): Relationship suspend fun updateProfile(userid: Long, updateCredentials: UpdateCredentials?): Account - suspend fun followRequests( - loginUser: Long, - maxId: Long?, - sinceId: Long?, - limit: Int = 20, - withIgnore: Boolean - ): List suspend fun followRequests( loginUser: Long, @@ -70,7 +63,6 @@ interface AccountApiService { suspend fun rejectFollowRequest(loginUser: Long, target: Long): Relationship suspend fun mute(userid: Long, target: Long): Relationship suspend fun unmute(userid: Long, target: Long): Relationship - suspend fun mutesAccount(userid: Long, maxId: Long?, sinceId: Long?, limit: Int): List suspend fun mutesAccount(userid: Long, pageByMaxId: Page.PageByMaxId): PaginationList } @@ -224,27 +216,6 @@ class AccountApiServiceImpl( accountService.findById(userid) } - override suspend fun followRequests( - loginUser: Long, - maxId: Long?, - sinceId: Long?, - limit: Int, - withIgnore: Boolean - ): List = transaction.transaction { - val actorIdList = relationshipRepository - .findByTargetIdAndFollowRequestAndIgnoreFollowRequest( - maxId = maxId, - sinceId = sinceId, - limit = limit, - targetId = loginUser, - followRequest = true, - ignoreFollowRequest = withIgnore - ) - .map { it.actorId } - - return@transaction accountService.findByIds(actorIdList) - } - override suspend fun followRequests( loginUser: Long, withIgnore: Boolean, @@ -286,13 +257,6 @@ class AccountApiServiceImpl( return@transaction fetchRelationship(userid, target) } - override suspend fun mutesAccount(userid: Long, maxId: Long?, sinceId: Long?, limit: Int): List { - val mutedAccounts = - relationshipRepository.findByActorIdAntMutingAndMaxIdAndSinceId(userid, true, maxId, sinceId, limit) - - return accountService.findByIds(mutedAccounts.map { it.targetActorId }) - } - override suspend fun mutesAccount(userid: Long, pageByMaxId: Page.PageByMaxId): PaginationList { val mutedAccounts = relationshipRepository.findByActorIdAndMuting(userid, true, pageByMaxId) From 592aba3edc5289631ab37f50c67c467a5f6ec099 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 29 Jan 2024 19:05:31 +0900 Subject: [PATCH 07/42] =?UTF-8?q?style:=20=E5=A4=89=E6=95=B0=E5=90=8D?= =?UTF-8?q?=E3=82=92=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/domain/model/relationship/RelationshipRepository.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt index a80b7132..b08180a6 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepository.kt @@ -37,7 +37,7 @@ interface RelationshipRepository { suspend fun findByTargetIdAndFollowing(targetId: Long, following: Boolean): List suspend fun findByTargetIdAndFollowRequestAndIgnoreFollowRequest( - targetIdLong: Long, + targetId: Long, followRequest: Boolean, ignoreFollowRequest: Boolean, page: Page.PageByMaxId From 7a116513b2e1d1fa484338b81075565cfd0e444b Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 29 Jan 2024 19:22:08 +0900 Subject: [PATCH 08/42] =?UTF-8?q?refactor:=20Pagination=20API=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exposedquery/StatusQueryServiceImpl.kt | 54 +++++++++++++++++++ .../account/MastodonAccountApiController.kt | 27 +++++++--- .../mastodon/query/StatusQueryService.kt | 13 +++++ .../service/account/AccountApiService.kt | 43 +++++++++++++++ 4 files changed, 129 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt index e3c47e6e..1db8bec2 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt @@ -1,5 +1,8 @@ package dev.usbharu.hideout.mastodon.infrastructure.exposedquery +import dev.usbharu.hideout.application.infrastructure.exposed.Page +import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList +import dev.usbharu.hideout.application.infrastructure.exposed.pagination import dev.usbharu.hideout.core.domain.model.emoji.CustomEmoji import dev.usbharu.hideout.core.domain.model.media.toMediaAttachments import dev.usbharu.hideout.core.infrastructure.exposedrepository.* @@ -108,6 +111,57 @@ class StatusQueryServiceImpl : StatusQueryService { return resolveReplyAndRepost(pairs) } + override suspend fun accountsStatus( + accountId: Long, + onlyMedia: Boolean, + excludeReplies: Boolean, + excludeReblogs: Boolean, + pinned: Boolean, + tagged: String?, + includeFollowers: Boolean, + page: Page + ): PaginationList { + val query = Posts + .leftJoin(PostsMedia) + .leftJoin(Actors) + .leftJoin(Media) + .select { Posts.actorId eq accountId } + + query.pagination(page, Posts.id) + + if (onlyMedia) { + query.andWhere { PostsMedia.mediaId.isNotNull() } + } + if (excludeReplies) { + query.andWhere { Posts.replyId.isNotNull() } + } + if (excludeReblogs) { + query.andWhere { Posts.repostId.isNotNull() } + } + if (includeFollowers) { + query.andWhere { Posts.visibility inList listOf(public.ordinal, unlisted.ordinal, private.ordinal) } + } else { + query.andWhere { Posts.visibility inList listOf(public.ordinal, unlisted.ordinal) } + } + + val pairs = query.groupBy { it[Posts.id] } + .map { it.value } + .map { + toStatus(it.first()).copy( + mediaAttachments = it.mapNotNull { resultRow -> + resultRow.toMediaOrNull()?.toMediaAttachments() + } + ) to it.first()[Posts.repostId] + } + + val statuses = resolveReplyAndRepost(pairs) + return PaginationList( + statuses, + statuses.lastOrNull()?.id?.toLongOrNull(), + statuses.firstOrNull()?.id?.toLongOrNull() + ) + } + override suspend fun findByPostId(id: Long): Status { val map = Posts .leftJoin(PostsMedia) diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt index 6d69423a..92e5e7f4 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt @@ -65,20 +65,31 @@ class MastodonAccountApiController( tagged: String? ): ResponseEntity> = runBlocking { val userid = loginUserContextHolder.getLoginUserId() - val statusFlow = accountApiService.accountsStatuses( + val statuses = accountApiService.accountsStatuses( userid = id.toLong(), - maxId = maxId?.toLongOrNull(), - sinceId = sinceId?.toLongOrNull(), - minId = minId?.toLongOrNull(), - limit = limit, onlyMedia = onlyMedia, excludeReplies = excludeReplies, excludeReblogs = excludeReblogs, pinned = pinned, tagged = tagged, - loginUser = userid - ).asFlow() - ResponseEntity.ok(statusFlow) + loginUser = userid, + page = Page.of( + maxId?.toLongOrNull(), + sinceId?.toLongOrNull(), + minId?.toLongOrNull(), + limit.coerceIn(0, 80) ?: 40 + ) + ) + val httpHeader = statuses.toHttpHeader( + { "${applicationConfig.url}/api/v1/follow_requests?max_id=$it" }, + { "${applicationConfig.url}/api/v1/follow_requests?min_id=$it" }, + ) + + if (httpHeader != null) { + return@runBlocking ResponseEntity.ok().header("Link", httpHeader).body(statuses.asFlow()) + } + + ResponseEntity.ok(statuses.asFlow()) } override fun apiV1AccountsRelationshipsGet( diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/query/StatusQueryService.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/query/StatusQueryService.kt index fe78a70d..39869beb 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/query/StatusQueryService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/query/StatusQueryService.kt @@ -1,5 +1,7 @@ package dev.usbharu.hideout.mastodon.query +import dev.usbharu.hideout.application.infrastructure.exposed.Page +import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList import dev.usbharu.hideout.domain.mastodon.model.generated.Status import dev.usbharu.hideout.mastodon.interfaces.api.status.StatusQuery @@ -37,5 +39,16 @@ interface StatusQueryService { includeFollowers: Boolean = false ): List + suspend fun accountsStatus( + accountId: Long, + onlyMedia: Boolean = false, + excludeReplies: Boolean = false, + excludeReblogs: Boolean = false, + pinned: Boolean = false, + tagged: String?, + includeFollowers: Boolean = false, + page: Page + ): PaginationList + suspend fun findByPostId(id: Long): Status } diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt index 5357effb..7aef09cd 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt @@ -34,6 +34,17 @@ interface AccountApiService { loginUser: Long? ): List + suspend fun accountsStatuses( + userid: Long, + onlyMedia: Boolean, + excludeReplies: Boolean, + excludeReblogs: Boolean, + pinned: Boolean, + tagged: String?, + loginUser: Long?, + page: Page + ): PaginationList + suspend fun verifyCredentials(userid: Long): CredentialAccount suspend fun registerAccount(userCreateDto: UserCreateDto): Unit suspend fun follow(loginUser: Long, followTargetUserId: Long): Relationship @@ -115,6 +126,38 @@ class AccountApiServiceImpl( } } + override suspend fun accountsStatuses( + userid: Long, + onlyMedia: Boolean, + excludeReplies: Boolean, + excludeReblogs: Boolean, + pinned: Boolean, + tagged: String?, + loginUser: Long?, + page: Page + ): PaginationList { + val canViewFollowers = if (loginUser == null) { + false + } else { + transaction.transaction { + isFollowing(loginUser, userid) + } + } + + return transaction.transaction { + statusQueryService.accountsStatus( + accountId = userid, + onlyMedia = onlyMedia, + excludeReplies = excludeReplies, + excludeReblogs = excludeReblogs, + pinned = pinned, + tagged = tagged, + includeFollowers = canViewFollowers, + page = page + ) + } + } + override suspend fun verifyCredentials(userid: Long): CredentialAccount = transaction.transaction { val account = accountService.findById(userid) from(account) From a1cf528a59ec632fdaeafa805498b46d6a535e73 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 29 Jan 2024 19:26:02 +0900 Subject: [PATCH 09/42] =?UTF-8?q?refactor:=20=E4=B8=8D=E8=A6=81=E3=81=AB?= =?UTF-8?q?=E3=81=AA=E3=81=A3=E3=81=9F=E9=96=A2=E6=95=B0=E3=82=92=E5=89=8A?= =?UTF-8?q?=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exposedquery/StatusQueryServiceImpl.kt | 56 ------------------- .../mastodon/query/StatusQueryService.kt | 30 ---------- .../service/account/AccountApiService.kt | 51 ----------------- 3 files changed, 137 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt index 1db8bec2..de7bd9ea 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt @@ -55,62 +55,6 @@ class StatusQueryServiceImpl : StatusQueryService { } } - override suspend fun accountsStatus( - accountId: Long, - maxId: Long?, - sinceId: Long?, - minId: Long?, - limit: Int, - onlyMedia: Boolean, - excludeReplies: Boolean, - excludeReblogs: Boolean, - pinned: Boolean, - tagged: String?, - includeFollowers: Boolean - ): List { - val query = Posts - .leftJoin(PostsMedia) - .leftJoin(Actors) - .leftJoin(Media) - .select { Posts.actorId eq accountId }.limit(20) - - if (maxId != null) { - query.andWhere { Posts.id eq maxId } - } - if (sinceId != null) { - query.andWhere { Posts.id eq sinceId } - } - if (minId != null) { - query.andWhere { Posts.id eq minId } - } - if (onlyMedia) { - query.andWhere { PostsMedia.mediaId.isNotNull() } - } - if (excludeReplies) { - query.andWhere { Posts.replyId.isNotNull() } - } - if (excludeReblogs) { - query.andWhere { Posts.repostId.isNotNull() } - } - if (includeFollowers) { - query.andWhere { Posts.visibility inList listOf(public.ordinal, unlisted.ordinal, private.ordinal) } - } else { - query.andWhere { Posts.visibility inList listOf(public.ordinal, unlisted.ordinal) } - } - - val pairs = query.groupBy { it[Posts.id] } - .map { it.value } - .map { - toStatus(it.first()).copy( - mediaAttachments = it.mapNotNull { resultRow -> - resultRow.toMediaOrNull()?.toMediaAttachments() - } - ) to it.first()[Posts.repostId] - } - - return resolveReplyAndRepost(pairs) - } - override suspend fun accountsStatus( accountId: Long, onlyMedia: Boolean, diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/query/StatusQueryService.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/query/StatusQueryService.kt index 39869beb..c17a49a7 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/query/StatusQueryService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/query/StatusQueryService.kt @@ -9,36 +9,6 @@ interface StatusQueryService { suspend fun findByPostIds(ids: List): List suspend fun findByPostIdsWithMediaIds(statusQueries: List): List - /** - * アカウントの投稿一覧を取得します - * - * @param accountId 対象アカウントのid - * @param maxId 投稿の最大id - * @param sinceId 投稿の最小id - * @param minId 不明 - * @param limit 投稿の最大件数 - * @param onlyMedia メディア付き投稿のみ - * @param excludeReplies 返信を除外 - * @param excludeReblogs リブログを除外 - * @param pinned ピン止め投稿のみ - * @param tagged タグ付き? - * @param includeFollowers フォロワー限定投稿を含める - */ - @Suppress("LongParameterList") - suspend fun accountsStatus( - accountId: Long, - maxId: Long? = null, - sinceId: Long? = null, - minId: Long? = null, - limit: Int, - onlyMedia: Boolean = false, - excludeReplies: Boolean = false, - excludeReblogs: Boolean = false, - pinned: Boolean = false, - tagged: String? = null, - includeFollowers: Boolean = false - ): List - suspend fun accountsStatus( accountId: Long, onlyMedia: Boolean = false, diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt index 7aef09cd..68c2781c 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt @@ -19,20 +19,6 @@ import kotlin.math.min @Service @Suppress("TooManyFunctions") interface AccountApiService { - @Suppress("LongParameterList") - suspend fun accountsStatuses( - userid: Long, - maxId: Long?, - sinceId: Long?, - minId: Long?, - limit: Int, - onlyMedia: Boolean, - excludeReplies: Boolean, - excludeReblogs: Boolean, - pinned: Boolean, - tagged: String?, - loginUser: Long? - ): List suspend fun accountsStatuses( userid: Long, @@ -88,43 +74,6 @@ class AccountApiServiceImpl( private val mediaService: MediaService ) : AccountApiService { - override suspend fun accountsStatuses( - userid: Long, - maxId: Long?, - sinceId: Long?, - minId: Long?, - limit: Int, - onlyMedia: Boolean, - excludeReplies: Boolean, - excludeReblogs: Boolean, - pinned: Boolean, - tagged: String?, - loginUser: Long? - ): List { - val canViewFollowers = if (loginUser == null) { - false - } else { - transaction.transaction { - isFollowing(loginUser, userid) - } - } - - return transaction.transaction { - statusQueryService.accountsStatus( - accountId = userid, - maxId = maxId, - sinceId = sinceId, - minId = minId, - limit = limit, - onlyMedia = onlyMedia, - excludeReplies = excludeReplies, - excludeReblogs = excludeReblogs, - pinned = pinned, - tagged = tagged, - includeFollowers = canViewFollowers - ) - } - } override suspend fun accountsStatuses( userid: Long, From 0f4d00840653eb71fdd0ac2cc87ef1a8f9f68804 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 29 Jan 2024 19:37:30 +0900 Subject: [PATCH 10/42] =?UTF-8?q?refactor:=20Notification=20API=E3=82=92Pa?= =?UTF-8?q?gination=20API=E3=81=AB=E5=AF=BE=E5=BF=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../model/MastodonNotificationRepository.kt | 10 ++++ .../ExposedMastodonNotificationRepository.kt | 20 ++++++++ ...goMastodonNotificationRepositoryWrapper.kt | 29 ++++++++++++ .../notification/NotificationApiService.kt | 10 ++++ .../NotificationApiServiceImpl.kt | 46 +++++++++++++++++++ 5 files changed, 115 insertions(+) diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/MastodonNotificationRepository.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/MastodonNotificationRepository.kt index 8d903a56..a89ffdca 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/MastodonNotificationRepository.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/MastodonNotificationRepository.kt @@ -1,5 +1,8 @@ package dev.usbharu.hideout.mastodon.domain.model +import dev.usbharu.hideout.application.infrastructure.exposed.Page +import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList + interface MastodonNotificationRepository { suspend fun save(mastodonNotification: MastodonNotification): MastodonNotification suspend fun deleteById(id: Long) @@ -16,6 +19,13 @@ interface MastodonNotificationRepository { accountId: List ): List + suspend fun findByUserIdAndInTypesAndInSourceActorId( + loginUser: Long, + types: List, + accountId: List, + page: Page + ): PaginationList + suspend fun deleteByUserId(userId: Long) suspend fun deleteByUserIdAndId(userId: Long, id: Long) } diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedrepository/ExposedMastodonNotificationRepository.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedrepository/ExposedMastodonNotificationRepository.kt index 14279e69..6181f3ef 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedrepository/ExposedMastodonNotificationRepository.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedrepository/ExposedMastodonNotificationRepository.kt @@ -1,5 +1,8 @@ package dev.usbharu.hideout.mastodon.infrastructure.exposedrepository +import dev.usbharu.hideout.application.infrastructure.exposed.Page +import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList +import dev.usbharu.hideout.application.infrastructure.exposed.pagination import dev.usbharu.hideout.core.infrastructure.exposedrepository.AbstractRepository import dev.usbharu.hideout.core.infrastructure.exposedrepository.Timelines import dev.usbharu.hideout.mastodon.domain.model.MastodonNotification @@ -88,6 +91,23 @@ class ExposedMastodonNotificationRepository : MastodonNotificationRepository, Ab return@query result.map { it.toMastodonNotification() } } + override suspend fun findByUserIdAndInTypesAndInSourceActorId( + loginUser: Long, + types: List, + accountId: List, + page: Page + ): PaginationList = query { + val query = MastodonNotifications.select { + MastodonNotifications.userId eq loginUser + } + val result = query + .pagination(page, MastodonNotifications.id) + .orderBy(Timelines.createdAt, SortOrder.DESC) + + val notifications = result.map { it.toMastodonNotification() } + return@query PaginationList(notifications, notifications.lastOrNull()?.id, notifications.firstOrNull()?.id) + } + override suspend fun deleteByUserId(userId: Long) { MastodonNotifications.deleteWhere { MastodonNotifications.userId eq userId diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/mongorepository/MongoMastodonNotificationRepositoryWrapper.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/mongorepository/MongoMastodonNotificationRepositoryWrapper.kt index 96c7a278..06f4dadd 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/mongorepository/MongoMastodonNotificationRepositoryWrapper.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/mongorepository/MongoMastodonNotificationRepositoryWrapper.kt @@ -1,5 +1,7 @@ package dev.usbharu.hideout.mastodon.infrastructure.mongorepository +import dev.usbharu.hideout.application.infrastructure.exposed.Page +import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList import dev.usbharu.hideout.mastodon.domain.model.MastodonNotification import dev.usbharu.hideout.mastodon.domain.model.MastodonNotificationRepository import dev.usbharu.hideout.mastodon.domain.model.NotificationType @@ -57,6 +59,33 @@ class MongoMastodonNotificationRepositoryWrapper( return mongoTemplate.find(query, MastodonNotification::class.java) } + override suspend fun findByUserIdAndInTypesAndInSourceActorId( + loginUser: Long, + types: List, + accountId: List, + page: Page + ): PaginationList { + val query = Query() + + if (page.minId != null) { + page.minId?.let { query.addCriteria(Criteria.where("id").gt(it)) } + page.maxId?.let { query.addCriteria(Criteria.where("id").lt(it)) } + } else { + query.with(Sort.by(Sort.Direction.DESC, "createdAt")) + page.sinceId?.let { query.addCriteria(Criteria.where("id").gt(it)) } + page.maxId?.let { query.addCriteria(Criteria.where("id").lt(it)) } + } + + page.limit?.let { query.limit(it) } + + val mastodonNotifications = mongoTemplate.find(query, MastodonNotification::class.java) + return PaginationList( + mastodonNotifications, + mastodonNotifications.lastOrNull()?.id, + mastodonNotifications.firstOrNull()?.id + ) + } + override suspend fun deleteByUserId(userId: Long) { mongoMastodonNotificationRepository.deleteByUserId(userId) } diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/notification/NotificationApiService.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/notification/NotificationApiService.kt index 0849d69a..c336ccc6 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/notification/NotificationApiService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/notification/NotificationApiService.kt @@ -1,5 +1,7 @@ package dev.usbharu.hideout.mastodon.service.notification +import dev.usbharu.hideout.application.infrastructure.exposed.Page +import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList import dev.usbharu.hideout.domain.mastodon.model.generated.Notification import dev.usbharu.hideout.mastodon.domain.model.NotificationType @@ -16,6 +18,14 @@ interface NotificationApiService { accountId: List ): List + suspend fun notifications( + loginUser: Long, + types: List, + excludeTypes: List, + accountId: List, + page: Page + ): PaginationList + suspend fun fingById(loginUser: Long, notificationId: Long): Notification? suspend fun clearAll(loginUser: Long) diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/notification/NotificationApiServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/notification/NotificationApiServiceImpl.kt index 01b1bfb1..528c454c 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/notification/NotificationApiServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/notification/NotificationApiServiceImpl.kt @@ -1,6 +1,8 @@ package dev.usbharu.hideout.mastodon.service.notification import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.application.infrastructure.exposed.Page +import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList import dev.usbharu.hideout.domain.mastodon.model.generated.Notification import dev.usbharu.hideout.mastodon.domain.model.MastodonNotificationRepository import dev.usbharu.hideout.mastodon.domain.model.NotificationType @@ -65,6 +67,50 @@ class NotificationApiServiceImpl( } } + override suspend fun notifications( + loginUser: Long, + types: List, + excludeTypes: List, + accountId: List, + page: Page + ): PaginationList = transaction.transaction { + val typesTmp = mutableListOf() + + typesTmp.addAll(types) + typesTmp.removeAll(excludeTypes) + + val mastodonNotifications = + mastodonNotificationRepository.findByUserIdAndInTypesAndInSourceActorId( + loginUser, + typesTmp, + accountId, + page + ) + + val accounts = accountService.findByIds( + mastodonNotifications.map { + it.accountId + } + ).associateBy { it.id.toLong() } + + val statuses = statusQueryService.findByPostIds(mastodonNotifications.mapNotNull { it.statusId }) + .associateBy { it.id.toLong() } + + val notifications = mastodonNotifications.map { + Notification( + id = it.id.toString(), + type = convertNotificationType(it.type), + createdAt = it.createdAt.toString(), + account = accounts.getValue(it.accountId), + status = statuses[it.statusId], + report = null, + relationshipSeveranceEvent = null + ) + } + + return@transaction PaginationList(notifications, mastodonNotifications.next, mastodonNotifications.prev) + } + override suspend fun fingById(loginUser: Long, notificationId: Long): Notification? { val findById = mastodonNotificationRepository.findById(notificationId) ?: return null From c187aeafa86a763fbfe8c67798c668e14440b02c Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 29 Jan 2024 19:44:35 +0900 Subject: [PATCH 11/42] =?UTF-8?q?fix:=20=E3=82=B3=E3=83=94=E3=83=9A?= =?UTF-8?q?=E5=BE=8C=E3=81=AE=E7=B7=A8=E9=9B=86=E5=BF=98=E3=82=8C=E3=82=92?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../interfaces/api/account/MastodonAccountApiController.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt index 92e5e7f4..64700a54 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt @@ -81,8 +81,8 @@ class MastodonAccountApiController( ) ) val httpHeader = statuses.toHttpHeader( - { "${applicationConfig.url}/api/v1/follow_requests?max_id=$it" }, - { "${applicationConfig.url}/api/v1/follow_requests?min_id=$it" }, + { "${applicationConfig.url}/api/v1/accounts/$id/statuses?max_id=$it" }, + { "${applicationConfig.url}/api/v1/accounts/$id/statuses?min_id=$it" }, ) if (httpHeader != null) { From ee88e2977040742162aa6ee93a05547a89e37e61 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 29 Jan 2024 19:44:50 +0900 Subject: [PATCH 12/42] =?UTF-8?q?refactor:=20Notifications=20API=E3=82=92P?= =?UTF-8?q?agination=20API=E3=81=AB=E5=AF=BE=E5=BF=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MastodonNotificationApiController.kt | 33 ++++++++++++++----- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/notification/MastodonNotificationApiController.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/notification/MastodonNotificationApiController.kt index ba83cb37..1c8905c1 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/notification/MastodonNotificationApiController.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/notification/MastodonNotificationApiController.kt @@ -1,5 +1,8 @@ package dev.usbharu.hideout.mastodon.interfaces.api.notification +import dev.usbharu.hideout.application.config.ApplicationConfig +import dev.usbharu.hideout.application.infrastructure.exposed.Page +import dev.usbharu.hideout.application.infrastructure.exposed.toHttpHeader import dev.usbharu.hideout.controller.mastodon.generated.NotificationsApi import dev.usbharu.hideout.core.infrastructure.springframework.security.LoginUserContextHolder import dev.usbharu.hideout.domain.mastodon.model.generated.Notification @@ -14,7 +17,8 @@ import org.springframework.stereotype.Controller @Controller class MastodonNotificationApiController( private val loginUserContextHolder: LoginUserContextHolder, - private val notificationApiService: NotificationApiService + private val notificationApiService: NotificationApiService, + private val applicationConfig: ApplicationConfig ) : NotificationsApi { override suspend fun apiV1NotificationsClearPost(): ResponseEntity { notificationApiService.clearAll(loginUserContextHolder.getLoginUserId()) @@ -30,17 +34,28 @@ class MastodonNotificationApiController( excludeTypes: List?, accountId: List? ): ResponseEntity> = runBlocking { - val notificationFlow = notificationApiService.notifications( + val notifications = notificationApiService.notifications( loginUser = loginUserContextHolder.getLoginUserId(), - maxId = maxId?.toLong(), - minId = minId?.toLong(), - sinceId = sinceId?.toLong(), - limit = limit ?: 20, types = types.orEmpty().mapNotNull { NotificationType.parse(it) }, excludeTypes = excludeTypes.orEmpty().mapNotNull { NotificationType.parse(it) }, - accountId = accountId.orEmpty().mapNotNull { it.toLongOrNull() } - ).asFlow() - ResponseEntity.ok(notificationFlow) + accountId = accountId.orEmpty().mapNotNull { it.toLongOrNull() }, + page = Page.of( + maxId?.toLongOrNull(), + sinceId?.toLongOrNull(), + minId?.toLongOrNull(), + limit?.coerceIn(0, 80) ?: 40 + ) + ) + + val httpHeader = notifications.toHttpHeader( + { "${applicationConfig.url}/api/v1/notifications?max_id=$it" }, + { "${applicationConfig.url}/api/v1/notifications?min_id=$it" } + ) ?: return@runBlocking ResponseEntity.ok( + notifications.asFlow() + ) + + ResponseEntity.ok().header("Link", httpHeader).body(notifications.asFlow()) + } override suspend fun apiV1NotificationsIdDismissPost(id: String): ResponseEntity { From 7141c7bc6abc1b87b4af3a4a7c896faac4ebdf5c Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 29 Jan 2024 20:17:15 +0900 Subject: [PATCH 13/42] =?UTF-8?q?refactor:=20=E4=B8=8D=E8=A6=81=E3=81=AB?= =?UTF-8?q?=E3=81=AA=E3=81=A3=E3=81=9F=E9=96=A2=E6=95=B0=E3=82=92=E5=89=8A?= =?UTF-8?q?=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../model/MastodonNotificationRepository.kt | 11 ----- .../ExposedMastodonNotificationRepository.kt | 29 ------------ ...goMastodonNotificationRepositoryWrapper.kt | 31 ------------ .../notification/NotificationApiService.kt | 11 ----- .../NotificationApiServiceImpl.kt | 47 ------------------- 5 files changed, 129 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/MastodonNotificationRepository.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/MastodonNotificationRepository.kt index a89ffdca..e1294843 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/MastodonNotificationRepository.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/domain/model/MastodonNotificationRepository.kt @@ -8,17 +8,6 @@ interface MastodonNotificationRepository { suspend fun deleteById(id: Long) suspend fun findById(id: Long): MastodonNotification? - @Suppress("LongParameterList", "FunctionMaxLength") - suspend fun findByUserIdAndMaxIdAndMinIdAndSinceIdAndInTypesAndInSourceActorId( - loginUser: Long, - maxId: Long?, - minId: Long?, - sinceId: Long?, - limit: Int, - typesTmp: MutableList, - accountId: List - ): List - suspend fun findByUserIdAndInTypesAndInSourceActorId( loginUser: Long, types: List, diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedrepository/ExposedMastodonNotificationRepository.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedrepository/ExposedMastodonNotificationRepository.kt index 6181f3ef..bc62b656 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedrepository/ExposedMastodonNotificationRepository.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedrepository/ExposedMastodonNotificationRepository.kt @@ -62,35 +62,6 @@ class ExposedMastodonNotificationRepository : MastodonNotificationRepository, Ab MastodonNotifications.select { MastodonNotifications.id eq id }.singleOrNull()?.toMastodonNotification() } - override suspend fun findByUserIdAndMaxIdAndMinIdAndSinceIdAndInTypesAndInSourceActorId( - loginUser: Long, - maxId: Long?, - minId: Long?, - sinceId: Long?, - limit: Int, - typesTmp: MutableList, - accountId: List - ): List = query { - val query = MastodonNotifications.select { - MastodonNotifications.userId eq loginUser - } - - if (maxId != null) { - query.andWhere { MastodonNotifications.id lessEq maxId } - } - if (minId != null) { - query.andWhere { MastodonNotifications.id greaterEq minId } - } - if (sinceId != null) { - query.andWhere { MastodonNotifications.id greaterEq sinceId } - } - val result = query - .limit(limit) - .orderBy(Timelines.createdAt, SortOrder.DESC) - - return@query result.map { it.toMastodonNotification() } - } - override suspend fun findByUserIdAndInTypesAndInSourceActorId( loginUser: Long, types: List, diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/mongorepository/MongoMastodonNotificationRepositoryWrapper.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/mongorepository/MongoMastodonNotificationRepositoryWrapper.kt index 06f4dadd..fae487d5 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/mongorepository/MongoMastodonNotificationRepositoryWrapper.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/mongorepository/MongoMastodonNotificationRepositoryWrapper.kt @@ -28,37 +28,6 @@ class MongoMastodonNotificationRepositoryWrapper( override suspend fun findById(id: Long): MastodonNotification? = mongoMastodonNotificationRepository.findById(id).getOrNull() - override suspend fun findByUserIdAndMaxIdAndMinIdAndSinceIdAndInTypesAndInSourceActorId( - loginUser: Long, - maxId: Long?, - minId: Long?, - sinceId: Long?, - limit: Int, - typesTmp: MutableList, - accountId: List - ): List { - val query = Query() - - if (maxId != null) { - val criteria = Criteria.where("id").lte(maxId) - query.addCriteria(criteria) - } - - if (minId != null) { - val criteria = Criteria.where("id").gte(minId) - query.addCriteria(criteria) - } - if (sinceId != null) { - val criteria = Criteria.where("id").gte(sinceId) - query.addCriteria(criteria) - } - - query.limit(limit) - query.with(Sort.by(Sort.Direction.DESC, "createdAt")) - - return mongoTemplate.find(query, MastodonNotification::class.java) - } - override suspend fun findByUserIdAndInTypesAndInSourceActorId( loginUser: Long, types: List, diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/notification/NotificationApiService.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/notification/NotificationApiService.kt index c336ccc6..d6b312be 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/notification/NotificationApiService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/notification/NotificationApiService.kt @@ -6,17 +6,6 @@ import dev.usbharu.hideout.domain.mastodon.model.generated.Notification import dev.usbharu.hideout.mastodon.domain.model.NotificationType interface NotificationApiService { - @Suppress("LongParameterList") - suspend fun notifications( - loginUser: Long, - maxId: Long?, - minId: Long?, - sinceId: Long?, - limit: Int, - types: List, - excludeTypes: List, - accountId: List - ): List suspend fun notifications( loginUser: Long, diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/notification/NotificationApiServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/notification/NotificationApiServiceImpl.kt index 528c454c..789d164b 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/notification/NotificationApiServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/notification/NotificationApiServiceImpl.kt @@ -19,53 +19,6 @@ class NotificationApiServiceImpl( private val statusQueryService: StatusQueryService ) : NotificationApiService { - override suspend fun notifications( - loginUser: Long, - maxId: Long?, - minId: Long?, - sinceId: Long?, - limit: Int, - types: List, - excludeTypes: List, - accountId: List - ): List = transaction.transaction { - val typesTmp = mutableListOf() - - typesTmp.addAll(types) - typesTmp.removeAll(excludeTypes) - - val mastodonNotifications = - mastodonNotificationRepository.findByUserIdAndMaxIdAndMinIdAndSinceIdAndInTypesAndInSourceActorId( - loginUser, - maxId, - minId, - sinceId, - limit, - typesTmp, - accountId - ) - - val accounts = accountService.findByIds( - mastodonNotifications.map { - it.accountId - } - ).associateBy { it.id.toLong() } - - val statuses = statusQueryService.findByPostIds(mastodonNotifications.mapNotNull { it.statusId }) - .associateBy { it.id.toLong() } - - mastodonNotifications.map { - Notification( - id = it.id.toString(), - type = convertNotificationType(it.type), - createdAt = it.createdAt.toString(), - account = accounts.getValue(it.accountId), - status = statuses[it.statusId], - report = null, - relationshipSeveranceEvent = null - ) - } - } override suspend fun notifications( loginUser: Long, From b63288ff288082f93f056fd17a73c19d6ca18ce7 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 29 Jan 2024 20:28:46 +0900 Subject: [PATCH 14/42] =?UTF-8?q?refactor:=20Timeline=20API=E3=82=92Pagina?= =?UTF-8?q?tion=20API=E3=81=AB=E5=AF=BE=E5=BF=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExposedGenerateTimelineService.kt | 39 ++++++++++++++ .../timeline/GenerateTimelineService.kt | 9 ++++ .../timeline/MongoGenerateTimelineService.kt | 52 +++++++++++++++++++ .../timeline/MastodonTimelineApiController.kt | 36 +++++++++---- .../service/timeline/TimelineApiService.kt | 24 +++++++++ 5 files changed, 150 insertions(+), 10 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/ExposedGenerateTimelineService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/ExposedGenerateTimelineService.kt index 7d83a9ac..d4f8e1af 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/ExposedGenerateTimelineService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/ExposedGenerateTimelineService.kt @@ -1,5 +1,8 @@ package dev.usbharu.hideout.core.service.timeline +import dev.usbharu.hideout.application.infrastructure.exposed.Page +import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList +import dev.usbharu.hideout.application.infrastructure.exposed.pagination import dev.usbharu.hideout.core.infrastructure.exposedrepository.Timelines import dev.usbharu.hideout.domain.mastodon.model.generated.Status import dev.usbharu.hideout.mastodon.interfaces.api.status.StatusQuery @@ -52,4 +55,40 @@ class ExposedGenerateTimelineService(private val statusQueryService: StatusQuery return statusQueryService.findByPostIdsWithMediaIds(statusQueries) } + + override suspend fun getTimeline( + forUserId: Long?, + localOnly: Boolean, + mediaOnly: Boolean, + page: Page + ): PaginationList { + val query = Timelines.selectAll() + + if (forUserId != null) { + query.andWhere { Timelines.userId eq forUserId } + } + if (localOnly) { + query.andWhere { Timelines.isLocal eq true } + } + query.pagination(page, Timelines.id) + val result = query + .orderBy(Timelines.createdAt, SortOrder.DESC) + + val statusQueries = result.map { + StatusQuery( + it[Timelines.postId], + it[Timelines.replyId], + it[Timelines.repostId], + it[Timelines.mediaIds].split(",").mapNotNull { s -> s.toLongOrNull() }, + it[Timelines.emojiIds].split(",").mapNotNull { s -> s.toLongOrNull() } + ) + } + + val findByPostIdsWithMediaIds = statusQueryService.findByPostIdsWithMediaIds(statusQueries) + return PaginationList( + findByPostIdsWithMediaIds, + findByPostIdsWithMediaIds.lastOrNull()?.id?.toLongOrNull(), + findByPostIdsWithMediaIds.firstOrNull()?.id?.toLongOrNull() + ) + } } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/GenerateTimelineService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/GenerateTimelineService.kt index 7684a97f..4e21c285 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/GenerateTimelineService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/GenerateTimelineService.kt @@ -1,5 +1,7 @@ package dev.usbharu.hideout.core.service.timeline +import dev.usbharu.hideout.application.infrastructure.exposed.Page +import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList import dev.usbharu.hideout.domain.mastodon.model.generated.Status import org.springframework.stereotype.Service @@ -15,4 +17,11 @@ interface GenerateTimelineService { sinceId: Long? = null, limit: Int = 20 ): List + + suspend fun getTimeline( + forUserId: Long? = null, + localOnly: Boolean = false, + mediaOnly: Boolean = false, + page: Page + ): PaginationList } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/MongoGenerateTimelineService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/MongoGenerateTimelineService.kt index 541f24c3..7cc7938f 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/MongoGenerateTimelineService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/MongoGenerateTimelineService.kt @@ -1,5 +1,7 @@ package dev.usbharu.hideout.core.service.timeline +import dev.usbharu.hideout.application.infrastructure.exposed.Page +import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList import dev.usbharu.hideout.core.domain.model.timeline.Timeline import dev.usbharu.hideout.domain.mastodon.model.generated.Status import dev.usbharu.hideout.mastodon.interfaces.api.status.StatusQuery @@ -63,4 +65,54 @@ class MongoGenerateTimelineService( } ) } + + override suspend fun getTimeline( + forUserId: Long?, + localOnly: Boolean, + mediaOnly: Boolean, + page: Page + ): PaginationList { + val query = Query() + + if (forUserId != null) { + val criteria = Criteria.where("userId").`is`(forUserId) + query.addCriteria(criteria) + } + if (localOnly) { + val criteria = Criteria.where("isLocal").`is`(true) + query.addCriteria(criteria) + } + + if (page.minId != null) { + page.minId?.let { query.addCriteria(Criteria.where("id").gt(it)) } + page.maxId?.let { query.addCriteria(Criteria.where("id").lt(it)) } + } else { + query.with(Sort.by(Sort.Direction.DESC, "createdAt")) + page.sinceId?.let { query.addCriteria(Criteria.where("id").gt(it)) } + page.maxId?.let { query.addCriteria(Criteria.where("id").lt(it)) } + } + + page.limit?.let { query.limit(it) } + + query.with(Sort.by(Sort.Direction.DESC, "createdAt")) + + val timelines = mongoTemplate.find(query, Timeline::class.java) + + val statuses = statusQueryService.findByPostIdsWithMediaIds( + timelines.map { + StatusQuery( + it.postId, + it.replyId, + it.repostId, + it.mediaIds, + it.emojiIds + ) + } + ) + return PaginationList( + statuses, + statuses.lastOrNull()?.id?.toLongOrNull(), + statuses.firstOrNull()?.id?.toLongOrNull() + ) + } } diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/timeline/MastodonTimelineApiController.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/timeline/MastodonTimelineApiController.kt index b7ddddae..aaec0252 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/timeline/MastodonTimelineApiController.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/timeline/MastodonTimelineApiController.kt @@ -1,5 +1,8 @@ package dev.usbharu.hideout.mastodon.interfaces.api.timeline +import dev.usbharu.hideout.application.config.ApplicationConfig +import dev.usbharu.hideout.application.infrastructure.exposed.Page +import dev.usbharu.hideout.application.infrastructure.exposed.toHttpHeader import dev.usbharu.hideout.controller.mastodon.generated.TimelineApi import dev.usbharu.hideout.core.infrastructure.springframework.security.LoginUserContextHolder import dev.usbharu.hideout.domain.mastodon.model.generated.Status @@ -14,7 +17,8 @@ import org.springframework.stereotype.Controller @Controller class MastodonTimelineApiController( private val timelineApiService: TimelineApiService, - private val loginUserContextHolder: LoginUserContextHolder + private val loginUserContextHolder: LoginUserContextHolder, + private val applicationConfig: ApplicationConfig, ) : TimelineApi { override fun apiV1TimelinesHomeGet( maxId: String?, @@ -24,10 +28,12 @@ class MastodonTimelineApiController( ): ResponseEntity> = runBlocking { val homeTimeline = timelineApiService.homeTimeline( userId = loginUserContextHolder.getLoginUserId(), - maxId = maxId?.toLongOrNull(), - minId = minId?.toLongOrNull(), - sinceId = sinceId?.toLongOrNull(), - limit = limit ?: 20 + page = Page.of( + maxId = maxId?.toLongOrNull(), + minId = minId?.toLongOrNull(), + sinceId = sinceId?.toLongOrNull(), + limit = limit?.coerceIn(0, 80) ?: 40 + ) ) ResponseEntity(homeTimeline.asFlow(), HttpStatus.OK) } @@ -45,11 +51,21 @@ class MastodonTimelineApiController( localOnly = local ?: false, remoteOnly = remote ?: false, mediaOnly = onlyMedia ?: false, - maxId = maxId?.toLongOrNull(), - minId = minId?.toLongOrNull(), - sinceId = sinceId?.toLongOrNull(), - limit = limit ?: 20 + page = Page.of( + maxId = maxId?.toLongOrNull(), + minId = minId?.toLongOrNull(), + sinceId = sinceId?.toLongOrNull(), + limit = limit?.coerceIn(0, 80) ?: 40 + ) ) - ResponseEntity(publicTimeline.asFlow(), HttpStatus.OK) + + val httpHeader = publicTimeline.toHttpHeader( + { "${applicationConfig.url}/api/v1/public?max_id=$it" }, + { "${applicationConfig.url}/api/v1/public?min_id=$it" } + ) ?: return@runBlocking ResponseEntity( + publicTimeline.asFlow(), + HttpStatus.OK + ) + ResponseEntity.ok().header("Link", httpHeader).body(publicTimeline.asFlow()) } } diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/timeline/TimelineApiService.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/timeline/TimelineApiService.kt index 29fb0e7b..9d11b8ac 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/timeline/TimelineApiService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/timeline/TimelineApiService.kt @@ -1,6 +1,8 @@ package dev.usbharu.hideout.mastodon.service.timeline import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.application.infrastructure.exposed.Page +import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList import dev.usbharu.hideout.core.service.timeline.GenerateTimelineService import dev.usbharu.hideout.domain.mastodon.model.generated.Status import org.springframework.stereotype.Service @@ -17,6 +19,13 @@ interface TimelineApiService { limit: Int = 20 ): List + suspend fun publicTimeline( + localOnly: Boolean = false, + remoteOnly: Boolean = false, + mediaOnly: Boolean = false, + page: Page + ): PaginationList + suspend fun homeTimeline( userId: Long, maxId: Long?, @@ -24,6 +33,11 @@ interface TimelineApiService { sinceId: Long?, limit: Int = 20 ): List + + suspend fun homeTimeline( + userId: Long, + page: Page + ): PaginationList } @Service @@ -51,6 +65,13 @@ class TimelineApiServiceImpl( ) } + override suspend fun publicTimeline( + localOnly: Boolean, + remoteOnly: Boolean, + mediaOnly: Boolean, + page: Page + ): PaginationList = generateTimelineService.getTimeline(forUserId = 0, localOnly, mediaOnly, page) + override suspend fun homeTimeline( userId: Long, maxId: Long?, @@ -66,4 +87,7 @@ class TimelineApiServiceImpl( limit = limit ) } + + override suspend fun homeTimeline(userId: Long, page: Page): PaginationList = + generateTimelineService.getTimeline(forUserId = userId, page = page) } From c0a7c2da3474b8037f6fcd0f483f0c66eb6aa4e6 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 29 Jan 2024 20:30:35 +0900 Subject: [PATCH 15/42] =?UTF-8?q?refactor:=20=E4=B8=8D=E8=A6=81=E3=81=AB?= =?UTF-8?q?=E3=81=AA=E3=81=A3=E3=81=9F=E9=96=A2=E6=95=B0=E3=82=92=E5=89=8A?= =?UTF-8?q?=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExposedGenerateTimelineService.kt | 39 ------------- .../timeline/GenerateTimelineService.kt | 9 --- .../timeline/MongoGenerateTimelineService.kt | 45 -------------- .../service/timeline/TimelineApiService.kt | 58 ++----------------- 4 files changed, 5 insertions(+), 146 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/ExposedGenerateTimelineService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/ExposedGenerateTimelineService.kt index d4f8e1af..42eedaaa 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/ExposedGenerateTimelineService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/ExposedGenerateTimelineService.kt @@ -16,45 +16,6 @@ import org.springframework.stereotype.Service @Service @ConditionalOnProperty("hideout.use-mongodb", havingValue = "false", matchIfMissing = true) class ExposedGenerateTimelineService(private val statusQueryService: StatusQueryService) : GenerateTimelineService { - override suspend fun getTimeline( - forUserId: Long?, - localOnly: Boolean, - mediaOnly: Boolean, - maxId: Long?, - minId: Long?, - sinceId: Long?, - limit: Int - ): List { - val query = Timelines.selectAll() - - if (forUserId != null) { - query.andWhere { Timelines.userId eq forUserId } - } - if (localOnly) { - query.andWhere { Timelines.isLocal eq true } - } - if (maxId != null) { - query.andWhere { Timelines.id lessEq maxId } - } - if (minId != null) { - query.andWhere { Timelines.id greaterEq minId } - } - val result = query - .limit(limit) - .orderBy(Timelines.createdAt, SortOrder.DESC) - - val statusQueries = result.map { - StatusQuery( - it[Timelines.postId], - it[Timelines.replyId], - it[Timelines.repostId], - it[Timelines.mediaIds].split(",").mapNotNull { s -> s.toLongOrNull() }, - it[Timelines.emojiIds].split(",").mapNotNull { s -> s.toLongOrNull() } - ) - } - - return statusQueryService.findByPostIdsWithMediaIds(statusQueries) - } override suspend fun getTimeline( forUserId: Long?, diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/GenerateTimelineService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/GenerateTimelineService.kt index 4e21c285..50ab6271 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/GenerateTimelineService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/GenerateTimelineService.kt @@ -8,15 +8,6 @@ import org.springframework.stereotype.Service @Service @Suppress("LongParameterList") interface GenerateTimelineService { - suspend fun getTimeline( - forUserId: Long? = null, - localOnly: Boolean = false, - mediaOnly: Boolean = false, - maxId: Long? = null, - minId: Long? = null, - sinceId: Long? = null, - limit: Int = 20 - ): List suspend fun getTimeline( forUserId: Long? = null, diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/MongoGenerateTimelineService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/MongoGenerateTimelineService.kt index 7cc7938f..c9bb7618 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/MongoGenerateTimelineService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/MongoGenerateTimelineService.kt @@ -20,51 +20,6 @@ class MongoGenerateTimelineService( private val mongoTemplate: MongoTemplate ) : GenerateTimelineService { - override suspend fun getTimeline( - forUserId: Long?, - localOnly: Boolean, - mediaOnly: Boolean, - maxId: Long?, - minId: Long?, - sinceId: Long?, - limit: Int - ): List { - val query = Query() - - if (forUserId != null) { - val criteria = Criteria.where("userId").`is`(forUserId) - query.addCriteria(criteria) - } - if (localOnly) { - val criteria = Criteria.where("isLocal").`is`(true) - query.addCriteria(criteria) - } - if (maxId != null) { - val criteria = Criteria.where("postId").lt(maxId) - query.addCriteria(criteria) - } - if (minId != null) { - val criteria = Criteria.where("postId").gt(minId) - query.addCriteria(criteria) - } - - query.limit(limit) - query.with(Sort.by(Sort.Direction.DESC, "createdAt")) - - val timelines = mongoTemplate.find(query, Timeline::class.java) - - return statusQueryService.findByPostIdsWithMediaIds( - timelines.map { - StatusQuery( - it.postId, - it.replyId, - it.repostId, - it.mediaIds, - it.emojiIds - ) - } - ) - } override suspend fun getTimeline( forUserId: Long?, diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/timeline/TimelineApiService.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/timeline/TimelineApiService.kt index 9d11b8ac..7a409584 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/timeline/TimelineApiService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/timeline/TimelineApiService.kt @@ -9,15 +9,6 @@ import org.springframework.stereotype.Service @Suppress("LongParameterList") interface TimelineApiService { - suspend fun publicTimeline( - localOnly: Boolean = false, - remoteOnly: Boolean = false, - mediaOnly: Boolean = false, - maxId: Long?, - minId: Long?, - sinceId: Long?, - limit: Int = 20 - ): List suspend fun publicTimeline( localOnly: Boolean = false, @@ -26,14 +17,6 @@ interface TimelineApiService { page: Page ): PaginationList - suspend fun homeTimeline( - userId: Long, - maxId: Long?, - minId: Long?, - sinceId: Long?, - limit: Int = 20 - ): List - suspend fun homeTimeline( userId: Long, page: Page @@ -45,49 +28,18 @@ class TimelineApiServiceImpl( private val generateTimelineService: GenerateTimelineService, private val transaction: Transaction ) : TimelineApiService { - override suspend fun publicTimeline( - localOnly: Boolean, - remoteOnly: Boolean, - mediaOnly: Boolean, - maxId: Long?, - minId: Long?, - sinceId: Long?, - limit: Int - ): List = transaction.transaction { - generateTimelineService.getTimeline( - forUserId = 0, - localOnly = localOnly, - mediaOnly = mediaOnly, - maxId = maxId, - minId = minId, - sinceId = sinceId, - limit = limit - ) - } override suspend fun publicTimeline( localOnly: Boolean, remoteOnly: Boolean, mediaOnly: Boolean, page: Page - ): PaginationList = generateTimelineService.getTimeline(forUserId = 0, localOnly, mediaOnly, page) - - override suspend fun homeTimeline( - userId: Long, - maxId: Long?, - minId: Long?, - sinceId: Long?, - limit: Int - ): List = transaction.transaction { - generateTimelineService.getTimeline( - forUserId = userId, - maxId = maxId, - minId = minId, - sinceId = sinceId, - limit = limit - ) + ): PaginationList = transaction.transaction { + return@transaction generateTimelineService.getTimeline(forUserId = 0, localOnly, mediaOnly, page) } override suspend fun homeTimeline(userId: Long, page: Page): PaginationList = - generateTimelineService.getTimeline(forUserId = userId, page = page) + transaction.transaction { + return@transaction generateTimelineService.getTimeline(forUserId = userId, page = page) + } } From 370869af6200c9e9e2c2ca8db9d375ff253ff3ce Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 29 Jan 2024 20:55:08 +0900 Subject: [PATCH 16/42] =?UTF-8?q?feat:=20=E3=83=87=E3=83=95=E3=82=A9?= =?UTF-8?q?=E3=83=AB=E3=83=88=E5=BC=95=E6=95=B0=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../infrastructure/exposed/Page.kt | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/Page.kt b/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/Page.kt index 33902372..cff9e245 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/Page.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/Page.kt @@ -23,14 +23,20 @@ sealed class Page { } companion object { - fun of(maxId: Long?, sinceId: Long?, minId: Long?, limit: Int?): Page = if (minId != null) { - PageByMinId( - maxId, minId, limit - ) - } else { - PageByMaxId( - maxId, sinceId, limit - ) - } + fun of( + maxId: Long? = null, + sinceId: Long? = null, + minId: Long? = null, + limit: Int? = null + ): Page = + if (minId != null) { + PageByMinId( + maxId, minId, limit + ) + } else { + PageByMaxId( + maxId, sinceId, limit + ) + } } } From 942d5d71e30cbb3574a1361c17a8505a1564d0d8 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 29 Jan 2024 20:55:28 +0900 Subject: [PATCH 17/42] =?UTF-8?q?test:=20=E3=83=86=E3=82=B9=E3=83=88?= =?UTF-8?q?=E3=82=92Pagination=20API=E3=81=AB=E5=AF=BE=E5=BF=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MastodonAccountApiControllerTest.kt | 5 + .../MastodonTimelineApiControllerTest.kt | 218 +++++++++--------- .../account/AccountApiServiceImplTest.kt | 144 ++++++------ 3 files changed, 177 insertions(+), 190 deletions(-) diff --git a/src/test/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiControllerTest.kt b/src/test/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiControllerTest.kt index fd0b9fd4..e8a1e46f 100644 --- a/src/test/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiControllerTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiControllerTest.kt @@ -1,6 +1,7 @@ package dev.usbharu.hideout.mastodon.interfaces.api.account import dev.usbharu.hideout.application.config.ActivityPubConfig +import dev.usbharu.hideout.application.config.ApplicationConfig import dev.usbharu.hideout.core.infrastructure.springframework.security.OAuth2JwtLoginUserContextHolder import dev.usbharu.hideout.domain.mastodon.model.generated.AccountSource import dev.usbharu.hideout.domain.mastodon.model.generated.CredentialAccount @@ -26,6 +27,7 @@ import org.springframework.test.web.servlet.get import org.springframework.test.web.servlet.post import org.springframework.test.web.servlet.setup.MockMvcBuilders import utils.TestTransaction +import java.net.URL @ExtendWith(MockitoExtension::class) class MastodonAccountApiControllerTest { @@ -41,6 +43,9 @@ class MastodonAccountApiControllerTest { @Mock private lateinit var accountApiService: AccountApiService + @Spy + private val applicationConfig: ApplicationConfig = ApplicationConfig(URL("https://example.com")) + @InjectMocks private lateinit var mastodonAccountApiController: MastodonAccountApiController diff --git a/src/test/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/timeline/MastodonTimelineApiControllerTest.kt b/src/test/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/timeline/MastodonTimelineApiControllerTest.kt index 02d10533..7dd4f299 100644 --- a/src/test/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/timeline/MastodonTimelineApiControllerTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/timeline/MastodonTimelineApiControllerTest.kt @@ -1,6 +1,8 @@ package dev.usbharu.hideout.mastodon.interfaces.api.timeline import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import dev.usbharu.hideout.application.config.ApplicationConfig +import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList import dev.usbharu.hideout.core.infrastructure.springframework.security.OAuth2JwtLoginUserContextHolder import dev.usbharu.hideout.domain.mastodon.model.generated.Account import dev.usbharu.hideout.domain.mastodon.model.generated.Status @@ -21,6 +23,7 @@ import org.springframework.test.web.servlet.MockMvc import org.springframework.test.web.servlet.get import org.springframework.test.web.servlet.post import org.springframework.test.web.servlet.setup.MockMvcBuilders +import java.net.URL @ExtendWith(MockitoExtension::class) class MastodonTimelineApiControllerTest { @@ -31,6 +34,9 @@ class MastodonTimelineApiControllerTest { @Mock private lateinit var timelineApiService: TimelineApiService + @Spy + private val applicationConfig: ApplicationConfig = ApplicationConfig(URL("https://example.com")) + @InjectMocks private lateinit var mastodonTimelineApiController: MastodonTimelineApiController @@ -41,107 +47,109 @@ class MastodonTimelineApiControllerTest { mockMvc = MockMvcBuilders.standaloneSetup(mastodonTimelineApiController).build() } - val statusList = listOf( - Status( - id = "", - uri = "", - createdAt = "", - account = Account( + val statusList = PaginationList( + listOf( + Status( id = "", - username = "", - acct = "", - url = "", - displayName = "", - note = "", - avatar = "", - avatarStatic = "", - header = "", - headerStatic = "", - locked = false, - fields = emptyList(), - emojis = emptyList(), - bot = false, - group = false, - discoverable = true, + uri = "", createdAt = "", - lastStatusAt = "", - statusesCount = 0, - followersCount = 0, - noindex = false, - moved = false, - suspendex = false, - limited = false, - followingCount = 0 - ), - content = "", - visibility = Status.Visibility.public, - sensitive = false, - spoilerText = "", - mediaAttachments = emptyList(), - mentions = emptyList(), - tags = emptyList(), - emojis = emptyList(), - reblogsCount = 0, - favouritesCount = 0, - repliesCount = 0, - url = "https://example.com", - inReplyToId = null, - inReplyToAccountId = null, - language = "ja_JP", - text = "Test", - editedAt = null + account = Account( + id = "", + username = "", + acct = "", + url = "", + displayName = "", + note = "", + avatar = "", + avatarStatic = "", + header = "", + headerStatic = "", + locked = false, + fields = emptyList(), + emojis = emptyList(), + bot = false, + group = false, + discoverable = true, + createdAt = "", + lastStatusAt = "", + statusesCount = 0, + followersCount = 0, + noindex = false, + moved = false, + suspendex = false, + limited = false, + followingCount = 0 + ), + content = "", + visibility = Status.Visibility.public, + sensitive = false, + spoilerText = "", + mediaAttachments = emptyList(), + mentions = emptyList(), + tags = emptyList(), + emojis = emptyList(), + reblogsCount = 0, + favouritesCount = 0, + repliesCount = 0, + url = "https://example.com", + inReplyToId = null, + inReplyToAccountId = null, + language = "ja_JP", + text = "Test", + editedAt = null - ), - Status( - id = "", - uri = "", - createdAt = "", - account = Account( + ), + Status( id = "", - username = "", - acct = "", - url = "", - displayName = "", - note = "", - avatar = "", - avatarStatic = "", - header = "", - headerStatic = "", - locked = false, - fields = emptyList(), - emojis = emptyList(), - bot = false, - group = false, - discoverable = true, + uri = "", createdAt = "", - lastStatusAt = "", - statusesCount = 0, - followersCount = 0, - noindex = false, - moved = false, - suspendex = false, - limited = false, - followingCount = 0 - ), - content = "", - visibility = Status.Visibility.public, - sensitive = false, - spoilerText = "", - mediaAttachments = emptyList(), - mentions = emptyList(), - tags = emptyList(), - emojis = emptyList(), - reblogsCount = 0, - favouritesCount = 0, - repliesCount = 0, - url = "https://example.com", - inReplyToId = null, - inReplyToAccountId = null, - language = "ja_JP", - text = "Test", - editedAt = null + account = Account( + id = "", + username = "", + acct = "", + url = "", + displayName = "", + note = "", + avatar = "", + avatarStatic = "", + header = "", + headerStatic = "", + locked = false, + fields = emptyList(), + emojis = emptyList(), + bot = false, + group = false, + discoverable = true, + createdAt = "", + lastStatusAt = "", + statusesCount = 0, + followersCount = 0, + noindex = false, + moved = false, + suspendex = false, + limited = false, + followingCount = 0 + ), + content = "", + visibility = Status.Visibility.public, + sensitive = false, + spoilerText = "", + mediaAttachments = emptyList(), + mentions = emptyList(), + tags = emptyList(), + emojis = emptyList(), + reblogsCount = 0, + favouritesCount = 0, + repliesCount = 0, + url = "https://example.com", + inReplyToId = null, + inReplyToAccountId = null, + language = "ja_JP", + text = "Test", + editedAt = null - ) + ) + ), null, null ) @Test @@ -156,10 +164,7 @@ class MastodonTimelineApiControllerTest { whenever( timelineApiService.homeTimeline( eq(1234), - eq(123456), - eq(54321), - eq(1234567), - eq(20) + any() ) ).doReturn(statusList) @@ -183,10 +188,7 @@ class MastodonTimelineApiControllerTest { whenever( timelineApiService.homeTimeline( eq(1234), - isNull(), - isNull(), - isNull(), - eq(20) + any() ) ).doReturn(statusList) @@ -213,10 +215,7 @@ class MastodonTimelineApiControllerTest { localOnly = eq(false), remoteOnly = eq(true), mediaOnly = eq(false), - maxId = eq(1234), - minId = eq(4321), - sinceId = eq(12345), - limit = eq(20) + any() ) ).doAnswer { println(it.arguments.joinToString()) @@ -245,10 +244,7 @@ class MastodonTimelineApiControllerTest { localOnly = eq(false), remoteOnly = eq(false), mediaOnly = eq(false), - maxId = isNull(), - minId = isNull(), - sinceId = isNull(), - limit = eq(20) + any() ) ).doAnswer { println(it.arguments.joinToString()) diff --git a/src/test/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiServiceImplTest.kt index 192eeb3f..0b5f7a50 100644 --- a/src/test/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiServiceImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiServiceImplTest.kt @@ -1,6 +1,8 @@ package dev.usbharu.hideout.mastodon.service.account import dev.usbharu.hideout.application.external.Transaction +import dev.usbharu.hideout.application.infrastructure.exposed.Page +import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository import dev.usbharu.hideout.core.query.FollowerQueryService @@ -48,63 +50,65 @@ class AccountApiServiceImplTest { @Mock private lateinit var relationshipRepository: RelationshipRepository - + @Mock private lateinit var mediaService: MediaService @InjectMocks private lateinit var accountApiServiceImpl: AccountApiServiceImpl - private val statusList = listOf( - Status( - id = "", - uri = "", - createdAt = "", - account = Account( + private val statusList = PaginationList( + listOf( + Status( id = "", - username = "", - acct = "", - url = "", - displayName = "", - note = "", - avatar = "", - avatarStatic = "", - header = "", - headerStatic = "", - locked = false, - fields = emptyList(), - emojis = emptyList(), - bot = false, - group = false, - discoverable = true, + uri = "", createdAt = "", - lastStatusAt = "", - statusesCount = 0, - followersCount = 0, - noindex = false, - moved = false, - suspendex = false, - limited = false, - followingCount = 0 - ), - content = "", - visibility = Status.Visibility.public, - sensitive = false, - spoilerText = "", - mediaAttachments = emptyList(), - mentions = emptyList(), - tags = emptyList(), - emojis = emptyList(), - reblogsCount = 0, - favouritesCount = 0, - repliesCount = 0, - url = "https://example.com", - inReplyToId = null, - inReplyToAccountId = null, - language = "ja_JP", - text = "Test", - editedAt = null - ) + account = Account( + id = "", + username = "", + acct = "", + url = "", + displayName = "", + note = "", + avatar = "", + avatarStatic = "", + header = "", + headerStatic = "", + locked = false, + fields = emptyList(), + emojis = emptyList(), + bot = false, + group = false, + discoverable = true, + createdAt = "", + lastStatusAt = "", + statusesCount = 0, + followersCount = 0, + noindex = false, + moved = false, + suspendex = false, + limited = false, + followingCount = 0 + ), + content = "", + visibility = Status.Visibility.public, + sensitive = false, + spoilerText = "", + mediaAttachments = emptyList(), + mentions = emptyList(), + tags = emptyList(), + emojis = emptyList(), + reblogsCount = 0, + favouritesCount = 0, + repliesCount = 0, + url = "https://example.com", + inReplyToId = null, + inReplyToAccountId = null, + language = "ja_JP", + text = "Test", + editedAt = null + ) + ), null, null ) @Test @@ -114,16 +118,13 @@ class AccountApiServiceImplTest { whenever( statusQueryService.accountsStatus( accountId = eq(userId), - maxId = isNull(), - sinceId = isNull(), - minId = isNull(), - limit = eq(20), onlyMedia = eq(false), excludeReplies = eq(false), excludeReblogs = eq(false), pinned = eq(false), tagged = isNull(), - includeFollowers = eq(false) + includeFollowers = eq(false), + page = any() ) ).doReturn( statusList @@ -132,16 +133,13 @@ class AccountApiServiceImplTest { val accountsStatuses = accountApiServiceImpl.accountsStatuses( userid = userId, - maxId = null, - sinceId = null, - minId = null, - limit = 20, onlyMedia = false, excludeReplies = false, excludeReblogs = false, pinned = false, tagged = null, - loginUser = null + loginUser = null, + Page.of() ) assertThat(accountsStatuses).hasSize(1) @@ -156,31 +154,25 @@ class AccountApiServiceImplTest { whenever( statusQueryService.accountsStatus( accountId = eq(userId), - maxId = isNull(), - sinceId = isNull(), - minId = isNull(), - limit = eq(20), onlyMedia = eq(false), excludeReplies = eq(false), excludeReblogs = eq(false), pinned = eq(false), tagged = isNull(), - includeFollowers = eq(false) + includeFollowers = eq(false), + page = any() ) ).doReturn(statusList) val accountsStatuses = accountApiServiceImpl.accountsStatuses( userid = userId, - maxId = null, - sinceId = null, - minId = null, - limit = 20, onlyMedia = false, excludeReplies = false, excludeReblogs = false, pinned = false, tagged = null, - loginUser = loginUser + loginUser = loginUser, + Page.of() ) assertThat(accountsStatuses).hasSize(1) @@ -193,16 +185,13 @@ class AccountApiServiceImplTest { whenever( statusQueryService.accountsStatus( accountId = eq(userId), - maxId = isNull(), - sinceId = isNull(), - minId = isNull(), - limit = eq(20), onlyMedia = eq(false), excludeReplies = eq(false), excludeReblogs = eq(false), pinned = eq(false), tagged = isNull(), - includeFollowers = eq(true) + includeFollowers = eq(true), + page = any() ) ).doReturn(statusList) @@ -221,16 +210,13 @@ class AccountApiServiceImplTest { val accountsStatuses = accountApiServiceImpl.accountsStatuses( userid = userId, - maxId = null, - sinceId = null, - minId = null, - limit = 20, onlyMedia = false, excludeReplies = false, excludeReblogs = false, pinned = false, tagged = null, - loginUser = loginUser + loginUser = loginUser, + Page.of() ) assertThat(accountsStatuses).hasSize(1) From 71a14c65c8914cf07d30dbd3548655093a3cb243 Mon Sep 17 00:00:00 2001 From: usbharu Date: Mon, 29 Jan 2024 21:01:59 +0900 Subject: [PATCH 18/42] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../infrastructure/exposed/Page.kt | 8 +++++-- .../account/MastodonAccountApiController.kt | 24 ++++++++++++++----- .../MastodonNotificationApiController.kt | 1 - 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/Page.kt b/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/Page.kt index cff9e245..71df7898 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/Page.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/Page.kt @@ -31,11 +31,15 @@ sealed class Page { ): Page = if (minId != null) { PageByMinId( - maxId, minId, limit + maxId, + minId, + limit ) } else { PageByMaxId( - maxId, sinceId, limit + maxId, + sinceId, + limit ) } } diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt index 64700a54..a32d1910 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt @@ -27,7 +27,8 @@ class MastodonAccountApiController( ) : AccountApi { override suspend fun apiV1AccountsIdFollowPost( - id: String, followRequestBody: FollowRequestBody? + id: String, + followRequestBody: FollowRequestBody? ): ResponseEntity { val userid = loginUserContextHolder.getLoginUserId() @@ -38,11 +39,17 @@ class MastodonAccountApiController( ResponseEntity.ok(accountApiService.account(id.toLong())) override suspend fun apiV1AccountsVerifyCredentialsGet(): ResponseEntity = ResponseEntity( - accountApiService.verifyCredentials(loginUserContextHolder.getLoginUserId()), HttpStatus.OK + accountApiService.verifyCredentials(loginUserContextHolder.getLoginUserId()), + HttpStatus.OK ) override suspend fun apiV1AccountsPost( - username: String, password: String, email: String?, agreement: Boolean?, locale: Boolean?, reason: String? + username: String, + password: String, + email: String?, + agreement: Boolean?, + locale: Boolean?, + reason: String? ): ResponseEntity { transaction.transaction { accountApiService.registerAccount(UserCreateDto(username, username, "", password)) @@ -93,7 +100,8 @@ class MastodonAccountApiController( } override fun apiV1AccountsRelationshipsGet( - id: List?, withSuspended: Boolean + id: List?, + withSuspended: Boolean ): ResponseEntity> = runBlocking { val userid = loginUserContextHolder.getLoginUserId() @@ -164,8 +172,12 @@ class MastodonAccountApiController( val userid = loginUserContextHolder.getLoginUserId() val followRequests = accountApiService.followRequests( - userid, false, Page.PageByMaxId( - maxId?.toLongOrNull(), sinceId?.toLongOrNull(), limit?.coerceIn(0, 80) ?: 40 + userid, + false, + Page.PageByMaxId( + maxId?.toLongOrNull(), + sinceId?.toLongOrNull(), + limit?.coerceIn(0, 80) ?: 40 ) ) diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/notification/MastodonNotificationApiController.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/notification/MastodonNotificationApiController.kt index 1c8905c1..88d06682 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/notification/MastodonNotificationApiController.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/notification/MastodonNotificationApiController.kt @@ -55,7 +55,6 @@ class MastodonNotificationApiController( ) ResponseEntity.ok().header("Link", httpHeader).body(notifications.asFlow()) - } override suspend fun apiV1NotificationsIdDismissPost(id: String): ResponseEntity { From 063692832d9daf35d4bc1bedeba7af6aa1916f7d Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 30 Jan 2024 14:12:46 +0900 Subject: [PATCH 19/42] =?UTF-8?q?test:=20=E3=83=9A=E3=83=BC=E3=82=B8?= =?UTF-8?q?=E3=83=8D=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3=E7=B3=BB=E3=81=AE?= =?UTF-8?q?=E3=83=86=E3=82=B9=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../infrastructure/exposed/PageTest.kt | 27 +++++++++++ .../exposed/PaginationListKtTest.kt | 48 +++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 src/test/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/PageTest.kt create mode 100644 src/test/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/PaginationListKtTest.kt diff --git a/src/test/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/PageTest.kt b/src/test/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/PageTest.kt new file mode 100644 index 00000000..30f88c2c --- /dev/null +++ b/src/test/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/PageTest.kt @@ -0,0 +1,27 @@ +package dev.usbharu.hideout.application.infrastructure.exposed + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test + +class PageTest { + @Test + fun minIdが指定されているとsinceIdは無視される() { + val page = Page.of(1, 2, 3, 4) + + assertThat(page.maxId).isEqualTo(1) + assertThat(page.sinceId).isNull() + assertThat(page.minId).isEqualTo(3) + assertThat(page.limit).isEqualTo(4) + } + + @Test + fun minIdがnullのときはsinceIdが使われる() { + val page = Page.of(1, 2, null, 4) + + assertThat(page.maxId).isEqualTo(1) + assertThat(page.minId).isNull() + assertThat(page.sinceId).isEqualTo(2) + assertThat(page.limit).isEqualTo(4) + } +} \ No newline at end of file diff --git a/src/test/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/PaginationListKtTest.kt b/src/test/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/PaginationListKtTest.kt new file mode 100644 index 00000000..a7f21eba --- /dev/null +++ b/src/test/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/PaginationListKtTest.kt @@ -0,0 +1,48 @@ +package dev.usbharu.hideout.application.infrastructure.exposed + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test + +class PaginationListKtTest { + @Test + fun `toHttpHeader nextとprevがnullでない場合両方作成される`() { + val paginationList = PaginationList(emptyList(), 1, 2) + + val httpHeader = + paginationList.toHttpHeader({ "https://example.com?max_id=$it" }, { "https://example.com?min_id=$it" }) + + assertThat(httpHeader).isEqualTo("; rel=\"next\", ; rel=\"prev\"") + } + + @Test + fun `toHttpHeader nextがnullなら片方だけ作成される`() { + val paginationList = PaginationList(emptyList(), 1,null) + + val httpHeader = + paginationList.toHttpHeader({ "https://example.com?max_id=$it" }, { "https://example.com?min_id=$it" }) + + assertThat(httpHeader).isEqualTo("; rel=\"next\"") + } + + @Test + fun `toHttpHeader prevがnullなら片方だけ作成される`() { + val paginationList = PaginationList(emptyList(), null,2) + + val httpHeader = + paginationList.toHttpHeader({ "https://example.com?max_id=$it" }, { "https://example.com?min_id=$it" }) + + assertThat(httpHeader).isEqualTo("; rel=\"prev\"") + } + + @Test + fun `toHttpHeader 両方nullならnullが返ってくる`() { + val paginationList = PaginationList(emptyList(), null, null) + + + val httpHeader = + paginationList.toHttpHeader({ "https://example.com?max_id=$it" }, { "https://example.com?min_id=$it" }) + + assertThat(httpHeader).isNull() + } +} \ No newline at end of file From ec7c1bdde53e8fbcb9bcb70c21403a19462dd24d Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 30 Jan 2024 14:13:40 +0900 Subject: [PATCH 20/42] =?UTF-8?q?fix:=20=E3=83=9A=E3=83=BC=E3=82=B8?= =?UTF-8?q?=E3=83=8D=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=AEHTTP?= =?UTF-8?q?=E3=83=98=E3=83=83=E3=83=80=E3=83=BC=E5=A4=89=E6=8F=9B=E3=81=AE?= =?UTF-8?q?URL=E7=B5=84=E3=81=BF=E7=AB=8B=E3=81=A6=E9=83=A8=E5=88=86?= =?UTF-8?q?=E3=81=AE=E3=83=90=E3=82=B0=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/infrastructure/exposed/PaginationList.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/PaginationList.kt b/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/PaginationList.kt index 9bb2239b..011aaaa2 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/PaginationList.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/PaginationList.kt @@ -8,15 +8,15 @@ fun PaginationList.toHttpHeader( ): String? { val mutableListOf = mutableListOf() if (next != null) { - mutableListOf.add("<${nextBlock(nextBlock.toString())}>; rel=\"next\";") + mutableListOf.add("<${nextBlock(this.next.toString())}>; rel=\"next\"") } if (prev != null) { - mutableListOf.add("<${prevBlock(prevBlock.toString())}>; rel=\"prev\";") + mutableListOf.add("<${prevBlock(this.prev.toString())}>; rel=\"prev\"") } if (mutableListOf.isEmpty()) { return null } - return mutableListOf.joinToString(",") + return mutableListOf.joinToString(", ") } From 2efae6f6daf281c2106a221cf014c0c71495db16 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 30 Jan 2024 22:26:13 +0900 Subject: [PATCH 21/42] =?UTF-8?q?test:=20=E9=96=93=E9=81=95=E3=81=A3?= =?UTF-8?q?=E3=81=A6=E3=81=84=E3=82=8B=E3=83=86=E3=82=B9=E3=83=88=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExposedPaginationExtensionKtTest.kt | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 src/test/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtensionKtTest.kt diff --git a/src/test/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtensionKtTest.kt b/src/test/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtensionKtTest.kt new file mode 100644 index 00000000..e258978f --- /dev/null +++ b/src/test/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtensionKtTest.kt @@ -0,0 +1,115 @@ +package dev.usbharu.hideout.application.infrastructure.exposed + +import org.assertj.core.api.Assertions.assertThat +import org.jetbrains.exposed.sql.* +import org.jetbrains.exposed.sql.transactions.transaction +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +class ExposedPaginationExtensionKtTest { + + @BeforeEach + fun setUp(): Unit = transaction { + val map = (1..100).map { it to it.toString() } + + ExposePaginationTestTable.batchInsert(map){ + this[ExposePaginationTestTable.id] = it.first.toLong() + this[ExposePaginationTestTable.name] = it.second + } + } + + @AfterEach + fun tearDown():Unit = transaction { + ExposePaginationTestTable.deleteAll() + } + + @Test + fun パラメーター無しでの取得(): Unit = transaction { + val pagination: List = ExposePaginationTestTable.selectAll().pagination(Page.of(), ExposePaginationTestTable.id).limit(20).toList() + + assertThat(pagination.firstOrNull()?.getOrNull(ExposePaginationTestTable.id)).isEqualTo(100) + assertThat(pagination.lastOrNull()?.getOrNull(ExposePaginationTestTable.id)).isEqualTo(81) + assertThat(pagination).size().isEqualTo(20) + } + + @Test + fun maxIdを指定して取得(): Unit = transaction { + val pagination: List = ExposePaginationTestTable.selectAll().pagination(Page.of(maxId = 100), ExposePaginationTestTable.id).limit(20).toList() + + assertThat(pagination.firstOrNull()?.getOrNull(ExposePaginationTestTable.id)).isEqualTo(99) + assertThat(pagination.lastOrNull()?.getOrNull(ExposePaginationTestTable.id)).isEqualTo(80) + assertThat(pagination).size().isEqualTo(20) + } + + @Test + fun sinceIdを指定して取得(): Unit = transaction { + val pagination: List = ExposePaginationTestTable.selectAll().pagination(Page.of(sinceId = 15), ExposePaginationTestTable.id).limit(20).toList() + + assertThat(pagination.firstOrNull()?.getOrNull(ExposePaginationTestTable.id)).isEqualTo(100) + assertThat(pagination.lastOrNull()?.getOrNull(ExposePaginationTestTable.id)).isEqualTo(81) + assertThat(pagination).size().isEqualTo(20) + } + + @Test + fun minIdを指定して取得():Unit = transaction { + val pagination: List = ExposePaginationTestTable.selectAll().pagination(Page.of(minId = 45), ExposePaginationTestTable.id).limit(20).toList() + + assertThat(pagination.firstOrNull()?.getOrNull(ExposePaginationTestTable.id)).isEqualTo(46) + assertThat(pagination.lastOrNull()?.getOrNull(ExposePaginationTestTable.id)).isEqualTo(65) + assertThat(pagination).size().isEqualTo(20) + } + + @Test + fun maxIdとsinceIdを指定して取得(): Unit = transaction { + val pagination: List = ExposePaginationTestTable.selectAll().pagination(Page.of(maxId = 45, sinceId = 34), ExposePaginationTestTable.id).limit(20).toList() + + assertThat(pagination.firstOrNull()?.getOrNull(ExposePaginationTestTable.id)).isEqualTo(44) + assertThat(pagination.lastOrNull()?.getOrNull(ExposePaginationTestTable.id)).isEqualTo(35) + assertThat(pagination).size().isEqualTo(10) + } + + @Test + fun maxIdとminIdを指定して取得():Unit = transaction { + val pagination: List = ExposePaginationTestTable.selectAll().pagination(Page.of(maxId = 54, minId = 45), ExposePaginationTestTable.id).limit(20).toList() + + assertThat(pagination.firstOrNull()?.getOrNull(ExposePaginationTestTable.id)).isEqualTo(46) + assertThat(pagination.lastOrNull()?.getOrNull(ExposePaginationTestTable.id)).isEqualTo(53) + assertThat(pagination).size().isEqualTo(8) + } + + @Test + fun limitを指定して取得():Unit = transaction { + val pagination: List = ExposePaginationTestTable.selectAll().pagination(Page.of(limit = 30), ExposePaginationTestTable.id).toList() + assertThat(pagination).size().isEqualTo(30) + } + + object ExposePaginationTestTable : Table(){ + val id = long("id") + val name = varchar("name",100) + + override val primaryKey: PrimaryKey? + get() = PrimaryKey(id) + } + + companion object { + private lateinit var database: Database + + @JvmStatic + @BeforeAll + fun beforeAll(): Unit { + database = Database.connect( + url = "jdbc:h2:mem:test;MODE=POSTGRESQL;DB_CLOSE_DELAY=-1;CASE_INSENSITIVE_IDENTIFIERS=true;TRACE_LEVEL_FILE=4;", + driver = "org.h2.Driver", + user = "sa", + password = "" + ) + + transaction(database) { + SchemaUtils.create(ExposePaginationTestTable) + SchemaUtils.createMissingTablesAndColumns(ExposePaginationTestTable) + } + } + } +} \ No newline at end of file From e028da59257222b7f065485aec90bc62d62b5946 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 30 Jan 2024 22:26:27 +0900 Subject: [PATCH 22/42] =?UTF-8?q?feat:=20=E4=BF=AE=E6=AD=A3=E3=81=97?= =?UTF-8?q?=E3=81=9F=E5=AE=9F=E8=A3=85=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exposed/ExposedPaginationExtension.kt | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtension.kt b/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtension.kt index f00d9dcb..611d19bd 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtension.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtension.kt @@ -1,9 +1,6 @@ package dev.usbharu.hideout.application.infrastructure.exposed -import org.jetbrains.exposed.sql.ExpressionWithColumnType -import org.jetbrains.exposed.sql.Query -import org.jetbrains.exposed.sql.SortOrder -import org.jetbrains.exposed.sql.andWhere +import org.jetbrains.exposed.sql.* fun Query.pagination(page: Page, exp: ExpressionWithColumnType): Query { if (page.minId != null) { @@ -17,3 +14,19 @@ fun Query.pagination(page: Page, exp: ExpressionWithColumnType): Query { page.limit?.let { limit(it) } return this } + +fun Query.withPagination(page: Page, exp: ExpressionWithColumnType): PaginationList { + page.limit?.let { limit(it) } + val resultRows = if (page.minId != null) { + page.maxId?.let { andWhere { exp.less(it) } } + page.minId?.let { andWhere { exp.greater(it) } } + reversed() + } else { + page.maxId?.let { andWhere { exp.less(it) } } + page.sinceId?.let { andWhere { exp.greater(it) } } + orderBy(exp, SortOrder.DESC) + toList() + } + + return PaginationList(resultRows, resultRows.firstOrNull()?.getOrNull(exp), resultRows.lastOrNull()?.getOrNull(exp)) +} \ No newline at end of file From 97f773144af373b0400a5932d1ba73eaea703002 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 30 Jan 2024 22:54:23 +0900 Subject: [PATCH 23/42] =?UTF-8?q?test:=20=E4=BF=AE=E6=AD=A3=E3=81=97?= =?UTF-8?q?=E3=81=9F=E5=AE=9F=E8=A3=85=E3=81=AE=E6=AD=A3=E3=81=97=E3=81=84?= =?UTF-8?q?=E3=83=86=E3=82=B9=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExposedPaginationExtensionKtTest.kt | 50 ++++++++++++------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/src/test/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtensionKtTest.kt b/src/test/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtensionKtTest.kt index e258978f..b0d8e93b 100644 --- a/src/test/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtensionKtTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtensionKtTest.kt @@ -27,61 +27,73 @@ class ExposedPaginationExtensionKtTest { @Test fun パラメーター無しでの取得(): Unit = transaction { - val pagination: List = ExposePaginationTestTable.selectAll().pagination(Page.of(), ExposePaginationTestTable.id).limit(20).toList() + val pagination: PaginationList = ExposePaginationTestTable.selectAll().limit(20).withPagination(Page.of(), ExposePaginationTestTable.id) - assertThat(pagination.firstOrNull()?.getOrNull(ExposePaginationTestTable.id)).isEqualTo(100) - assertThat(pagination.lastOrNull()?.getOrNull(ExposePaginationTestTable.id)).isEqualTo(81) + assertThat(pagination.next).isEqualTo(100) + assertThat(pagination.prev).isEqualTo(81) + assertThat(pagination.first()[ExposePaginationTestTable.id]).isEqualTo(100) + assertThat(pagination.last()[ExposePaginationTestTable.id]).isEqualTo(81) assertThat(pagination).size().isEqualTo(20) } @Test fun maxIdを指定して取得(): Unit = transaction { - val pagination: List = ExposePaginationTestTable.selectAll().pagination(Page.of(maxId = 100), ExposePaginationTestTable.id).limit(20).toList() + val pagination: PaginationList = ExposePaginationTestTable.selectAll().limit(20).withPagination(Page.of(maxId = 100), ExposePaginationTestTable.id) - assertThat(pagination.firstOrNull()?.getOrNull(ExposePaginationTestTable.id)).isEqualTo(99) - assertThat(pagination.lastOrNull()?.getOrNull(ExposePaginationTestTable.id)).isEqualTo(80) + assertThat(pagination.next).isEqualTo(99) + assertThat(pagination.prev).isEqualTo(80) + assertThat(pagination.first()[ExposePaginationTestTable.id]).isEqualTo(99) + assertThat(pagination.last()[ExposePaginationTestTable.id]).isEqualTo(80) assertThat(pagination).size().isEqualTo(20) } @Test fun sinceIdを指定して取得(): Unit = transaction { - val pagination: List = ExposePaginationTestTable.selectAll().pagination(Page.of(sinceId = 15), ExposePaginationTestTable.id).limit(20).toList() + val pagination: PaginationList = ExposePaginationTestTable.selectAll().limit(20).withPagination(Page.of(sinceId = 15), ExposePaginationTestTable.id) - assertThat(pagination.firstOrNull()?.getOrNull(ExposePaginationTestTable.id)).isEqualTo(100) - assertThat(pagination.lastOrNull()?.getOrNull(ExposePaginationTestTable.id)).isEqualTo(81) + assertThat(pagination.next).isEqualTo(100) + assertThat(pagination.prev).isEqualTo(81) + assertThat(pagination.first()[ExposePaginationTestTable.id]).isEqualTo(100) + assertThat(pagination.last()[ExposePaginationTestTable.id]).isEqualTo(81) assertThat(pagination).size().isEqualTo(20) } @Test fun minIdを指定して取得():Unit = transaction { - val pagination: List = ExposePaginationTestTable.selectAll().pagination(Page.of(minId = 45), ExposePaginationTestTable.id).limit(20).toList() + val pagination: PaginationList = ExposePaginationTestTable.selectAll().limit(20).withPagination(Page.of(minId = 45), ExposePaginationTestTable.id) - assertThat(pagination.firstOrNull()?.getOrNull(ExposePaginationTestTable.id)).isEqualTo(46) - assertThat(pagination.lastOrNull()?.getOrNull(ExposePaginationTestTable.id)).isEqualTo(65) + assertThat(pagination.next).isEqualTo(65) + assertThat(pagination.prev).isEqualTo(46) + assertThat(pagination.first()[ExposePaginationTestTable.id]).isEqualTo(65) + assertThat(pagination.last()[ExposePaginationTestTable.id]).isEqualTo(46) assertThat(pagination).size().isEqualTo(20) } @Test fun maxIdとsinceIdを指定して取得(): Unit = transaction { - val pagination: List = ExposePaginationTestTable.selectAll().pagination(Page.of(maxId = 45, sinceId = 34), ExposePaginationTestTable.id).limit(20).toList() + val pagination: PaginationList = ExposePaginationTestTable.selectAll().limit(20).withPagination(Page.of(maxId = 45, sinceId = 34), ExposePaginationTestTable.id) - assertThat(pagination.firstOrNull()?.getOrNull(ExposePaginationTestTable.id)).isEqualTo(44) - assertThat(pagination.lastOrNull()?.getOrNull(ExposePaginationTestTable.id)).isEqualTo(35) + assertThat(pagination.next).isEqualTo(44) + assertThat(pagination.prev).isEqualTo(35) + assertThat(pagination.first()[ExposePaginationTestTable.id]).isEqualTo(44) + assertThat(pagination.last()[ExposePaginationTestTable.id]).isEqualTo(35) assertThat(pagination).size().isEqualTo(10) } @Test fun maxIdとminIdを指定して取得():Unit = transaction { - val pagination: List = ExposePaginationTestTable.selectAll().pagination(Page.of(maxId = 54, minId = 45), ExposePaginationTestTable.id).limit(20).toList() + val pagination: PaginationList = ExposePaginationTestTable.selectAll().limit(20).withPagination(Page.of(maxId = 54, minId = 45), ExposePaginationTestTable.id) - assertThat(pagination.firstOrNull()?.getOrNull(ExposePaginationTestTable.id)).isEqualTo(46) - assertThat(pagination.lastOrNull()?.getOrNull(ExposePaginationTestTable.id)).isEqualTo(53) + assertThat(pagination.next).isEqualTo(53) + assertThat(pagination.prev).isEqualTo(46) + assertThat(pagination.first()[ExposePaginationTestTable.id]).isEqualTo(53) + assertThat(pagination.last()[ExposePaginationTestTable.id]).isEqualTo(46) assertThat(pagination).size().isEqualTo(8) } @Test fun limitを指定して取得():Unit = transaction { - val pagination: List = ExposePaginationTestTable.selectAll().pagination(Page.of(limit = 30), ExposePaginationTestTable.id).toList() + val pagination: PaginationList = ExposePaginationTestTable.selectAll().withPagination(Page.of(limit = 30), ExposePaginationTestTable.id) assertThat(pagination).size().isEqualTo(30) } From 3a6271a872ccf87bb35244075736c8cca4900666 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 30 Jan 2024 22:54:39 +0900 Subject: [PATCH 24/42] =?UTF-8?q?fix:=20=E4=BF=AE=E6=AD=A3=E3=81=97?= =?UTF-8?q?=E3=81=9F=E5=AE=9F=E8=A3=85=E3=81=AB=E5=88=87=E3=82=8A=E6=9B=BF?= =?UTF-8?q?=E3=81=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../relationship/RelationshipRepositoryImpl.kt | 17 +++++++++-------- .../timeline/ExposedGenerateTimelineService.kt | 6 +++--- .../exposedquery/StatusQueryServiceImpl.kt | 7 ++++--- .../ExposedMastodonNotificationRepository.kt | 8 +++----- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt index 5ee4c5f1..96637607 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt @@ -3,6 +3,7 @@ package dev.usbharu.hideout.core.domain.model.relationship import dev.usbharu.hideout.application.infrastructure.exposed.Page import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList import dev.usbharu.hideout.application.infrastructure.exposed.pagination +import dev.usbharu.hideout.application.infrastructure.exposed.withPagination import dev.usbharu.hideout.core.infrastructure.exposedrepository.AbstractRepository import dev.usbharu.hideout.core.infrastructure.exposedrepository.Actors import org.jetbrains.exposed.dao.id.LongIdTable @@ -87,12 +88,12 @@ class RelationshipRepositoryImpl : RelationshipRepository, AbstractRepository() .and(Relationships.ignoreFollowRequestFromTarget.eq(ignoreFollowRequest)) } - val resultRowList = query.pagination(page, Relationships.id).toList() + val resultRowList = query.withPagination(page, Relationships.id) return@query PaginationList( - query.map { it.toRelationships() }, - resultRowList.lastOrNull()?.getOrNull(Relationships.id)?.value, - resultRowList.firstOrNull()?.getOrNull(Relationships.id)?.value + resultRowList.map { it.toRelationships() }, + resultRowList.next?.value, + resultRowList.prev?.value ) } @@ -105,12 +106,12 @@ class RelationshipRepositoryImpl : RelationshipRepository, AbstractRepository() Relationships.actorId.eq(actorId).and(Relationships.muting.eq(muting)) } - val resultRowList = query.pagination(page, Relationships.id).toList() + val resultRowList = query.withPagination(page, Relationships.id) return@query PaginationList( - query.map { it.toRelationships() }, - resultRowList.lastOrNull()?.getOrNull(Relationships.id)?.value, - resultRowList.firstOrNull()?.getOrNull(Relationships.id)?.value + resultRowList.map { it.toRelationships() }, + resultRowList.next?.value, + resultRowList.prev?.value ) } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/ExposedGenerateTimelineService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/ExposedGenerateTimelineService.kt index 42eedaaa..ce0bb389 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/ExposedGenerateTimelineService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/ExposedGenerateTimelineService.kt @@ -3,6 +3,7 @@ package dev.usbharu.hideout.core.service.timeline import dev.usbharu.hideout.application.infrastructure.exposed.Page import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList import dev.usbharu.hideout.application.infrastructure.exposed.pagination +import dev.usbharu.hideout.application.infrastructure.exposed.withPagination import dev.usbharu.hideout.core.infrastructure.exposedrepository.Timelines import dev.usbharu.hideout.domain.mastodon.model.generated.Status import dev.usbharu.hideout.mastodon.interfaces.api.status.StatusQuery @@ -31,9 +32,8 @@ class ExposedGenerateTimelineService(private val statusQueryService: StatusQuery if (localOnly) { query.andWhere { Timelines.isLocal eq true } } - query.pagination(page, Timelines.id) - val result = query - .orderBy(Timelines.createdAt, SortOrder.DESC) + val result = query.withPagination(page, Timelines.id) + val statusQueries = result.map { StatusQuery( diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt index de7bd9ea..5d0589fa 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt @@ -3,6 +3,7 @@ package dev.usbharu.hideout.mastodon.infrastructure.exposedquery import dev.usbharu.hideout.application.infrastructure.exposed.Page import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList import dev.usbharu.hideout.application.infrastructure.exposed.pagination +import dev.usbharu.hideout.application.infrastructure.exposed.withPagination import dev.usbharu.hideout.core.domain.model.emoji.CustomEmoji import dev.usbharu.hideout.core.domain.model.media.toMediaAttachments import dev.usbharu.hideout.core.infrastructure.exposedrepository.* @@ -71,8 +72,6 @@ class StatusQueryServiceImpl : StatusQueryService { .leftJoin(Media) .select { Posts.actorId eq accountId } - query.pagination(page, Posts.id) - if (onlyMedia) { query.andWhere { PostsMedia.mediaId.isNotNull() } } @@ -88,7 +87,9 @@ class StatusQueryServiceImpl : StatusQueryService { query.andWhere { Posts.visibility inList listOf(public.ordinal, unlisted.ordinal) } } - val pairs = query.groupBy { it[Posts.id] } + val pairs = query + .withPagination(page,Posts.id) + .groupBy { it[Posts.id] } .map { it.value } .map { toStatus(it.first()).copy( diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedrepository/ExposedMastodonNotificationRepository.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedrepository/ExposedMastodonNotificationRepository.kt index bc62b656..2aacff1e 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedrepository/ExposedMastodonNotificationRepository.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedrepository/ExposedMastodonNotificationRepository.kt @@ -3,6 +3,7 @@ package dev.usbharu.hideout.mastodon.infrastructure.exposedrepository import dev.usbharu.hideout.application.infrastructure.exposed.Page import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList import dev.usbharu.hideout.application.infrastructure.exposed.pagination +import dev.usbharu.hideout.application.infrastructure.exposed.withPagination import dev.usbharu.hideout.core.infrastructure.exposedrepository.AbstractRepository import dev.usbharu.hideout.core.infrastructure.exposedrepository.Timelines import dev.usbharu.hideout.mastodon.domain.model.MastodonNotification @@ -71,12 +72,9 @@ class ExposedMastodonNotificationRepository : MastodonNotificationRepository, Ab val query = MastodonNotifications.select { MastodonNotifications.userId eq loginUser } - val result = query - .pagination(page, MastodonNotifications.id) - .orderBy(Timelines.createdAt, SortOrder.DESC) + val result = query.withPagination(page, MastodonNotifications.id) - val notifications = result.map { it.toMastodonNotification() } - return@query PaginationList(notifications, notifications.lastOrNull()?.id, notifications.firstOrNull()?.id) + return@query PaginationList(result.map { it.toMastodonNotification() }, result.next, result.prev) } override suspend fun deleteByUserId(userId: Long) { From f80148815a04e82b60b26071e5e98c08894ee0e9 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 30 Jan 2024 22:55:06 +0900 Subject: [PATCH 25/42] =?UTF-8?q?refactor:=20=E9=96=93=E9=81=95=E3=81=A3?= =?UTF-8?q?=E3=81=A6=E3=81=84=E3=82=8B=E5=AE=9F=E8=A3=85=E3=82=92=E5=89=8A?= =?UTF-8?q?=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exposed/ExposedPaginationExtension.kt | 13 ------------- .../relationship/RelationshipRepositoryImpl.kt | 1 - .../timeline/ExposedGenerateTimelineService.kt | 2 -- .../exposedquery/StatusQueryServiceImpl.kt | 1 - .../ExposedMastodonNotificationRepository.kt | 2 -- 5 files changed, 19 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtension.kt b/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtension.kt index 611d19bd..44f93ed7 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtension.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtension.kt @@ -2,19 +2,6 @@ package dev.usbharu.hideout.application.infrastructure.exposed import org.jetbrains.exposed.sql.* -fun Query.pagination(page: Page, exp: ExpressionWithColumnType): Query { - if (page.minId != null) { - page.maxId?.let { andWhere { exp.less(it) } } - page.minId?.let { andWhere { exp.greater(it) } } - } else { - page.maxId?.let { andWhere { exp.less(it) } } - page.sinceId?.let { andWhere { exp.greater(it) } } - this.orderBy(exp, SortOrder.DESC) - } - page.limit?.let { limit(it) } - return this -} - fun Query.withPagination(page: Page, exp: ExpressionWithColumnType): PaginationList { page.limit?.let { limit(it) } val resultRows = if (page.minId != null) { diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt index 96637607..fbda7c9a 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/relationship/RelationshipRepositoryImpl.kt @@ -2,7 +2,6 @@ package dev.usbharu.hideout.core.domain.model.relationship import dev.usbharu.hideout.application.infrastructure.exposed.Page import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList -import dev.usbharu.hideout.application.infrastructure.exposed.pagination import dev.usbharu.hideout.application.infrastructure.exposed.withPagination import dev.usbharu.hideout.core.infrastructure.exposedrepository.AbstractRepository import dev.usbharu.hideout.core.infrastructure.exposedrepository.Actors diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/ExposedGenerateTimelineService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/ExposedGenerateTimelineService.kt index ce0bb389..b45300d5 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/ExposedGenerateTimelineService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/ExposedGenerateTimelineService.kt @@ -2,13 +2,11 @@ package dev.usbharu.hideout.core.service.timeline import dev.usbharu.hideout.application.infrastructure.exposed.Page import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList -import dev.usbharu.hideout.application.infrastructure.exposed.pagination import dev.usbharu.hideout.application.infrastructure.exposed.withPagination import dev.usbharu.hideout.core.infrastructure.exposedrepository.Timelines import dev.usbharu.hideout.domain.mastodon.model.generated.Status import dev.usbharu.hideout.mastodon.interfaces.api.status.StatusQuery import dev.usbharu.hideout.mastodon.query.StatusQueryService -import org.jetbrains.exposed.sql.SortOrder import org.jetbrains.exposed.sql.andWhere import org.jetbrains.exposed.sql.selectAll import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt index 5d0589fa..86e4f9f4 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt @@ -2,7 +2,6 @@ package dev.usbharu.hideout.mastodon.infrastructure.exposedquery import dev.usbharu.hideout.application.infrastructure.exposed.Page import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList -import dev.usbharu.hideout.application.infrastructure.exposed.pagination import dev.usbharu.hideout.application.infrastructure.exposed.withPagination import dev.usbharu.hideout.core.domain.model.emoji.CustomEmoji import dev.usbharu.hideout.core.domain.model.media.toMediaAttachments diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedrepository/ExposedMastodonNotificationRepository.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedrepository/ExposedMastodonNotificationRepository.kt index 2aacff1e..ff67cae5 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedrepository/ExposedMastodonNotificationRepository.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedrepository/ExposedMastodonNotificationRepository.kt @@ -2,10 +2,8 @@ package dev.usbharu.hideout.mastodon.infrastructure.exposedrepository import dev.usbharu.hideout.application.infrastructure.exposed.Page import dev.usbharu.hideout.application.infrastructure.exposed.PaginationList -import dev.usbharu.hideout.application.infrastructure.exposed.pagination import dev.usbharu.hideout.application.infrastructure.exposed.withPagination import dev.usbharu.hideout.core.infrastructure.exposedrepository.AbstractRepository -import dev.usbharu.hideout.core.infrastructure.exposedrepository.Timelines import dev.usbharu.hideout.mastodon.domain.model.MastodonNotification import dev.usbharu.hideout.mastodon.domain.model.MastodonNotificationRepository import dev.usbharu.hideout.mastodon.domain.model.NotificationType From 2d6d25763e79e3407bc8340d50cf765fbd149175 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 30 Jan 2024 22:59:13 +0900 Subject: [PATCH 26/42] =?UTF-8?q?test:=20prev=E3=81=A8next=E3=81=8Cnull?= =?UTF-8?q?=E3=81=AB=E3=81=AA=E3=82=8B=E5=A0=B4=E5=90=88=E3=81=AE=E3=83=86?= =?UTF-8?q?=E3=82=B9=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exposed/ExposedPaginationExtensionKtTest.kt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/test/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtensionKtTest.kt b/src/test/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtensionKtTest.kt index b0d8e93b..2f6930d5 100644 --- a/src/test/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtensionKtTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtensionKtTest.kt @@ -97,6 +97,16 @@ class ExposedPaginationExtensionKtTest { assertThat(pagination).size().isEqualTo(30) } + @Test + fun 結果が0件の場合はprevとnextがnullになる():Unit = transaction { + val pagination = ExposePaginationTestTable.select { ExposePaginationTestTable.id.isNull() } + .withPagination(Page.of(), ExposePaginationTestTable.id) + + assertThat(pagination).isEmpty() + assertThat(pagination.next).isNull() + assertThat(pagination.prev).isNull() + } + object ExposePaginationTestTable : Table(){ val id = long("id") val name = varchar("name",100) From 09e5ff7ea56489803ee3ca2ebe1d7d0cd3bebded Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 30 Jan 2024 23:17:41 +0900 Subject: [PATCH 27/42] =?UTF-8?q?refactor:=20=E4=B8=8D=E8=A6=81=E3=81=AAnu?= =?UTF-8?q?ll=E3=83=81=E3=82=A7=E3=83=83=E3=82=AF=E3=82=92=E5=89=8A?= =?UTF-8?q?=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../infrastructure/exposed/ExposedPaginationExtension.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtension.kt b/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtension.kt index 44f93ed7..7b0b0c4f 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtension.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtension.kt @@ -6,7 +6,7 @@ fun Query.withPagination(page: Page, exp: ExpressionWithColumnType): Pagi page.limit?.let { limit(it) } val resultRows = if (page.minId != null) { page.maxId?.let { andWhere { exp.less(it) } } - page.minId?.let { andWhere { exp.greater(it) } } + andWhere { exp.greater(page.minId!!) } reversed() } else { page.maxId?.let { andWhere { exp.less(it) } } From 31697ee5b7f6ff324696d0a53db20f9ff8f37b77 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 30 Jan 2024 23:47:08 +0900 Subject: [PATCH 28/42] =?UTF-8?q?test:=20=E9=80=9A=E7=9F=A5API=E3=81=AE?= =?UTF-8?q?=E3=83=9A=E3=83=BC=E3=82=B8=E3=83=B3=E3=82=B0=E3=81=AE=E3=83=86?= =?UTF-8?q?=E3=82=B9=E3=83=88=E7=94=A8SQL=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sql/notification/test-notifications.sql | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 src/intTest/resources/sql/notification/test-notifications.sql diff --git a/src/intTest/resources/sql/notification/test-notifications.sql b/src/intTest/resources/sql/notification/test-notifications.sql new file mode 100644 index 00000000..2107cc3e --- /dev/null +++ b/src/intTest/resources/sql/notification/test-notifications.sql @@ -0,0 +1,68 @@ +insert into notifications(id, type, user_id, source_actor_id, post_id, text, reaction_id, created_at) +VALUES (1, 'follow', 1, 2, null, null, null, current_timestamp), + (2, 'follow', 1, 2, null, null, null, current_timestamp), + (3, 'follow', 1, 2, null, null, null, current_timestamp), + (4, 'follow', 1, 2, null, null, null, current_timestamp), + (5, 'follow', 1, 2, null, null, null, current_timestamp), + (6, 'follow', 1, 2, null, null, null, current_timestamp), + (7, 'follow', 1, 2, null, null, null, current_timestamp), + (8, 'follow', 1, 2, null, null, null, current_timestamp), + (9, 'follow', 1, 2, null, null, null, current_timestamp), + (10, 'follow', 1, 2, null, null, null, current_timestamp), + (11, 'follow', 1, 2, null, null, null, current_timestamp), + (12, 'follow', 1, 2, null, null, null, current_timestamp), + (13, 'follow', 1, 2, null, null, null, current_timestamp), + (14, 'follow', 1, 2, null, null, null, current_timestamp), + (15, 'follow', 1, 2, null, null, null, current_timestamp), + (16, 'follow', 1, 2, null, null, null, current_timestamp), + (17, 'follow', 1, 2, null, null, null, current_timestamp), + (18, 'follow', 1, 2, null, null, null, current_timestamp), + (19, 'follow', 1, 2, null, null, null, current_timestamp), + (20, 'follow', 1, 2, null, null, null, current_timestamp), + (21, 'follow', 1, 2, null, null, null, current_timestamp), + (22, 'follow', 1, 2, null, null, null, current_timestamp), + (23, 'follow', 1, 2, null, null, null, current_timestamp), + (24, 'follow', 1, 2, null, null, null, current_timestamp), + (25, 'follow', 1, 2, null, null, null, current_timestamp), + (26, 'follow', 1, 2, null, null, null, current_timestamp), + (27, 'follow', 1, 2, null, null, null, current_timestamp), + (28, 'follow', 1, 2, null, null, null, current_timestamp), + (29, 'follow', 1, 2, null, null, null, current_timestamp), + (30, 'follow', 1, 2, null, null, null, current_timestamp), + (31, 'follow', 1, 2, null, null, null, current_timestamp), + (32, 'follow', 1, 2, null, null, null, current_timestamp), + (33, 'follow', 1, 2, null, null, null, current_timestamp), + (34, 'follow', 1, 2, null, null, null, current_timestamp), + (35, 'follow', 1, 2, null, null, null, current_timestamp), + (36, 'follow', 1, 2, null, null, null, current_timestamp), + (37, 'follow', 1, 2, null, null, null, current_timestamp), + (38, 'follow', 1, 2, null, null, null, current_timestamp), + (39, 'follow', 1, 2, null, null, null, current_timestamp), + (40, 'follow', 1, 2, null, null, null, current_timestamp), + (41, 'follow', 1, 2, null, null, null, current_timestamp), + (42, 'follow', 1, 2, null, null, null, current_timestamp), + (43, 'follow', 1, 2, null, null, null, current_timestamp), + (44, 'follow', 1, 2, null, null, null, current_timestamp), + (45, 'follow', 1, 2, null, null, null, current_timestamp), + (46, 'follow', 1, 2, null, null, null, current_timestamp), + (47, 'follow', 1, 2, null, null, null, current_timestamp), + (48, 'follow', 1, 2, null, null, null, current_timestamp), + (49, 'follow', 1, 2, null, null, null, current_timestamp), + (50, 'follow', 1, 2, null, null, null, current_timestamp), + (51, 'follow', 1, 2, null, null, null, current_timestamp), + (52, 'follow', 1, 2, null, null, null, current_timestamp), + (53, 'follow', 1, 2, null, null, null, current_timestamp), + (54, 'follow', 1, 2, null, null, null, current_timestamp), + (55, 'follow', 1, 2, null, null, null, current_timestamp), + (56, 'follow', 1, 2, null, null, null, current_timestamp), + (57, 'follow', 1, 2, null, null, null, current_timestamp), + (58, 'follow', 1, 2, null, null, null, current_timestamp), + (59, 'follow', 1, 2, null, null, null, current_timestamp), + (60, 'follow', 1, 2, null, null, null, current_timestamp), + (61, 'follow', 1, 2, null, null, null, current_timestamp), + (62, 'follow', 1, 2, null, null, null, current_timestamp), + (63, 'follow', 1, 2, null, null, null, current_timestamp), + (64, 'follow', 1, 2, null, null, null, current_timestamp), + (65, 'follow', 1, 2, null, null, null, current_timestamp); + +-- insert into From 637b2b84d5d1e97f10455aa61cdafb541051237d Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 31 Jan 2024 00:45:42 +0900 Subject: [PATCH 29/42] =?UTF-8?q?test:=20Mastodon=E4=BA=92=E6=8F=9BAPI?= =?UTF-8?q?=E3=81=AE=E9=80=9A=E7=9F=A5=E3=82=A8=E3=83=B3=E3=83=89=E3=83=9D?= =?UTF-8?q?=E3=82=A4=E3=83=B3=E3=83=88=E3=81=AE=E3=83=9A=E3=83=BC=E3=82=B8?= =?UTF-8?q?=E3=83=B3=E3=82=B0=E3=83=86=E3=82=B9=E3=83=88=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notifications/NotificationsTest.kt | 113 ++++++++++++++++++ .../sql/notification/test-notifications.sql | 67 ++++++++++- 2 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 src/intTest/kotlin/mastodon/notifications/NotificationsTest.kt diff --git a/src/intTest/kotlin/mastodon/notifications/NotificationsTest.kt b/src/intTest/kotlin/mastodon/notifications/NotificationsTest.kt new file mode 100644 index 00000000..4dbabab7 --- /dev/null +++ b/src/intTest/kotlin/mastodon/notifications/NotificationsTest.kt @@ -0,0 +1,113 @@ +package mastodon.notifications + +import dev.usbharu.hideout.SpringApplication +import kotlinx.coroutines.test.runTest +import org.flywaydb.core.Flyway +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.security.core.authority.SimpleGrantedAuthority +import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors +import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers +import org.springframework.test.context.jdbc.Sql +import org.springframework.test.web.servlet.MockMvc +import org.springframework.test.web.servlet.get +import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder +import org.springframework.test.web.servlet.setup.MockMvcBuilders +import org.springframework.transaction.annotation.Transactional +import org.springframework.web.context.WebApplicationContext + +@SpringBootTest(classes = [SpringApplication::class], properties = ["hideout.use-mongodb=false"]) +@AutoConfigureMockMvc +@Transactional +@Sql("/sql/test-user.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) +@Sql("/sql/test-user2.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) +@Sql("/sql/notification/test-notifications.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) +class NotificationsTest { + @Autowired + private lateinit var context: WebApplicationContext + + private lateinit var mockMvc: MockMvc + + @Test + fun `通知を取得できる`() = runTest { + mockMvc + .get("/api/v1/notifications") { + with( + SecurityMockMvcRequestPostProcessors.jwt() + .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) + ) + } + .asyncDispatch() + .andExpect { + header { + string( + "Link", + "; rel=\"next\", ; rel=\"prev\"" + ) + } + } + + } + + @Test + fun maxIdを指定して通知を取得できる() = runTest { + mockMvc + .get("/api/v1/notifications?max_id=26") { + with( + SecurityMockMvcRequestPostProcessors.jwt() + .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) + ) + } + .asyncDispatch() + .andExpect { + header { + string( + "Link", + "; rel=\"next\", ; rel=\"prev\"" + ) + } + } + + } + + @Test + fun minIdを指定して通知を取得できる() = runTest { + mockMvc + .get("/api/v1/notifications?min_id=25") { + with( + SecurityMockMvcRequestPostProcessors.jwt() + .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) + ) + } + .asyncDispatch() + .andExpect { + header { + string( + "Link", + "; rel=\"next\", ; rel=\"prev\"" + ) + } + } + + } + + @BeforeEach + fun setUp() { + mockMvc = MockMvcBuilders.webAppContextSetup(context) + .apply(SecurityMockMvcConfigurers.springSecurity()) + .build() + } + + companion object { + @JvmStatic + @AfterAll + fun dropDatabase(@Autowired flyway: Flyway) { + flyway.clean() + flyway.migrate() + } + } +} \ No newline at end of file diff --git a/src/intTest/resources/sql/notification/test-notifications.sql b/src/intTest/resources/sql/notification/test-notifications.sql index 2107cc3e..99620742 100644 --- a/src/intTest/resources/sql/notification/test-notifications.sql +++ b/src/intTest/resources/sql/notification/test-notifications.sql @@ -65,4 +65,69 @@ VALUES (1, 'follow', 1, 2, null, null, null, current_timestamp), (64, 'follow', 1, 2, null, null, null, current_timestamp), (65, 'follow', 1, 2, null, null, null, current_timestamp); --- insert into +insert into mastodon_notifications (id, user_id, type, created_at, account_id, status_id, report_id, relationship_serverance_event_id) +values (1, 1, 'follow', current_timestamp, 2, null, null, null), + (2, 1, 'follow', current_timestamp, 2, null, null, null), + (3, 1, 'follow', current_timestamp, 2, null, null, null), + (4, 1, 'follow', current_timestamp, 2, null, null, null), + (5, 1, 'follow', current_timestamp, 2, null, null, null), + (6, 1, 'follow', current_timestamp, 2, null, null, null), + (7, 1, 'follow', current_timestamp, 2, null, null, null), + (8, 1, 'follow', current_timestamp, 2, null, null, null), + (9, 1, 'follow', current_timestamp, 2, null, null, null), + (10, 1, 'follow', current_timestamp, 2, null, null, null), + (11, 1, 'follow', current_timestamp, 2, null, null, null), + (12, 1, 'follow', current_timestamp, 2, null, null, null), + (13, 1, 'follow', current_timestamp, 2, null, null, null), + (14, 1, 'follow', current_timestamp, 2, null, null, null), + (15, 1, 'follow', current_timestamp, 2, null, null, null), + (16, 1, 'follow', current_timestamp, 2, null, null, null), + (17, 1, 'follow', current_timestamp, 2, null, null, null), + (18, 1, 'follow', current_timestamp, 2, null, null, null), + (19, 1, 'follow', current_timestamp, 2, null, null, null), + (20, 1, 'follow', current_timestamp, 2, null, null, null), + (21, 1, 'follow', current_timestamp, 2, null, null, null), + (22, 1, 'follow', current_timestamp, 2, null, null, null), + (23, 1, 'follow', current_timestamp, 2, null, null, null), + (24, 1, 'follow', current_timestamp, 2, null, null, null), + (25, 1, 'follow', current_timestamp, 2, null, null, null), + (26, 1, 'follow', current_timestamp, 2, null, null, null), + (27, 1, 'follow', current_timestamp, 2, null, null, null), + (28, 1, 'follow', current_timestamp, 2, null, null, null), + (29, 1, 'follow', current_timestamp, 2, null, null, null), + (30, 1, 'follow', current_timestamp, 2, null, null, null), + (31, 1, 'follow', current_timestamp, 2, null, null, null), + (32, 1, 'follow', current_timestamp, 2, null, null, null), + (33, 1, 'follow', current_timestamp, 2, null, null, null), + (34, 1, 'follow', current_timestamp, 2, null, null, null), + (35, 1, 'follow', current_timestamp, 2, null, null, null), + (36, 1, 'follow', current_timestamp, 2, null, null, null), + (37, 1, 'follow', current_timestamp, 2, null, null, null), + (38, 1, 'follow', current_timestamp, 2, null, null, null), + (39, 1, 'follow', current_timestamp, 2, null, null, null), + (40, 1, 'follow', current_timestamp, 2, null, null, null), + (41, 1, 'follow', current_timestamp, 2, null, null, null), + (42, 1, 'follow', current_timestamp, 2, null, null, null), + (43, 1, 'follow', current_timestamp, 2, null, null, null), + (44, 1, 'follow', current_timestamp, 2, null, null, null), + (45, 1, 'follow', current_timestamp, 2, null, null, null), + (46, 1, 'follow', current_timestamp, 2, null, null, null), + (47, 1, 'follow', current_timestamp, 2, null, null, null), + (48, 1, 'follow', current_timestamp, 2, null, null, null), + (49, 1, 'follow', current_timestamp, 2, null, null, null), + (50, 1, 'follow', current_timestamp, 2, null, null, null), + (51, 1, 'follow', current_timestamp, 2, null, null, null), + (52, 1, 'follow', current_timestamp, 2, null, null, null), + (53, 1, 'follow', current_timestamp, 2, null, null, null), + (54, 1, 'follow', current_timestamp, 2, null, null, null), + (55, 1, 'follow', current_timestamp, 2, null, null, null), + (56, 1, 'follow', current_timestamp, 2, null, null, null), + (57, 1, 'follow', current_timestamp, 2, null, null, null), + (58, 1, 'follow', current_timestamp, 2, null, null, null), + (59, 1, 'follow', current_timestamp, 2, null, null, null), + (60, 1, 'follow', current_timestamp, 2, null, null, null), + (61, 1, 'follow', current_timestamp, 2, null, null, null), + (62, 1, 'follow', current_timestamp, 2, null, null, null), + (63, 1, 'follow', current_timestamp, 2, null, null, null), + (64, 1, 'follow', current_timestamp, 2, null, null, null), + (65, 1, 'follow', current_timestamp, 2, null, null, null); \ No newline at end of file From 44bca9d373c730ade3631cf8ef3ea2360f643530 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 31 Jan 2024 00:46:12 +0900 Subject: [PATCH 30/42] =?UTF-8?q?fix:=20=E3=83=9A=E3=83=BC=E3=82=B8?= =?UTF-8?q?=E3=83=B3=E3=82=B0=E3=81=AE=E3=83=AC=E3=82=B9=E3=83=9D=E3=83=B3?= =?UTF-8?q?=E3=82=B9=E3=81=8C=E9=96=93=E9=81=95=E3=81=A3=E3=81=A6=E3=81=84?= =?UTF-8?q?=E3=81=9F=E3=81=AE=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/notification/MastodonNotificationApiController.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/notification/MastodonNotificationApiController.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/notification/MastodonNotificationApiController.kt index 88d06682..0ec66d0e 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/notification/MastodonNotificationApiController.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/notification/MastodonNotificationApiController.kt @@ -48,8 +48,8 @@ class MastodonNotificationApiController( ) val httpHeader = notifications.toHttpHeader( - { "${applicationConfig.url}/api/v1/notifications?max_id=$it" }, - { "${applicationConfig.url}/api/v1/notifications?min_id=$it" } + { "${applicationConfig.url}/api/v1/notifications?min_id=$it" }, + { "${applicationConfig.url}/api/v1/notifications?max_id=$it" } ) ?: return@runBlocking ResponseEntity.ok( notifications.asFlow() ) From 48974d71faa44fc6598de021d2dc0523ebf0e284 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 31 Jan 2024 14:39:35 +0900 Subject: [PATCH 31/42] =?UTF-8?q?chore:=20Jackson=E3=81=8CRequired?= =?UTF-8?q?=E3=81=A0=E3=81=8Cnullable=E3=82=92=E7=90=86=E8=A7=A3=E3=81=97?= =?UTF-8?q?=E3=81=AA=E3=81=84=E3=81=AE=E3=81=A7=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/openapi/mastodon.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/resources/openapi/mastodon.yaml b/src/main/resources/openapi/mastodon.yaml index c628586e..45675b63 100644 --- a/src/main/resources/openapi/mastodon.yaml +++ b/src/main/resources/openapi/mastodon.yaml @@ -1051,7 +1051,6 @@ components: - group - discoverable - created_at - - last_status_at - statuses_count - followers_count - followers_count From 75a66ee2d3ee8799aead6967de5d0c1aecea0b69 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 31 Jan 2024 14:40:11 +0900 Subject: [PATCH 32/42] =?UTF-8?q?test:=20=E9=80=9A=E7=9F=A5API=E3=81=A8?= =?UTF-8?q?=E3=83=9A=E3=83=BC=E3=82=B8=E3=83=8D=E3=83=BC=E3=82=B7=E3=83=A7?= =?UTF-8?q?=E3=83=B3=E3=81=AE=E3=83=AC=E3=82=B9=E3=83=9D=E3=83=B3=E3=82=B9?= =?UTF-8?q?=E3=83=81=E3=82=A7=E3=83=83=E3=82=AF=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notifications/NotificationsTest.kt | 56 ++++++++++++++++++- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/src/intTest/kotlin/mastodon/notifications/NotificationsTest.kt b/src/intTest/kotlin/mastodon/notifications/NotificationsTest.kt index 4dbabab7..282a9605 100644 --- a/src/intTest/kotlin/mastodon/notifications/NotificationsTest.kt +++ b/src/intTest/kotlin/mastodon/notifications/NotificationsTest.kt @@ -1,7 +1,11 @@ package mastodon.notifications +import com.fasterxml.jackson.core.type.TypeReference +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import dev.usbharu.hideout.SpringApplication +import dev.usbharu.hideout.domain.mastodon.model.generated.Notification import kotlinx.coroutines.test.runTest +import org.assertj.core.api.Assertions.assertThat import org.flywaydb.core.Flyway import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.BeforeEach @@ -34,7 +38,7 @@ class NotificationsTest { @Test fun `通知を取得できる`() = runTest { - mockMvc + val content = mockMvc .get("/api/v1/notifications") { with( SecurityMockMvcRequestPostProcessors.jwt() @@ -50,12 +54,19 @@ class NotificationsTest { ) } } + .andReturn() + .response + .contentAsString + val value = jacksonObjectMapper().readValue(content, object : TypeReference>() {}) + + assertThat(value.first().id).isEqualTo("65") + assertThat(value.last().id).isEqualTo("26") } @Test fun maxIdを指定して通知を取得できる() = runTest { - mockMvc + val content = mockMvc .get("/api/v1/notifications?max_id=26") { with( SecurityMockMvcRequestPostProcessors.jwt() @@ -71,12 +82,20 @@ class NotificationsTest { ) } } + .andReturn() + .response + .contentAsString + + val value = jacksonObjectMapper().readValue(content, object : TypeReference>() {}) + + assertThat(value.first().id).isEqualTo("25") + assertThat(value.last().id).isEqualTo("1") } @Test fun minIdを指定して通知を取得できる() = runTest { - mockMvc + val content = mockMvc .get("/api/v1/notifications?min_id=25") { with( SecurityMockMvcRequestPostProcessors.jwt() @@ -92,7 +111,38 @@ class NotificationsTest { ) } } + .andReturn() + .response + .contentAsString + val value = jacksonObjectMapper().readValue(content, object : TypeReference>() {}) + + assertThat(value.first().id).isEqualTo("65") + assertThat(value.last().id).isEqualTo("26") + } + + @Test + fun 結果が0件のときはページネーションのヘッダーがない() = runTest { + val content = mockMvc + .get("/api/v1/notifications?max_id=1") { + with( + SecurityMockMvcRequestPostProcessors.jwt() + .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) + ) + } + .asyncDispatch() + .andExpect { + header { + doesNotExist("Link") + } + } + .andReturn() + .response + .contentAsString + + val value = jacksonObjectMapper().readValue(content, object : TypeReference>() {}) + + assertThat(value).size().isZero() } @BeforeEach From ca33eb344bb348b0f3ed5b9c1c3a32f146e7ce65 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 31 Jan 2024 15:01:07 +0900 Subject: [PATCH 33/42] =?UTF-8?q?test:=20Mongodb=E3=81=A7=E3=81=AE?= =?UTF-8?q?=E3=83=86=E3=82=B9=E3=83=88=E3=82=92=E8=A1=8C=E3=81=88=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E3=81=99=E3=82=8B=E3=81=9F=E3=82=81?= =?UTF-8?q?=E3=81=ABSQL=E3=82=92=E5=88=86=E5=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ... ExposedNotificationsApiPaginationTest.kt} | 3 +- src/intTest/resources/application.yml | 2 +- .../test-mastodon_notifications.sql | 66 ++++++++++++++++++ .../sql/notification/test-notifications.sql | 69 +------------------ 4 files changed, 70 insertions(+), 70 deletions(-) rename src/intTest/kotlin/mastodon/notifications/{NotificationsTest.kt => ExposedNotificationsApiPaginationTest.kt} (97%) create mode 100644 src/intTest/resources/sql/notification/test-mastodon_notifications.sql diff --git a/src/intTest/kotlin/mastodon/notifications/NotificationsTest.kt b/src/intTest/kotlin/mastodon/notifications/ExposedNotificationsApiPaginationTest.kt similarity index 97% rename from src/intTest/kotlin/mastodon/notifications/NotificationsTest.kt rename to src/intTest/kotlin/mastodon/notifications/ExposedNotificationsApiPaginationTest.kt index 282a9605..22fd7224 100644 --- a/src/intTest/kotlin/mastodon/notifications/NotificationsTest.kt +++ b/src/intTest/kotlin/mastodon/notifications/ExposedNotificationsApiPaginationTest.kt @@ -30,7 +30,8 @@ import org.springframework.web.context.WebApplicationContext @Sql("/sql/test-user.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) @Sql("/sql/test-user2.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) @Sql("/sql/notification/test-notifications.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) -class NotificationsTest { +@Sql("/sql/notification/test-mastodon_notifications.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) +class ExposedNotificationsApiPaginationTest { @Autowired private lateinit var context: WebApplicationContext diff --git a/src/intTest/resources/application.yml b/src/intTest/resources/application.yml index 51622edd..da6769aa 100644 --- a/src/intTest/resources/application.yml +++ b/src/intTest/resources/application.yml @@ -24,7 +24,7 @@ spring: auto-index-creation: true host: localhost port: 27017 - database: hideout + database: hideout-integration-test h2: console: enabled: true diff --git a/src/intTest/resources/sql/notification/test-mastodon_notifications.sql b/src/intTest/resources/sql/notification/test-mastodon_notifications.sql new file mode 100644 index 00000000..c97a25a7 --- /dev/null +++ b/src/intTest/resources/sql/notification/test-mastodon_notifications.sql @@ -0,0 +1,66 @@ +insert into mastodon_notifications (id, user_id, type, created_at, account_id, status_id, report_id, relationship_serverance_event_id) +values (1, 1, 'follow', current_timestamp, 2, null, null, null), + (2, 1, 'follow', current_timestamp, 2, null, null, null), + (3, 1, 'follow', current_timestamp, 2, null, null, null), + (4, 1, 'follow', current_timestamp, 2, null, null, null), + (5, 1, 'follow', current_timestamp, 2, null, null, null), + (6, 1, 'follow', current_timestamp, 2, null, null, null), + (7, 1, 'follow', current_timestamp, 2, null, null, null), + (8, 1, 'follow', current_timestamp, 2, null, null, null), + (9, 1, 'follow', current_timestamp, 2, null, null, null), + (10, 1, 'follow', current_timestamp, 2, null, null, null), + (11, 1, 'follow', current_timestamp, 2, null, null, null), + (12, 1, 'follow', current_timestamp, 2, null, null, null), + (13, 1, 'follow', current_timestamp, 2, null, null, null), + (14, 1, 'follow', current_timestamp, 2, null, null, null), + (15, 1, 'follow', current_timestamp, 2, null, null, null), + (16, 1, 'follow', current_timestamp, 2, null, null, null), + (17, 1, 'follow', current_timestamp, 2, null, null, null), + (18, 1, 'follow', current_timestamp, 2, null, null, null), + (19, 1, 'follow', current_timestamp, 2, null, null, null), + (20, 1, 'follow', current_timestamp, 2, null, null, null), + (21, 1, 'follow', current_timestamp, 2, null, null, null), + (22, 1, 'follow', current_timestamp, 2, null, null, null), + (23, 1, 'follow', current_timestamp, 2, null, null, null), + (24, 1, 'follow', current_timestamp, 2, null, null, null), + (25, 1, 'follow', current_timestamp, 2, null, null, null), + (26, 1, 'follow', current_timestamp, 2, null, null, null), + (27, 1, 'follow', current_timestamp, 2, null, null, null), + (28, 1, 'follow', current_timestamp, 2, null, null, null), + (29, 1, 'follow', current_timestamp, 2, null, null, null), + (30, 1, 'follow', current_timestamp, 2, null, null, null), + (31, 1, 'follow', current_timestamp, 2, null, null, null), + (32, 1, 'follow', current_timestamp, 2, null, null, null), + (33, 1, 'follow', current_timestamp, 2, null, null, null), + (34, 1, 'follow', current_timestamp, 2, null, null, null), + (35, 1, 'follow', current_timestamp, 2, null, null, null), + (36, 1, 'follow', current_timestamp, 2, null, null, null), + (37, 1, 'follow', current_timestamp, 2, null, null, null), + (38, 1, 'follow', current_timestamp, 2, null, null, null), + (39, 1, 'follow', current_timestamp, 2, null, null, null), + (40, 1, 'follow', current_timestamp, 2, null, null, null), + (41, 1, 'follow', current_timestamp, 2, null, null, null), + (42, 1, 'follow', current_timestamp, 2, null, null, null), + (43, 1, 'follow', current_timestamp, 2, null, null, null), + (44, 1, 'follow', current_timestamp, 2, null, null, null), + (45, 1, 'follow', current_timestamp, 2, null, null, null), + (46, 1, 'follow', current_timestamp, 2, null, null, null), + (47, 1, 'follow', current_timestamp, 2, null, null, null), + (48, 1, 'follow', current_timestamp, 2, null, null, null), + (49, 1, 'follow', current_timestamp, 2, null, null, null), + (50, 1, 'follow', current_timestamp, 2, null, null, null), + (51, 1, 'follow', current_timestamp, 2, null, null, null), + (52, 1, 'follow', current_timestamp, 2, null, null, null), + (53, 1, 'follow', current_timestamp, 2, null, null, null), + (54, 1, 'follow', current_timestamp, 2, null, null, null), + (55, 1, 'follow', current_timestamp, 2, null, null, null), + (56, 1, 'follow', current_timestamp, 2, null, null, null), + (57, 1, 'follow', current_timestamp, 2, null, null, null), + (58, 1, 'follow', current_timestamp, 2, null, null, null), + (59, 1, 'follow', current_timestamp, 2, null, null, null), + (60, 1, 'follow', current_timestamp, 2, null, null, null), + (61, 1, 'follow', current_timestamp, 2, null, null, null), + (62, 1, 'follow', current_timestamp, 2, null, null, null), + (63, 1, 'follow', current_timestamp, 2, null, null, null), + (64, 1, 'follow', current_timestamp, 2, null, null, null), + (65, 1, 'follow', current_timestamp, 2, null, null, null); \ No newline at end of file diff --git a/src/intTest/resources/sql/notification/test-notifications.sql b/src/intTest/resources/sql/notification/test-notifications.sql index 99620742..38982603 100644 --- a/src/intTest/resources/sql/notification/test-notifications.sql +++ b/src/intTest/resources/sql/notification/test-notifications.sql @@ -63,71 +63,4 @@ VALUES (1, 'follow', 1, 2, null, null, null, current_timestamp), (62, 'follow', 1, 2, null, null, null, current_timestamp), (63, 'follow', 1, 2, null, null, null, current_timestamp), (64, 'follow', 1, 2, null, null, null, current_timestamp), - (65, 'follow', 1, 2, null, null, null, current_timestamp); - -insert into mastodon_notifications (id, user_id, type, created_at, account_id, status_id, report_id, relationship_serverance_event_id) -values (1, 1, 'follow', current_timestamp, 2, null, null, null), - (2, 1, 'follow', current_timestamp, 2, null, null, null), - (3, 1, 'follow', current_timestamp, 2, null, null, null), - (4, 1, 'follow', current_timestamp, 2, null, null, null), - (5, 1, 'follow', current_timestamp, 2, null, null, null), - (6, 1, 'follow', current_timestamp, 2, null, null, null), - (7, 1, 'follow', current_timestamp, 2, null, null, null), - (8, 1, 'follow', current_timestamp, 2, null, null, null), - (9, 1, 'follow', current_timestamp, 2, null, null, null), - (10, 1, 'follow', current_timestamp, 2, null, null, null), - (11, 1, 'follow', current_timestamp, 2, null, null, null), - (12, 1, 'follow', current_timestamp, 2, null, null, null), - (13, 1, 'follow', current_timestamp, 2, null, null, null), - (14, 1, 'follow', current_timestamp, 2, null, null, null), - (15, 1, 'follow', current_timestamp, 2, null, null, null), - (16, 1, 'follow', current_timestamp, 2, null, null, null), - (17, 1, 'follow', current_timestamp, 2, null, null, null), - (18, 1, 'follow', current_timestamp, 2, null, null, null), - (19, 1, 'follow', current_timestamp, 2, null, null, null), - (20, 1, 'follow', current_timestamp, 2, null, null, null), - (21, 1, 'follow', current_timestamp, 2, null, null, null), - (22, 1, 'follow', current_timestamp, 2, null, null, null), - (23, 1, 'follow', current_timestamp, 2, null, null, null), - (24, 1, 'follow', current_timestamp, 2, null, null, null), - (25, 1, 'follow', current_timestamp, 2, null, null, null), - (26, 1, 'follow', current_timestamp, 2, null, null, null), - (27, 1, 'follow', current_timestamp, 2, null, null, null), - (28, 1, 'follow', current_timestamp, 2, null, null, null), - (29, 1, 'follow', current_timestamp, 2, null, null, null), - (30, 1, 'follow', current_timestamp, 2, null, null, null), - (31, 1, 'follow', current_timestamp, 2, null, null, null), - (32, 1, 'follow', current_timestamp, 2, null, null, null), - (33, 1, 'follow', current_timestamp, 2, null, null, null), - (34, 1, 'follow', current_timestamp, 2, null, null, null), - (35, 1, 'follow', current_timestamp, 2, null, null, null), - (36, 1, 'follow', current_timestamp, 2, null, null, null), - (37, 1, 'follow', current_timestamp, 2, null, null, null), - (38, 1, 'follow', current_timestamp, 2, null, null, null), - (39, 1, 'follow', current_timestamp, 2, null, null, null), - (40, 1, 'follow', current_timestamp, 2, null, null, null), - (41, 1, 'follow', current_timestamp, 2, null, null, null), - (42, 1, 'follow', current_timestamp, 2, null, null, null), - (43, 1, 'follow', current_timestamp, 2, null, null, null), - (44, 1, 'follow', current_timestamp, 2, null, null, null), - (45, 1, 'follow', current_timestamp, 2, null, null, null), - (46, 1, 'follow', current_timestamp, 2, null, null, null), - (47, 1, 'follow', current_timestamp, 2, null, null, null), - (48, 1, 'follow', current_timestamp, 2, null, null, null), - (49, 1, 'follow', current_timestamp, 2, null, null, null), - (50, 1, 'follow', current_timestamp, 2, null, null, null), - (51, 1, 'follow', current_timestamp, 2, null, null, null), - (52, 1, 'follow', current_timestamp, 2, null, null, null), - (53, 1, 'follow', current_timestamp, 2, null, null, null), - (54, 1, 'follow', current_timestamp, 2, null, null, null), - (55, 1, 'follow', current_timestamp, 2, null, null, null), - (56, 1, 'follow', current_timestamp, 2, null, null, null), - (57, 1, 'follow', current_timestamp, 2, null, null, null), - (58, 1, 'follow', current_timestamp, 2, null, null, null), - (59, 1, 'follow', current_timestamp, 2, null, null, null), - (60, 1, 'follow', current_timestamp, 2, null, null, null), - (61, 1, 'follow', current_timestamp, 2, null, null, null), - (62, 1, 'follow', current_timestamp, 2, null, null, null), - (63, 1, 'follow', current_timestamp, 2, null, null, null), - (64, 1, 'follow', current_timestamp, 2, null, null, null), - (65, 1, 'follow', current_timestamp, 2, null, null, null); \ No newline at end of file + (65, 'follow', 1, 2, null, null, null, current_timestamp); \ No newline at end of file From 9859c360516e43e3eee03befe59e2476168bcd91 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 31 Jan 2024 15:01:30 +0900 Subject: [PATCH 34/42] =?UTF-8?q?test:=20Mongodb=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E6=99=82=E3=81=AE=E9=80=9A=E7=9F=A5API=E3=81=AE=E3=83=9A?= =?UTF-8?q?=E3=83=BC=E3=82=B8=E3=83=8D=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3?= =?UTF-8?q?=E3=81=AE=E3=83=86=E3=82=B9=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MongodbNotificationsApiPaginationTest.kt | 196 ++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 src/intTest/kotlin/mastodon/notifications/MongodbNotificationsApiPaginationTest.kt diff --git a/src/intTest/kotlin/mastodon/notifications/MongodbNotificationsApiPaginationTest.kt b/src/intTest/kotlin/mastodon/notifications/MongodbNotificationsApiPaginationTest.kt new file mode 100644 index 00000000..f8eb566e --- /dev/null +++ b/src/intTest/kotlin/mastodon/notifications/MongodbNotificationsApiPaginationTest.kt @@ -0,0 +1,196 @@ +package mastodon.notifications + +import com.fasterxml.jackson.core.type.TypeReference +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import dev.usbharu.hideout.SpringApplication +import dev.usbharu.hideout.domain.mastodon.model.generated.Notification +import dev.usbharu.hideout.mastodon.domain.model.MastodonNotification +import dev.usbharu.hideout.mastodon.domain.model.NotificationType +import dev.usbharu.hideout.mastodon.infrastructure.mongorepository.MongoMastodonNotificationRepository +import kotlinx.coroutines.test.runTest +import org.assertj.core.api.Assertions +import org.flywaydb.core.Flyway +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.data.mongodb.core.MongoTemplate +import org.springframework.security.core.authority.SimpleGrantedAuthority +import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors +import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers +import org.springframework.test.context.jdbc.Sql +import org.springframework.test.web.servlet.MockMvc +import org.springframework.test.web.servlet.get +import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder +import org.springframework.test.web.servlet.setup.MockMvcBuilders +import org.springframework.transaction.annotation.Transactional +import org.springframework.web.context.WebApplicationContext +import java.time.Instant + +@SpringBootTest(classes = [SpringApplication::class], properties = ["hideout.use-mongodb=true"]) +@AutoConfigureMockMvc +@Transactional +@Sql("/sql/test-user.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) +@Sql("/sql/test-user2.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) +@Sql("/sql/notification/test-notifications.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) +class MongodbNotificationsApiPaginationTest { + @Autowired + private lateinit var context: WebApplicationContext + + private lateinit var mockMvc: MockMvc + + @Test + fun `通知を取得できる`() = runTest { + val content = mockMvc + .get("/api/v1/notifications") { + with( + SecurityMockMvcRequestPostProcessors.jwt() + .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) + ) + } + .asyncDispatch() + .andDo { print() } + .andExpect { + header { + string( + "Link", + "; rel=\"next\", ; rel=\"prev\"" + ) + } + } + .andReturn() + .response + .contentAsString + + val value = jacksonObjectMapper().readValue(content, object : TypeReference>() {}) + + Assertions.assertThat(value.first().id).isEqualTo("65") + Assertions.assertThat(value.last().id).isEqualTo("26") + } + + @Test + fun maxIdを指定して通知を取得できる() = runTest { + val content = mockMvc + .get("/api/v1/notifications?max_id=26") { + with( + SecurityMockMvcRequestPostProcessors.jwt() + .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) + ) + } + .asyncDispatch() + .andExpect { + header { + string( + "Link", + "; rel=\"next\", ; rel=\"prev\"" + ) + } + } + .andReturn() + .response + .contentAsString + + val value = jacksonObjectMapper().readValue(content, object : TypeReference>() {}) + + Assertions.assertThat(value.first().id).isEqualTo("25") + Assertions.assertThat(value.last().id).isEqualTo("1") + + } + + @Test + fun minIdを指定して通知を取得できる() = runTest { + val content = mockMvc + .get("/api/v1/notifications?min_id=25") { + with( + SecurityMockMvcRequestPostProcessors.jwt() + .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) + ) + } + .asyncDispatch() + .andExpect { + header { + string( + "Link", + "; rel=\"next\", ; rel=\"prev\"" + ) + } + } + .andReturn() + .response + .contentAsString + + val value = jacksonObjectMapper().readValue(content, object : TypeReference>() {}) + + Assertions.assertThat(value.first().id).isEqualTo("65") + Assertions.assertThat(value.last().id).isEqualTo("26") + } + + @Test + fun 結果が0件のときはページネーションのヘッダーがない() = runTest { + val content = mockMvc + .get("/api/v1/notifications?max_id=1") { + with( + SecurityMockMvcRequestPostProcessors.jwt() + .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) + ) + } + .asyncDispatch() + .andExpect { + header { + doesNotExist("Link") + } + } + .andReturn() + .response + .contentAsString + + val value = jacksonObjectMapper().readValue(content, object : TypeReference>() {}) + + Assertions.assertThat(value).size().isZero() + } + + @BeforeEach + fun setUp() { + mockMvc = MockMvcBuilders.webAppContextSetup(context) + .apply(SecurityMockMvcConfigurers.springSecurity()) + .build() + } + + companion object { + @JvmStatic + @BeforeAll + fun setupMongodb( + @Autowired mongoMastodonNotificationRepository: MongoMastodonNotificationRepository + ) { + val notifications = (1..65).map { + MastodonNotification( + it.toLong(), + 1, + NotificationType.follow, + Instant.now(), + 2, + null, + null, + null + ) + } + + mongoMastodonNotificationRepository.saveAll(notifications) + } + + @JvmStatic + @AfterAll + fun dropDatabase( + @Autowired flyway: Flyway, + @Autowired mongodbMastodonNotificationRepository: MongoMastodonNotificationRepository + ) { + flyway.clean() + flyway.migrate() + + mongodbMastodonNotificationRepository.deleteAll() + } + } +} \ No newline at end of file From 4ed61efe76022c8b3988dfa53f3d04d91ea38d00 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 31 Jan 2024 15:01:59 +0900 Subject: [PATCH 35/42] =?UTF-8?q?fix:=20Mongodb=E3=81=AE=E9=80=9A=E7=9F=A5?= =?UTF-8?q?API=E3=81=8C=E3=81=8A=E3=81=8B=E3=81=97=E3=81=8F=E3=81=AA?= =?UTF-8?q?=E3=81=A3=E3=81=A6=E3=81=84=E3=81=9F=E3=81=AE=E3=82=92=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...goMastodonNotificationRepositoryWrapper.kt | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/mongorepository/MongoMastodonNotificationRepositoryWrapper.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/mongorepository/MongoMastodonNotificationRepositoryWrapper.kt index fae487d5..fb9593e6 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/mongorepository/MongoMastodonNotificationRepositoryWrapper.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/mongorepository/MongoMastodonNotificationRepositoryWrapper.kt @@ -36,22 +36,25 @@ class MongoMastodonNotificationRepositoryWrapper( ): PaginationList { val query = Query() - if (page.minId != null) { - page.minId?.let { query.addCriteria(Criteria.where("id").gt(it)) } - page.maxId?.let { query.addCriteria(Criteria.where("id").lt(it)) } - } else { - query.with(Sort.by(Sort.Direction.DESC, "createdAt")) - page.sinceId?.let { query.addCriteria(Criteria.where("id").gt(it)) } - page.maxId?.let { query.addCriteria(Criteria.where("id").lt(it)) } - } - page.limit?.let { query.limit(it) } - val mastodonNotifications = mongoTemplate.find(query, MastodonNotification::class.java) + val mastodonNotifications = if (page.minId != null) { + query.with(Sort.by(Sort.Direction.ASC, "id")) + page.minId?.let { query.addCriteria(Criteria.where("id").gt(it)) } + page.maxId?.let { query.addCriteria(Criteria.where("id").lt(it)) } + mongoTemplate.find(query, MastodonNotification::class.java).reversed() + } else { + query.with(Sort.by(Sort.Direction.DESC, "id")) + page.sinceId?.let { query.addCriteria(Criteria.where("id").gt(it)) } + page.maxId?.let { query.addCriteria(Criteria.where("id").lt(it)) } + mongoTemplate.find(query, MastodonNotification::class.java) + } + + return PaginationList( mastodonNotifications, - mastodonNotifications.lastOrNull()?.id, - mastodonNotifications.firstOrNull()?.id + mastodonNotifications.firstOrNull()?.id, + mastodonNotifications.lastOrNull()?.id ) } From a3518ddf5323c210ee133ee508317f4092767577 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 31 Jan 2024 15:05:00 +0900 Subject: [PATCH 36/42] =?UTF-8?q?test:=20=E3=83=86=E3=82=B9=E3=83=88?= =?UTF-8?q?=E5=AE=9F=E8=A1=8C=E5=89=8D=E3=81=ABMongodb=E3=82=92=E3=82=AF?= =?UTF-8?q?=E3=83=AA=E3=82=A2=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notifications/MongodbNotificationsApiPaginationTest.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/intTest/kotlin/mastodon/notifications/MongodbNotificationsApiPaginationTest.kt b/src/intTest/kotlin/mastodon/notifications/MongodbNotificationsApiPaginationTest.kt index f8eb566e..b2e6cff0 100644 --- a/src/intTest/kotlin/mastodon/notifications/MongodbNotificationsApiPaginationTest.kt +++ b/src/intTest/kotlin/mastodon/notifications/MongodbNotificationsApiPaginationTest.kt @@ -165,6 +165,9 @@ class MongodbNotificationsApiPaginationTest { fun setupMongodb( @Autowired mongoMastodonNotificationRepository: MongoMastodonNotificationRepository ) { + + mongoMastodonNotificationRepository.deleteAll() + val notifications = (1..65).map { MastodonNotification( it.toLong(), From 5d01be2d88ed244e1b3006cd22573d7cd90aa7e9 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 31 Jan 2024 16:43:22 +0900 Subject: [PATCH 37/42] =?UTF-8?q?chore:=20Jackson=E3=81=8Cnullable?= =?UTF-8?q?=E3=81=8B=E3=81=A4required=E3=82=92=E7=90=86=E8=A7=A3=E3=81=97?= =?UTF-8?q?=E3=81=AA=E3=81=84=E3=81=AE=E3=81=A7=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/openapi/mastodon.yaml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/resources/openapi/mastodon.yaml b/src/main/resources/openapi/mastodon.yaml index 45675b63..f4c5875b 100644 --- a/src/main/resources/openapi/mastodon.yaml +++ b/src/main/resources/openapi/mastodon.yaml @@ -573,6 +573,7 @@ paths: required: false schema: type: integer + nullable: true default: 20 - in: query name: only_media @@ -1278,6 +1279,7 @@ components: language: type: string nullable: true + default: null text: type: string nullable: true @@ -1315,11 +1317,7 @@ components: - favourites_count - replies_count - url - - in_reply_to_id - - in_reply_to_account_id - - language - text - - edited_at MediaAttachment: type: object From 8703a45fc2c9e0030b99c01f5b648d1ea7be9f75 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 31 Jan 2024 16:43:55 +0900 Subject: [PATCH 38/42] =?UTF-8?q?test:=20=E3=82=A2=E3=82=AB=E3=82=A6?= =?UTF-8?q?=E3=83=B3=E3=83=88=E3=81=AE=E6=8A=95=E7=A8=BF=E4=B8=80=E8=A6=A7?= =?UTF-8?q?=E3=81=AE=E5=8F=96=E5=BE=97=E3=83=86=E3=82=B9=E3=83=88=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../account/AccountApiPaginationTest.kt | 78 +++++++ .../sql/accounts/test-accounts-statuses.sql | 202 ++++++++++++++++++ 2 files changed, 280 insertions(+) create mode 100644 src/intTest/kotlin/mastodon/account/AccountApiPaginationTest.kt create mode 100644 src/intTest/resources/sql/accounts/test-accounts-statuses.sql diff --git a/src/intTest/kotlin/mastodon/account/AccountApiPaginationTest.kt b/src/intTest/kotlin/mastodon/account/AccountApiPaginationTest.kt new file mode 100644 index 00000000..e5cede90 --- /dev/null +++ b/src/intTest/kotlin/mastodon/account/AccountApiPaginationTest.kt @@ -0,0 +1,78 @@ +package mastodon.account + +import com.fasterxml.jackson.core.type.TypeReference +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import dev.usbharu.hideout.SpringApplication +import dev.usbharu.hideout.domain.mastodon.model.generated.Notification +import dev.usbharu.hideout.domain.mastodon.model.generated.Status +import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.assertThat +import org.flywaydb.core.Flyway +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.security.core.authority.SimpleGrantedAuthority +import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors +import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers +import org.springframework.test.context.jdbc.Sql +import org.springframework.test.web.servlet.MockMvc +import org.springframework.test.web.servlet.get +import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder +import org.springframework.test.web.servlet.setup.MockMvcBuilders +import org.springframework.transaction.annotation.Transactional +import org.springframework.web.context.WebApplicationContext + +@SpringBootTest(classes = [SpringApplication::class]) +@AutoConfigureMockMvc +@Transactional +@Sql("/sql/test-user.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) +@Sql("/sql/test-user2.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) +@Sql("/sql/accounts/test-accounts-statuses.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS) +class AccountApiPaginationTest { + @Autowired + private lateinit var context: WebApplicationContext + + private lateinit var mockMvc: MockMvc + + @Test + fun `apiV1AccountsIdStatusesGet 投稿を取得できる`() { + val content = mockMvc + .get("/api/v1/accounts/1/statuses"){ + with( + SecurityMockMvcRequestPostProcessors.jwt() + .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) + ) + } + .asyncDispatch() + .andExpect { status { isOk() } } + .andExpect { header { string("Link","; rel=\"next\", ; rel=\"prev\"") } } + .andReturn() + .response + .contentAsString + + val value = jacksonObjectMapper().readValue(content, object : TypeReference>() {}) + + Assertions.assertThat(value.first().id).isEqualTo("100") + Assertions.assertThat(value.last().id).isEqualTo("81") + assertThat(value).size().isEqualTo(20) + } + + @BeforeEach + fun setUp() { + mockMvc = MockMvcBuilders.webAppContextSetup(context) + .apply(SecurityMockMvcConfigurers.springSecurity()) + .build() + } + + companion object { + @JvmStatic + @AfterAll + fun dropDatabase(@Autowired flyway: Flyway) { + flyway.clean() + flyway.migrate() + } + } +} \ No newline at end of file diff --git a/src/intTest/resources/sql/accounts/test-accounts-statuses.sql b/src/intTest/resources/sql/accounts/test-accounts-statuses.sql new file mode 100644 index 00000000..10352e07 --- /dev/null +++ b/src/intTest/resources/sql/accounts/test-accounts-statuses.sql @@ -0,0 +1,202 @@ +insert into posts (id, actor_id, overview, content, text, created_at, visibility, url, repost_id, reply_id, sensitive, + ap_id, deleted) +VALUES (1, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/1', + null, null, false, 'https://example.com/users/1/posts/1', false), + (2, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/2', + null, 1, false, 'https://example.com/users/1/posts/2', false), + (3, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/3', + null, null, false, 'https://example.com/users/1/posts/3', false), + (4, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/4', + null, 3, false, 'https://example.com/users/1/posts/4', false), + (5, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/5', + null, null, false, 'https://example.com/users/1/posts/5', false), + (6, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/6', + null, null, false, 'https://example.com/users/1/posts/6', false), + (7, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/7', + null, null, false, 'https://example.com/users/1/posts/7', false), + (8, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/8', + null, 7, false, 'https://example.com/users/1/posts/8', false), + (9, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/9', + null, null, false, 'https://example.com/users/1/posts/9', false), + (10, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/10', + null, 9, false, 'https://example.com/users/1/posts/10', false), + (11, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/11', + null, null, false, 'https://example.com/users/1/posts/11', false), + (12, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/12', + null, null, false, 'https://example.com/users/1/posts/12', false), + (13, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/13', + null, null, false, 'https://example.com/users/1/posts/13', false), + (14, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/14', + null, 13, false, 'https://example.com/users/1/posts/14', false), + (15, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/15', + null, null, false, 'https://example.com/users/1/posts/15', false), + (16, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/16', + null, 15, false, 'https://example.com/users/1/posts/16', false), + (17, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/17', + null, null, false, 'https://example.com/users/1/posts/17', false), + (18, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/18', + null, null, false, 'https://example.com/users/1/posts/18', false), + (19, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/19', + null, null, false, 'https://example.com/users/1/posts/19', false), + (20, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/20', + null, 19, false, 'https://example.com/users/1/posts/20', false), + (21, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/21', + null, null, false, 'https://example.com/users/1/posts/21', false), + (22, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/22', + null, 21, false, 'https://example.com/users/1/posts/22', false), + (23, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/23', + null, null, false, 'https://example.com/users/1/posts/23', false), + (24, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/24', + null, null, false, 'https://example.com/users/1/posts/24', false), + (25, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/25', + null, null, false, 'https://example.com/users/1/posts/25', false), + (26, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/26', + null, 25, false, 'https://example.com/users/1/posts/26', false), + (27, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/27', + null, null, false, 'https://example.com/users/1/posts/27', false), + (28, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/28', + null, 27, false, 'https://example.com/users/1/posts/28', false), + (29, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/29', + null, null, false, 'https://example.com/users/1/posts/29', false), + (30, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/30', + null, null, false, 'https://example.com/users/1/posts/30', false), + (31, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/31', + null, null, false, 'https://example.com/users/1/posts/31', false), + (32, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/32', + null, 31, false, 'https://example.com/users/1/posts/32', false), + (33, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/33', + null, null, false, 'https://example.com/users/1/posts/33', false), + (34, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/34', + null, 33, false, 'https://example.com/users/1/posts/34', false), + (35, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/35', + null, null, false, 'https://example.com/users/1/posts/35', false), + (36, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/36', + null, null, false, 'https://example.com/users/1/posts/36', false), + (37, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/37', + null, null, false, 'https://example.com/users/1/posts/37', false), + (38, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/38', + null, 37, false, 'https://example.com/users/1/posts/38', false), + (39, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/39', + null, null, false, 'https://example.com/users/1/posts/39', false), + (40, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/40', + null, 39, false, 'https://example.com/users/1/posts/40', false), + (41, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/41', + null, null, false, 'https://example.com/users/1/posts/41', false), + (42, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/42', + null, null, false, 'https://example.com/users/1/posts/42', false), + (43, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/43', + null, null, false, 'https://example.com/users/1/posts/43', false), + (44, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/44', + null, 43, false, 'https://example.com/users/1/posts/44', false), + (45, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/45', + null, null, false, 'https://example.com/users/1/posts/45', false), + (46, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/46', + null, 45, false, 'https://example.com/users/1/posts/46', false), + (47, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/47', + null, null, false, 'https://example.com/users/1/posts/47', false), + (48, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/48', + null, null, false, 'https://example.com/users/1/posts/48', false), + (49, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/49', + null, null, false, 'https://example.com/users/1/posts/49', false), + (50, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/50', + null, 49, false, 'https://example.com/users/1/posts/50', false), + (51, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/51', + null, null, false, 'https://example.com/users/1/posts/51', false), + (52, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/52', + null, 51, false, 'https://example.com/users/1/posts/52', false), + (53, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/53', + null, null, false, 'https://example.com/users/1/posts/53', false), + (54, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/54', + null, null, false, 'https://example.com/users/1/posts/54', false), + (55, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/55', + null, null, false, 'https://example.com/users/1/posts/55', false), + (56, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/56', + null, 55, false, 'https://example.com/users/1/posts/56', false), + (57, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/57', + null, null, false, 'https://example.com/users/1/posts/57', false), + (58, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/58', + null, 57, false, 'https://example.com/users/1/posts/58', false), + (59, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/59', + null, null, false, 'https://example.com/users/1/posts/59', false), + (60, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/60', + null, null, false, 'https://example.com/users/1/posts/60', false), + (61, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/61', + null, null, false, 'https://example.com/users/1/posts/61', false), + (62, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/62', + null, 61, false, 'https://example.com/users/1/posts/62', false), + (63, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/63', + null, null, false, 'https://example.com/users/1/posts/63', false), + (64, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/64', + null, 63, false, 'https://example.com/users/1/posts/64', false), + (65, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/65', + null, null, false, 'https://example.com/users/1/posts/65', false), + (66, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/66', + null, null, false, 'https://example.com/users/1/posts/66', false), + (67, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/67', + null, null, false, 'https://example.com/users/1/posts/67', false), + (68, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/68', + null, 67, false, 'https://example.com/users/1/posts/68', false), + (69, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/69', + null, null, false, 'https://example.com/users/1/posts/69', false), + (70, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/70', + null, 69, false, 'https://example.com/users/1/posts/70', false), + (71, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/71', + null, null, false, 'https://example.com/users/1/posts/71', false), + (72, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/72', + null, null, false, 'https://example.com/users/1/posts/72', false), + (73, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/73', + null, null, false, 'https://example.com/users/1/posts/73', false), + (74, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/74', + null, 73, false, 'https://example.com/users/1/posts/74', false), + (75, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/75', + null, null, false, 'https://example.com/users/1/posts/75', false), + (76, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/76', + null, 75, false, 'https://example.com/users/1/posts/76', false), + (77, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/77', + null, null, false, 'https://example.com/users/1/posts/77', false), + (78, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/78', + null, null, false, 'https://example.com/users/1/posts/78', false), + (79, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/79', + null, null, false, 'https://example.com/users/1/posts/79', false), + (80, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/80', + null, 79, false, 'https://example.com/users/1/posts/80', false), + (81, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/81', + null, null, false, 'https://example.com/users/1/posts/81', false), + (82, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/82', + null, 81, false, 'https://example.com/users/1/posts/82', false), + (83, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/83', + null, null, false, 'https://example.com/users/1/posts/83', false), + (84, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/84', + null, null, false, 'https://example.com/users/1/posts/84', false), + (85, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/85', + null, null, false, 'https://example.com/users/1/posts/85', false), + (86, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/86', + null, 85, false, 'https://example.com/users/1/posts/86', false), + (87, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/87', + null, null, false, 'https://example.com/users/1/posts/87', false), + (88, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/88', + null, 87, false, 'https://example.com/users/1/posts/88', false), + (89, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/89', + null, null, false, 'https://example.com/users/1/posts/89', false), + (90, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/90', + null, null, false, 'https://example.com/users/1/posts/90', false), + (91, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/91', + null, null, false, 'https://example.com/users/1/posts/91', false), + (92, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/92', + null, 91, false, 'https://example.com/users/1/posts/92', false), + (93, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/93', + null, null, false, 'https://example.com/users/1/posts/93', false), + (94, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/94', + null, 93, false, 'https://example.com/users/1/posts/94', false), + (95, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/95', + null, null, false, 'https://example.com/users/1/posts/95', false), + (96, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/96', + null, null, false, 'https://example.com/users/1/posts/96', false), + (97, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/97', + null, null, false, 'https://example.com/users/1/posts/97', false), + (98, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/98', + null, 97, false, 'https://example.com/users/1/posts/98', false), + (99, 1, null, '

this is test

', 'this is test', 1706684146436, 2, 'https://example.com/users/1/posts/99', + null, null, false, 'https://example.com/users/1/posts/99', false), + (100, 1, null, '

this is test

', 'this is test', 1706684146436, 0, 'https://example.com/users/1/posts/100', + null, 99, false, 'https://example.com/users/1/posts/100', false); \ No newline at end of file From 8d24b366e46ed420a24c71c11c3a0fda0359f902 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 31 Jan 2024 16:44:20 +0900 Subject: [PATCH 39/42] =?UTF-8?q?fix:=20=E3=82=A2=E3=82=AB=E3=82=A6?= =?UTF-8?q?=E3=83=B3=E3=83=88=E3=81=AE=E6=8A=95=E7=A8=BF=E4=B8=80=E8=A6=A7?= =?UTF-8?q?=E3=81=AE=E5=8F=96=E5=BE=97=E3=81=AE=E3=83=90=E3=82=B0=E3=82=92?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../infrastructure/exposedquery/StatusQueryServiceImpl.kt | 8 +++++--- .../api/account/MastodonAccountApiController.kt | 2 +- .../hideout/mastodon/service/account/AccountApiService.kt | 2 ++ 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt index 86e4f9f4..07a8dbcb 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt @@ -101,8 +101,8 @@ class StatusQueryServiceImpl : StatusQueryService { val statuses = resolveReplyAndRepost(pairs) return PaginationList( statuses, - statuses.lastOrNull()?.id?.toLongOrNull(), - statuses.firstOrNull()?.id?.toLongOrNull() + statuses.firstOrNull()?.id?.toLongOrNull(), + statuses.lastOrNull()?.id?.toLongOrNull() ) } @@ -137,7 +137,9 @@ class StatusQueryServiceImpl : StatusQueryService { } .map { if (it.inReplyToId != null) { - it.copy(inReplyToAccountId = statuses.find { (id) -> id == it.inReplyToId }?.id) + println("statuses trace: $statuses") + println("inReplyToId trace: ${it.inReplyToId}") + it.copy(inReplyToAccountId = statuses.find { (id) -> id == it.inReplyToId }?.account?.id) } else { it } diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt index a32d1910..650199ad 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt @@ -88,8 +88,8 @@ class MastodonAccountApiController( ) ) val httpHeader = statuses.toHttpHeader( - { "${applicationConfig.url}/api/v1/accounts/$id/statuses?max_id=$it" }, { "${applicationConfig.url}/api/v1/accounts/$id/statuses?min_id=$it" }, + { "${applicationConfig.url}/api/v1/accounts/$id/statuses?max_id=$it" }, ) if (httpHeader != null) { diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt index 68c2781c..42bb0e87 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt @@ -87,6 +87,8 @@ class AccountApiServiceImpl( ): PaginationList { val canViewFollowers = if (loginUser == null) { false + }else if(loginUser == userid) { + true } else { transaction.transaction { isFollowing(loginUser, userid) From 40c2c93f89b1b2695a91bf3593e39e45d2c72887 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 31 Jan 2024 21:33:12 +0900 Subject: [PATCH 40/42] =?UTF-8?q?test:=20=E3=82=A2=E3=82=AB=E3=82=A6?= =?UTF-8?q?=E3=83=B3=E3=83=88=E3=81=AE=E6=8A=95=E7=A8=BF=E4=B8=80=E8=A6=A7?= =?UTF-8?q?=E3=81=AE=E5=8F=96=E5=BE=97=E3=81=AE=E3=83=9A=E3=83=BC=E3=82=B8?= =?UTF-8?q?=E3=83=8D=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=AE=E3=83=86?= =?UTF-8?q?=E3=82=B9=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../account/AccountApiPaginationTest.kt | 72 ++++++++++++++++++- 1 file changed, 70 insertions(+), 2 deletions(-) diff --git a/src/intTest/kotlin/mastodon/account/AccountApiPaginationTest.kt b/src/intTest/kotlin/mastodon/account/AccountApiPaginationTest.kt index e5cede90..39dd46ff 100644 --- a/src/intTest/kotlin/mastodon/account/AccountApiPaginationTest.kt +++ b/src/intTest/kotlin/mastodon/account/AccountApiPaginationTest.kt @@ -55,8 +55,76 @@ class AccountApiPaginationTest { val value = jacksonObjectMapper().readValue(content, object : TypeReference>() {}) - Assertions.assertThat(value.first().id).isEqualTo("100") - Assertions.assertThat(value.last().id).isEqualTo("81") + assertThat(value.first().id).isEqualTo("100") + assertThat(value.last().id).isEqualTo("81") + assertThat(value).size().isEqualTo(20) + } + + @Test + fun `apiV1AccountsIdStatusesGet 結果が0件のときはLinkヘッダーがない`() { + val content = mockMvc + .get("/api/v1/accounts/1/statuses?min_id=100"){ + with( + SecurityMockMvcRequestPostProcessors.jwt() + .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) + ) + } + .asyncDispatch() + .andExpect { status { isOk() } } + .andExpect { header { doesNotExist("Link") } } + .andReturn() + .response + .contentAsString + + val value = jacksonObjectMapper().readValue(content, object : TypeReference>() {}) + + + assertThat(value).isEmpty() + } + + @Test + fun `apiV1AccountsIdStatusesGet maxIdを指定して取得`() { + val content = mockMvc + .get("/api/v1/accounts/1/statuses?max_id=100"){ + with( + SecurityMockMvcRequestPostProcessors.jwt() + .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) + ) + } + .asyncDispatch() + .andExpect { status { isOk() } } + .andExpect { header { string("Link","; rel=\"next\", ; rel=\"prev\"") } } + .andReturn() + .response + .contentAsString + + val value = jacksonObjectMapper().readValue(content, object : TypeReference>() {}) + + assertThat(value.first().id).isEqualTo("99") + assertThat(value.last().id).isEqualTo("80") + assertThat(value).size().isEqualTo(20) + } + + @Test + fun `apiV1AccountsIdStatusesGet minIdを指定して取得`() { + val content = mockMvc + .get("/api/v1/accounts/1/statuses?min_id=1"){ + with( + SecurityMockMvcRequestPostProcessors.jwt() + .jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_read")) + ) + } + .asyncDispatch() + .andExpect { status { isOk() } } + .andExpect { header { string("Link","; rel=\"next\", ; rel=\"prev\"") } } + .andReturn() + .response + .contentAsString + + val value = jacksonObjectMapper().readValue(content, object : TypeReference>() {}) + + assertThat(value.first().id).isEqualTo("21") + assertThat(value.last().id).isEqualTo("2") assertThat(value).size().isEqualTo(20) } From 61e61ec4bd7768547d8ca1dc22773918657bd364 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 31 Jan 2024 21:56:28 +0900 Subject: [PATCH 41/42] =?UTF-8?q?fix:=20=E3=83=9A=E3=83=BC=E3=82=B8?= =?UTF-8?q?=E3=83=8D=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3=E5=AF=BE=E5=BF=9C?= =?UTF-8?q?=E5=BF=98=E3=82=8C=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/timeline/MastodonTimelineApiController.kt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/timeline/MastodonTimelineApiController.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/timeline/MastodonTimelineApiController.kt index aaec0252..994732c9 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/timeline/MastodonTimelineApiController.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/timeline/MastodonTimelineApiController.kt @@ -35,7 +35,15 @@ class MastodonTimelineApiController( limit = limit?.coerceIn(0, 80) ?: 40 ) ) - ResponseEntity(homeTimeline.asFlow(), HttpStatus.OK) + + val httpHeader = homeTimeline.toHttpHeader( + { "${applicationConfig.url}/api/v1/home?max_id=$it" }, + { "${applicationConfig.url}/api/v1/home?min_id=$it" } + ) ?: return@runBlocking ResponseEntity( + homeTimeline.asFlow(), + HttpStatus.OK + ) + ResponseEntity.ok().header("Link", httpHeader).body(homeTimeline.asFlow()) } override fun apiV1TimelinesPublicGet( From e5f620749e528396cef67c9dfc62a110365f025b Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 31 Jan 2024 22:17:07 +0900 Subject: [PATCH 42/42] style: fix lint --- .../activitypub/domain/model/Delete.kt | 12 ++--- .../hideout/activitypub/domain/model/Emoji.kt | 12 ++--- .../activitypub/domain/model/Follow.kt | 10 ++--- .../hideout/activitypub/domain/model/Note.kt | 24 +++++----- .../hideout/activitypub/domain/model/Undo.kt | 12 ++--- .../exposed/ExposedPaginationExtension.kt | 2 +- .../hideout/core/domain/model/actor/Actor.kt | 44 +++++++++---------- .../core/domain/model/emoji/CustomEmoji.kt | 6 +-- .../httpsignature/HttpSignatureUser.kt | 8 ++-- .../ExposedOAuth2AuthorizationService.kt | 2 +- .../springframework/oauth2/UserDetailsImpl.kt | 6 +-- .../core/service/media/MediaServiceImpl.kt | 2 +- .../image/ImageMediaProcessorConfiguration.kt | 2 +- .../ExposedGenerateTimelineService.kt | 1 - .../exposedquery/StatusQueryServiceImpl.kt | 2 +- ...goMastodonNotificationRepositoryWrapper.kt | 1 - .../interfaces/api/status/StatusesRequest.kt | 20 ++++----- .../mastodon/query/StatusQueryService.kt | 1 + .../service/account/AccountApiService.kt | 3 +- .../service/instance/InstanceApiService.kt | 2 +- .../dev/usbharu/hideout/util/LruCache.kt | 6 +-- 21 files changed, 89 insertions(+), 89 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Delete.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Delete.kt index 61ffb348..b19ac919 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Delete.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Delete.kt @@ -52,11 +52,11 @@ open class Delete : Object, HasId, HasActor { override fun toString(): String { return "Delete(" + - "apObject=$apObject, " + - "published='$published', " + - "actor='$actor', " + - "id='$id'" + - ")" + - " ${super.toString()}" + "apObject=$apObject, " + + "published='$published', " + + "actor='$actor', " + + "id='$id'" + + ")" + + " ${super.toString()}" } } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Emoji.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Emoji.kt index 2b9a0bee..8cd9b8f8 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Emoji.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Emoji.kt @@ -16,12 +16,12 @@ open class Emoji( override fun toString(): String { return "Emoji(" + - "name='$name', " + - "id='$id', " + - "updated='$updated', " + - "icon=$icon" + - ")" + - " ${super.toString()}" + "name='$name', " + + "id='$id', " + + "updated='$updated', " + + "icon=$icon" + + ")" + + " ${super.toString()}" } override fun equals(other: Any?): Boolean { diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Follow.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Follow.kt index 0216a2e3..35cd892c 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Follow.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Follow.kt @@ -36,10 +36,10 @@ open class Follow( override fun toString(): String { return "Follow(" + - "apObject='$apObject', " + - "actor='$actor', " + - "id=$id" + - ")" + - " ${super.toString()}" + "apObject='$apObject', " + + "actor='$actor', " + + "id=$id" + + ")" + + " ${super.toString()}" } } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Note.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Note.kt index b3383ef5..60e7858c 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Note.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Note.kt @@ -62,17 +62,17 @@ constructor( override fun toString(): String { return "Note(" + - "id='$id', " + - "attributedTo='$attributedTo', " + - "content='$content', " + - "published='$published', " + - "to=$to, " + - "cc=$cc, " + - "sensitive=$sensitive, " + - "inReplyTo=$inReplyTo, " + - "attachment=$attachment, " + - "tag=$tag" + - ")" + - " ${super.toString()}" + "id='$id', " + + "attributedTo='$attributedTo', " + + "content='$content', " + + "published='$published', " + + "to=$to, " + + "cc=$cc, " + + "sensitive=$sensitive, " + + "inReplyTo=$inReplyTo, " + + "attachment=$attachment, " + + "tag=$tag" + + ")" + + " ${super.toString()}" } } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Undo.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Undo.kt index 6f27026e..16555ae7 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Undo.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/Undo.kt @@ -40,11 +40,11 @@ open class Undo( override fun toString(): String { return "Undo(" + - "actor='$actor', " + - "id='$id', " + - "apObject=$apObject, " + - "published=$published" + - ")" + - " ${super.toString()}" + "actor='$actor', " + + "id='$id', " + + "apObject=$apObject, " + + "published=$published" + + ")" + + " ${super.toString()}" } } diff --git a/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtension.kt b/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtension.kt index 7b0b0c4f..53b3fe72 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtension.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/infrastructure/exposed/ExposedPaginationExtension.kt @@ -16,4 +16,4 @@ fun Query.withPagination(page: Page, exp: ExpressionWithColumnType): Pagi } return PaginationList(resultRows, resultRows.firstOrNull()?.getOrNull(exp), resultRows.lastOrNull()?.getOrNull(exp)) -} \ No newline at end of file +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt index 59ab16d3..5e8b4264 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/actor/Actor.kt @@ -212,27 +212,27 @@ data class Actor private constructor( fun withLastPostAt(lastPostDate: Instant): Actor = this.copy(lastPostDate = lastPostDate) override fun toString(): String { return "Actor(" + - "id=$id, " + - "name='$name', " + - "domain='$domain', " + - "screenName='$screenName', " + - "description='$description', " + - "inbox='$inbox', " + - "outbox='$outbox', " + - "url='$url', " + - "publicKey='$publicKey', " + - "privateKey=$privateKey, " + - "createdAt=$createdAt, " + - "keyId='$keyId', " + - "followers=$followers, " + - "following=$following, " + - "instance=$instance, " + - "locked=$locked, " + - "followersCount=$followersCount, " + - "followingCount=$followingCount, " + - "postsCount=$postsCount, " + - "lastPostDate=$lastPostDate, " + - "emojis=$emojis" + - ")" + "id=$id, " + + "name='$name', " + + "domain='$domain', " + + "screenName='$screenName', " + + "description='$description', " + + "inbox='$inbox', " + + "outbox='$outbox', " + + "url='$url', " + + "publicKey='$publicKey', " + + "privateKey=$privateKey, " + + "createdAt=$createdAt, " + + "keyId='$keyId', " + + "followers=$followers, " + + "following=$following, " + + "instance=$instance, " + + "locked=$locked, " + + "followersCount=$followersCount, " + + "followingCount=$followingCount, " + + "postsCount=$postsCount, " + + "lastPostDate=$lastPostDate, " + + "emojis=$emojis" + + ")" } } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmoji.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmoji.kt index fb4579aa..1e96ecb7 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmoji.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmoji.kt @@ -10,9 +10,9 @@ sealed class Emoji { abstract fun id(): String override fun toString(): String { return "Emoji(" + - "domain='$domain', " + - "name='$name'" + - ")" + "domain='$domain', " + + "name='$name'" + + ")" } } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUser.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUser.kt index 2d546af0..c946664d 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUser.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUser.kt @@ -41,10 +41,10 @@ class HttpSignatureUser( override fun toString(): String { return "HttpSignatureUser(" + - "domain='$domain', " + - "id=$id" + - ")" + - " ${super.toString()}" + "domain='$domain', " + + "id=$id" + + ")" + + " ${super.toString()}" } companion object { diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/ExposedOAuth2AuthorizationService.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/ExposedOAuth2AuthorizationService.kt index 34e72833..9be529c2 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/ExposedOAuth2AuthorizationService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/ExposedOAuth2AuthorizationService.kt @@ -273,7 +273,7 @@ class ExposedOAuth2AuthorizationService( oidcTokenIssuedAt, oidcTokenExpiresAt, oidcTokenMetadata.getValue(OAuth2Authorization.Token.CLAIMS_METADATA_NAME) - as MutableMap? + as MutableMap? ) builder.token(oidcIdToken) { it.putAll(oidcTokenMetadata) } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsImpl.kt index f41d3fe4..2cbd8b02 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsImpl.kt @@ -28,9 +28,9 @@ class UserDetailsImpl( ) : User(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities) { override fun toString(): String { return "UserDetailsImpl(" + - "id=$id" + - ")" + - " ${super.toString()}" + "id=$id" + + ")" + + " ${super.toString()}" } override fun equals(other: Any?): Boolean { diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaServiceImpl.kt index 6cc50463..4f57917f 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaServiceImpl.kt @@ -29,7 +29,7 @@ class MediaServiceImpl( val fileName = mediaRequest.file.name logger.info( "Media upload. filename:$fileName " + - "contentType:${mediaRequest.file.contentType}" + "contentType:${mediaRequest.file.contentType}" ) val tempFile = Files.createTempFile("hideout-tmp-file", ".tmp") diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/image/ImageMediaProcessorConfiguration.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/image/ImageMediaProcessorConfiguration.kt index 23eeadb7..eb6c5537 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/image/ImageMediaProcessorConfiguration.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/media/converter/image/ImageMediaProcessorConfiguration.kt @@ -8,7 +8,7 @@ data class ImageMediaProcessorConfiguration( val thubnail: ImageMediaProcessorThumbnailConfiguration?, val supportedType: List?, - ) +) data class ImageMediaProcessorThumbnailConfiguration( val generate: Boolean, diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/ExposedGenerateTimelineService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/ExposedGenerateTimelineService.kt index b45300d5..229abc9c 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/ExposedGenerateTimelineService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/timeline/ExposedGenerateTimelineService.kt @@ -32,7 +32,6 @@ class ExposedGenerateTimelineService(private val statusQueryService: StatusQuery } val result = query.withPagination(page, Timelines.id) - val statusQueries = result.map { StatusQuery( it[Timelines.postId], diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt index 07a8dbcb..2b9f3676 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/StatusQueryServiceImpl.kt @@ -87,7 +87,7 @@ class StatusQueryServiceImpl : StatusQueryService { } val pairs = query - .withPagination(page,Posts.id) + .withPagination(page, Posts.id) .groupBy { it[Posts.id] } .map { it.value } .map { diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/mongorepository/MongoMastodonNotificationRepositoryWrapper.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/mongorepository/MongoMastodonNotificationRepositoryWrapper.kt index fb9593e6..17dc9ba3 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/mongorepository/MongoMastodonNotificationRepositoryWrapper.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/mongorepository/MongoMastodonNotificationRepositoryWrapper.kt @@ -50,7 +50,6 @@ class MongoMastodonNotificationRepositoryWrapper( mongoTemplate.find(query, MastodonNotification::class.java) } - return PaginationList( mastodonNotifications, mastodonNotifications.firstOrNull()?.id, diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/status/StatusesRequest.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/status/StatusesRequest.kt index 0a9fac65..11b06068 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/status/StatusesRequest.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/status/StatusesRequest.kt @@ -66,16 +66,16 @@ class StatusesRequest { override fun toString(): String { return "StatusesRequest(" + - "status=$status, " + - "media_ids=$media_ids, " + - "poll=$poll, " + - "in_reply_to_id=$in_reply_to_id, " + - "sensitive=$sensitive, " + - "spoiler_text=$spoiler_text, " + - "visibility=$visibility, " + - "language=$language, " + - "scheduled_at=$scheduled_at" + - ")" + "status=$status, " + + "media_ids=$media_ids, " + + "poll=$poll, " + + "in_reply_to_id=$in_reply_to_id, " + + "sensitive=$sensitive, " + + "spoiler_text=$spoiler_text, " + + "visibility=$visibility, " + + "language=$language, " + + "scheduled_at=$scheduled_at" + + ")" } @Suppress("EnumNaming", "EnumEntryNameCase") diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/query/StatusQueryService.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/query/StatusQueryService.kt index c17a49a7..4ac2252b 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/query/StatusQueryService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/query/StatusQueryService.kt @@ -9,6 +9,7 @@ interface StatusQueryService { suspend fun findByPostIds(ids: List): List suspend fun findByPostIdsWithMediaIds(statusQueries: List): List + @Suppress("LongParameterList") suspend fun accountsStatus( accountId: Long, onlyMedia: Boolean = false, diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt index 42bb0e87..5ec3d733 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt @@ -20,6 +20,7 @@ import kotlin.math.min @Suppress("TooManyFunctions") interface AccountApiService { + @Suppress("ongParameterList") suspend fun accountsStatuses( userid: Long, onlyMedia: Boolean, @@ -87,7 +88,7 @@ class AccountApiServiceImpl( ): PaginationList { val canViewFollowers = if (loginUser == null) { false - }else if(loginUser == userid) { + } else if (loginUser == userid) { true } else { transaction.transaction { diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/instance/InstanceApiService.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/instance/InstanceApiService.kt index 2abc1169..b719cd6b 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/instance/InstanceApiService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/instance/InstanceApiService.kt @@ -19,7 +19,7 @@ class InstanceApiServiceImpl(private val applicationConfig: ApplicationConfig) : title = "Hideout Server", shortDescription = "Hideout test server", description = "This server is operated for testing of Hideout." + - " We are not responsible for any events that occur when associating with this server", + " We are not responsible for any events that occur when associating with this server", email = "i@usbharu.dev", version = "0.0.1", urls = V1InstanceUrls("wss://${url.host}"), diff --git a/src/main/kotlin/dev/usbharu/hideout/util/LruCache.kt b/src/main/kotlin/dev/usbharu/hideout/util/LruCache.kt index 77c891da..223063db 100644 --- a/src/main/kotlin/dev/usbharu/hideout/util/LruCache.kt +++ b/src/main/kotlin/dev/usbharu/hideout/util/LruCache.kt @@ -7,9 +7,9 @@ class LruCache(private val maxSize: Int) : LinkedHashMap(15, 0.75f, override fun removeEldestEntry(eldest: MutableMap.MutableEntry?): Boolean = size > maxSize override fun toString(): String { return "LruCache(" + - "maxSize=$maxSize" + - ")" + - " ${super.toString()}" + "maxSize=$maxSize" + + ")" + + " ${super.toString()}" } companion object {