refactor: Timeline APIをPagination APIに対応

This commit is contained in:
usbharu 2024-01-29 20:28:46 +09:00
parent 7141c7bc6a
commit b63288ff28
5 changed files with 150 additions and 10 deletions

View File

@ -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<Status, Long> {
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()
)
}
}

View File

@ -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<Status>
suspend fun getTimeline(
forUserId: Long? = null,
localOnly: Boolean = false,
mediaOnly: Boolean = false,
page: Page
): PaginationList<Status, Long>
}

View File

@ -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<Status, Long> {
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()
)
}
}

View File

@ -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<Flow<Status>> = 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())
}
}

View File

@ -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<Status>
suspend fun publicTimeline(
localOnly: Boolean = false,
remoteOnly: Boolean = false,
mediaOnly: Boolean = false,
page: Page
): PaginationList<Status, Long>
suspend fun homeTimeline(
userId: Long,
maxId: Long?,
@ -24,6 +33,11 @@ interface TimelineApiService {
sinceId: Long?,
limit: Int = 20
): List<Status>
suspend fun homeTimeline(
userId: Long,
page: Page
): PaginationList<Status, Long>
}
@Service
@ -51,6 +65,13 @@ class TimelineApiServiceImpl(
)
}
override suspend fun publicTimeline(
localOnly: Boolean,
remoteOnly: Boolean,
mediaOnly: Boolean,
page: Page
): PaginationList<Status, Long> = 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<Status, Long> =
generateTimelineService.getTimeline(forUserId = userId, page = page)
}