mirror of https://github.com/usbharu/Hideout.git
feat: タイムラインを読めるように
This commit is contained in:
parent
60d71e1eb2
commit
88a61ba97f
|
@ -1,14 +1,12 @@
|
||||||
package dev.usbharu.hideout.core.application.post
|
package dev.usbharu.hideout.core.application.post
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.domain.model.actor.Actor
|
import dev.usbharu.hideout.core.domain.model.actor.Actor
|
||||||
import dev.usbharu.hideout.core.domain.model.instance.Instance
|
|
||||||
import dev.usbharu.hideout.core.domain.model.media.Media
|
import dev.usbharu.hideout.core.domain.model.media.Media
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
|
|
||||||
data class ActorDetail(
|
data class ActorDetail(
|
||||||
val actorId: Long,
|
val actorId: Long,
|
||||||
val instanceId: Long,
|
val instanceId: Long,
|
||||||
val instanceName: String,
|
|
||||||
val name: String,
|
val name: String,
|
||||||
val domain: String,
|
val domain: String,
|
||||||
val screenName: String,
|
val screenName: String,
|
||||||
|
@ -17,11 +15,10 @@ data class ActorDetail(
|
||||||
val icon: URI?,
|
val icon: URI?,
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
fun of(actor: Actor, instance: Instance, iconMedia: Media?): ActorDetail {
|
fun of(actor: Actor, iconMedia: Media?): ActorDetail {
|
||||||
return ActorDetail(
|
return ActorDetail(
|
||||||
actor.id.id,
|
actor.id.id,
|
||||||
actor.instance.instanceId,
|
actor.instance.instanceId,
|
||||||
instance.name.name,
|
|
||||||
actor.name.name,
|
actor.name.name,
|
||||||
actor.domain.domain,
|
actor.domain.domain,
|
||||||
actor.screenName.screenName,
|
actor.screenName.screenName,
|
||||||
|
|
|
@ -5,8 +5,6 @@ import dev.usbharu.hideout.core.application.shared.AbstractApplicationService
|
||||||
import dev.usbharu.hideout.core.application.shared.Transaction
|
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||||
import dev.usbharu.hideout.core.domain.model.actor.Actor
|
import dev.usbharu.hideout.core.domain.model.actor.Actor
|
||||||
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
|
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
|
||||||
import dev.usbharu.hideout.core.domain.model.instance.Instance
|
|
||||||
import dev.usbharu.hideout.core.domain.model.instance.InstanceRepository
|
|
||||||
import dev.usbharu.hideout.core.domain.model.media.Media
|
import dev.usbharu.hideout.core.domain.model.media.Media
|
||||||
import dev.usbharu.hideout.core.domain.model.media.MediaRepository
|
import dev.usbharu.hideout.core.domain.model.media.MediaRepository
|
||||||
import dev.usbharu.hideout.core.domain.model.post.PostId
|
import dev.usbharu.hideout.core.domain.model.post.PostId
|
||||||
|
@ -21,7 +19,6 @@ class GetPostDetailApplicationService(
|
||||||
transaction: Transaction,
|
transaction: Transaction,
|
||||||
private val postRepository: PostRepository,
|
private val postRepository: PostRepository,
|
||||||
private val actorRepository: ActorRepository,
|
private val actorRepository: ActorRepository,
|
||||||
private val instanceRepository: InstanceRepository,
|
|
||||||
private val mediaRepository: MediaRepository,
|
private val mediaRepository: MediaRepository,
|
||||||
private val iPostReadAccessControl: IPostReadAccessControl
|
private val iPostReadAccessControl: IPostReadAccessControl
|
||||||
) : AbstractApplicationService<GetPostDetail, PostDetail>(
|
) : AbstractApplicationService<GetPostDetail, PostDetail>(
|
||||||
|
@ -36,8 +33,6 @@ class GetPostDetailApplicationService(
|
||||||
}
|
}
|
||||||
val actor =
|
val actor =
|
||||||
actorRepository.findById(post.actorId) ?: throw InternalServerException("Actor ${post.actorId} not found.")
|
actorRepository.findById(post.actorId) ?: throw InternalServerException("Actor ${post.actorId} not found.")
|
||||||
val instance = instanceRepository.findById(post.instanceId)
|
|
||||||
?: throw InternalServerException("Instance ${post.instanceId} not found.")
|
|
||||||
|
|
||||||
val iconMedia = actor.icon?.let { mediaRepository.findById(it) }
|
val iconMedia = actor.icon?.let { mediaRepository.findById(it) }
|
||||||
|
|
||||||
|
@ -46,19 +41,17 @@ class GetPostDetailApplicationService(
|
||||||
return PostDetail.of(
|
return PostDetail.of(
|
||||||
post,
|
post,
|
||||||
actor,
|
actor,
|
||||||
instance,
|
|
||||||
iconMedia,
|
iconMedia,
|
||||||
mediaList,
|
mediaList,
|
||||||
post.replyId?.let { fetchChild(it, actor, instance, iconMedia, principal) },
|
post.replyId?.let { fetchChild(it, actor, iconMedia, principal) },
|
||||||
post.repostId?.let { fetchChild(it, actor, instance, iconMedia, principal) },
|
post.repostId?.let { fetchChild(it, actor, iconMedia, principal) },
|
||||||
post.moveTo?.let { fetchChild(it, actor, instance, iconMedia, principal) },
|
post.moveTo?.let { fetchChild(it, actor, iconMedia, principal) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun fetchChild(
|
private suspend fun fetchChild(
|
||||||
postId: PostId,
|
postId: PostId,
|
||||||
actor: Actor,
|
actor: Actor,
|
||||||
instance: Instance,
|
|
||||||
iconMedia: Media?,
|
iconMedia: Media?,
|
||||||
principal: Principal
|
principal: Principal
|
||||||
): PostDetail? {
|
): PostDetail? {
|
||||||
|
@ -68,21 +61,16 @@ class GetPostDetailApplicationService(
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
val (first, second: Instance, third) = if (actor.id != post.actorId) {
|
val (first, third) = if (actor.id != post.actorId) {
|
||||||
Triple(
|
(actorRepository.findById(post.actorId) ?: return null) to actor.icon?.let { mediaRepository.findById(it) }
|
||||||
actorRepository.findById(post.actorId) ?: return null,
|
|
||||||
instanceRepository.findById(actor.instance) ?: return null,
|
|
||||||
actor.icon?.let { mediaRepository.findById(it) }
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
Triple(actor, instance, iconMedia)
|
actor to iconMedia
|
||||||
}
|
}
|
||||||
|
|
||||||
val mediaList = mediaRepository.findByIds(post.mediaIds)
|
val mediaList = mediaRepository.findByIds(post.mediaIds)
|
||||||
return PostDetail.of(
|
return PostDetail.of(
|
||||||
post,
|
post,
|
||||||
first,
|
first,
|
||||||
second,
|
|
||||||
third,
|
third,
|
||||||
mediaList
|
mediaList
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package dev.usbharu.hideout.core.application.post
|
package dev.usbharu.hideout.core.application.post
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.domain.model.actor.Actor
|
import dev.usbharu.hideout.core.domain.model.actor.Actor
|
||||||
import dev.usbharu.hideout.core.domain.model.instance.Instance
|
|
||||||
import dev.usbharu.hideout.core.domain.model.media.Media
|
import dev.usbharu.hideout.core.domain.model.media.Media
|
||||||
import dev.usbharu.hideout.core.domain.model.post.Post
|
import dev.usbharu.hideout.core.domain.model.post.Post
|
||||||
import dev.usbharu.hideout.core.domain.model.post.Visibility
|
import dev.usbharu.hideout.core.domain.model.post.Visibility
|
||||||
|
@ -30,7 +29,6 @@ data class PostDetail(
|
||||||
fun of(
|
fun of(
|
||||||
post: Post,
|
post: Post,
|
||||||
actor: Actor,
|
actor: Actor,
|
||||||
instance: Instance,
|
|
||||||
iconMedia: Media?,
|
iconMedia: Media?,
|
||||||
mediaList: List<Media>,
|
mediaList: List<Media>,
|
||||||
reply: PostDetail? = null,
|
reply: PostDetail? = null,
|
||||||
|
@ -39,7 +37,7 @@ data class PostDetail(
|
||||||
): PostDetail {
|
): PostDetail {
|
||||||
return PostDetail(
|
return PostDetail(
|
||||||
id = post.id.id,
|
id = post.id.id,
|
||||||
actor = ActorDetail.of(actor, instance, iconMedia),
|
actor = ActorDetail.of(actor, iconMedia),
|
||||||
overview = post.overview?.overview,
|
overview = post.overview?.overview,
|
||||||
text = post.text,
|
text = post.text,
|
||||||
content = post.content.content,
|
content = post.content.content,
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
package dev.usbharu.hideout.core.application.timeline
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.core.domain.model.support.page.Page
|
||||||
|
|
||||||
|
data class ReadTimeline(
|
||||||
|
val timelineId: Long,
|
||||||
|
val mediaOnly: Boolean,
|
||||||
|
val localOnly: Boolean,
|
||||||
|
val remoteOnly: Boolean,
|
||||||
|
val page: Page
|
||||||
|
)
|
|
@ -0,0 +1,47 @@
|
||||||
|
package dev.usbharu.hideout.core.application.timeline
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.core.application.shared.AbstractApplicationService
|
||||||
|
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||||
|
import dev.usbharu.hideout.core.domain.model.post.PostId
|
||||||
|
import dev.usbharu.hideout.core.domain.model.support.page.PaginationList
|
||||||
|
import dev.usbharu.hideout.core.domain.model.support.principal.Principal
|
||||||
|
import dev.usbharu.hideout.core.domain.model.support.timelineobjectdetail.TimelineObjectDetail
|
||||||
|
import dev.usbharu.hideout.core.domain.model.timeline.TimelineId
|
||||||
|
import dev.usbharu.hideout.core.domain.model.timeline.TimelineRepository
|
||||||
|
import dev.usbharu.hideout.core.external.timeline.ReadTimelineOption
|
||||||
|
import dev.usbharu.hideout.core.external.timeline.TimelineStore
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class ReadTimelineApplicationService(
|
||||||
|
private val timelineStore: TimelineStore,
|
||||||
|
private val timelineRepository: TimelineRepository,
|
||||||
|
transaction: Transaction
|
||||||
|
) :
|
||||||
|
AbstractApplicationService<ReadTimeline, PaginationList<TimelineObjectDetail, PostId>>(transaction, logger) {
|
||||||
|
override suspend fun internalExecute(
|
||||||
|
command: ReadTimeline,
|
||||||
|
principal: Principal
|
||||||
|
): PaginationList<TimelineObjectDetail, PostId> {
|
||||||
|
val findById = timelineRepository.findById(TimelineId(command.timelineId))
|
||||||
|
?: throw IllegalArgumentException("Timeline ${command.timelineId} not found.")
|
||||||
|
|
||||||
|
val readTimelineOption = ReadTimelineOption(
|
||||||
|
command.mediaOnly,
|
||||||
|
command.localOnly,
|
||||||
|
command.remoteOnly
|
||||||
|
)
|
||||||
|
|
||||||
|
return timelineStore.readTimeline(
|
||||||
|
findById,
|
||||||
|
readTimelineOption,
|
||||||
|
command.page,
|
||||||
|
principal,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val logger = LoggerFactory.getLogger(ReadTimelineApplicationService::class.java)
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,6 +22,18 @@ interface RelationshipRepository {
|
||||||
suspend fun save(relationship: Relationship): Relationship
|
suspend fun save(relationship: Relationship): Relationship
|
||||||
suspend fun delete(relationship: Relationship)
|
suspend fun delete(relationship: Relationship)
|
||||||
suspend fun findByActorIdAndTargetId(actorId: ActorId, targetId: ActorId): Relationship?
|
suspend fun findByActorIdAndTargetId(actorId: ActorId, targetId: ActorId): Relationship?
|
||||||
|
suspend fun findByActorIdsAndTargetIdAndBlocking(
|
||||||
|
actorIds: List<ActorId>,
|
||||||
|
targetId: ActorId,
|
||||||
|
blocking: Boolean
|
||||||
|
): List<Relationship>
|
||||||
|
|
||||||
|
suspend fun findByActorIdAndTargetIdsAndFollowing(
|
||||||
|
actorId: ActorId,
|
||||||
|
targetIds: List<ActorId>,
|
||||||
|
following: Boolean
|
||||||
|
): List<Relationship>
|
||||||
|
|
||||||
suspend fun findByTargetId(
|
suspend fun findByTargetId(
|
||||||
targetId: ActorId,
|
targetId: ActorId,
|
||||||
option: FindRelationshipOption? = null,
|
option: FindRelationshipOption? = null,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package dev.usbharu.hideout.core.domain.model.support.timelineobjectdetail
|
package dev.usbharu.hideout.core.domain.model.support.timelineobjectdetail
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.domain.model.actor.Actor
|
import dev.usbharu.hideout.core.domain.model.actor.Actor
|
||||||
|
import dev.usbharu.hideout.core.domain.model.media.Media
|
||||||
import dev.usbharu.hideout.core.domain.model.post.Post
|
import dev.usbharu.hideout.core.domain.model.post.Post
|
||||||
import dev.usbharu.hideout.core.domain.model.post.PostId
|
import dev.usbharu.hideout.core.domain.model.post.PostId
|
||||||
import dev.usbharu.hideout.core.domain.model.timelineobject.TimelineObject
|
import dev.usbharu.hideout.core.domain.model.timelineobject.TimelineObject
|
||||||
|
@ -14,11 +15,17 @@ data class TimelineObjectDetail(
|
||||||
val postId: PostId,
|
val postId: PostId,
|
||||||
val timelineUserDetail: UserDetail,
|
val timelineUserDetail: UserDetail,
|
||||||
val post: Post,
|
val post: Post,
|
||||||
|
val postMedias: List<Media>,
|
||||||
val postActor: Actor,
|
val postActor: Actor,
|
||||||
|
val postActorIconMedia: Media?,
|
||||||
val replyPost: Post?,
|
val replyPost: Post?,
|
||||||
|
val replyPostMedias: List<Media>?,
|
||||||
val replyPostActor: Actor?,
|
val replyPostActor: Actor?,
|
||||||
|
val replyPostActorIconMedia: Media?,
|
||||||
val repostPost: Post?,
|
val repostPost: Post?,
|
||||||
|
val repostPostMedias: List<Media>?,
|
||||||
val repostPostActor: Actor?,
|
val repostPostActor: Actor?,
|
||||||
|
val repostPostActorIconMedia: Media?,
|
||||||
val isPureRepost: Boolean,
|
val isPureRepost: Boolean,
|
||||||
val lastUpdateAt: Instant,
|
val lastUpdateAt: Instant,
|
||||||
val hasMediaInRepost: Boolean,
|
val hasMediaInRepost: Boolean,
|
||||||
|
@ -29,27 +36,39 @@ data class TimelineObjectDetail(
|
||||||
timelineObject: TimelineObject,
|
timelineObject: TimelineObject,
|
||||||
timelineUserDetail: UserDetail,
|
timelineUserDetail: UserDetail,
|
||||||
post: Post,
|
post: Post,
|
||||||
|
postMedias: List<Media>,
|
||||||
postActor: Actor,
|
postActor: Actor,
|
||||||
|
postActorIconMedia: Media?,
|
||||||
replyPost: Post?,
|
replyPost: Post?,
|
||||||
|
replyPostMedias: List<Media>?,
|
||||||
replyPostActor: Actor?,
|
replyPostActor: Actor?,
|
||||||
|
replyPostActorIconMedia: Media?,
|
||||||
repostPost: Post?,
|
repostPost: Post?,
|
||||||
|
repostPostMedias: List<Media>?,
|
||||||
repostPostActor: Actor?,
|
repostPostActor: Actor?,
|
||||||
|
repostPostActorIconMedia: Media?,
|
||||||
warnFilter: List<TimelineObjectWarnFilter>
|
warnFilter: List<TimelineObjectWarnFilter>
|
||||||
): TimelineObjectDetail {
|
): TimelineObjectDetail {
|
||||||
return TimelineObjectDetail(
|
return TimelineObjectDetail(
|
||||||
timelineObject.id,
|
id = timelineObject.id,
|
||||||
post.id,
|
postId = post.id,
|
||||||
timelineUserDetail,
|
timelineUserDetail = timelineUserDetail,
|
||||||
post,
|
post = post,
|
||||||
postActor,
|
postMedias = postMedias,
|
||||||
replyPost,
|
postActor = postActor,
|
||||||
replyPostActor,
|
postActorIconMedia = postActorIconMedia,
|
||||||
repostPost,
|
replyPost = replyPost,
|
||||||
repostPostActor,
|
replyPostMedias = replyPostMedias,
|
||||||
timelineObject.isPureRepost,
|
replyPostActor = replyPostActor,
|
||||||
timelineObject.lastUpdatedAt,
|
replyPostActorIconMedia = replyPostActorIconMedia,
|
||||||
timelineObject.hasMediaInRepost,
|
repostPost = repostPost,
|
||||||
warnFilter
|
repostPostMedias = repostPostMedias,
|
||||||
|
repostPostActor = repostPostActor,
|
||||||
|
repostPostActorIconMedia = repostPostActorIconMedia,
|
||||||
|
isPureRepost = timelineObject.isPureRepost,
|
||||||
|
lastUpdateAt = timelineObject.lastUpdatedAt,
|
||||||
|
hasMediaInRepost = timelineObject.hasMediaInRepost,
|
||||||
|
warnFilter = warnFilter
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import org.springframework.stereotype.Component
|
||||||
|
|
||||||
interface IPostReadAccessControl {
|
interface IPostReadAccessControl {
|
||||||
suspend fun isAllow(post: Post, principal: Principal): Boolean
|
suspend fun isAllow(post: Post, principal: Principal): Boolean
|
||||||
|
suspend fun areAllows(postList: List<Post>, principal: Principal): List<Post>
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
|
@ -22,9 +23,9 @@ class DefaultPostReadAccessControl(private val relationshipRepository: Relations
|
||||||
}
|
}
|
||||||
|
|
||||||
val relationship = (
|
val relationship = (
|
||||||
relationshipRepository.findByActorIdAndTargetId(post.actorId, principal.actorId)
|
relationshipRepository.findByActorIdAndTargetId(post.actorId, principal.actorId)
|
||||||
?: Relationship.default(post.actorId, principal.actorId)
|
?: Relationship.default(post.actorId, principal.actorId)
|
||||||
)
|
)
|
||||||
|
|
||||||
// ブロックされてたら見れない
|
// ブロックされてたら見れない
|
||||||
if (relationship.blocking) {
|
if (relationship.blocking) {
|
||||||
|
@ -57,4 +58,47 @@ class DefaultPostReadAccessControl(private val relationshipRepository: Relations
|
||||||
// その他の場合は見れない
|
// その他の場合は見れない
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun areAllows(postList: List<Post>, principal: Principal): List<Post> {
|
||||||
|
val actorIds = postList.map { it.actorId }
|
||||||
|
val relationshipList =
|
||||||
|
relationshipRepository.findByActorIdsAndTargetIdAndBlocking(actorIds, principal.actorId, true)
|
||||||
|
.map { it.actorId }
|
||||||
|
val inverseRelationshipList =
|
||||||
|
relationshipRepository.findByActorIdAndTargetIdsAndFollowing(principal.actorId, actorIds, true)
|
||||||
|
.map { it.actorId }
|
||||||
|
|
||||||
|
fun internalAllow(post: Post): Boolean {
|
||||||
|
// ポスト主は無条件で見れる
|
||||||
|
if (post.actorId == principal.actorId) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (relationshipList.contains(post.actorId)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (post.visibility == Visibility.PUBLIC || post.visibility == Visibility.UNLISTED) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (principal is Anonymous) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (post.visibility == Visibility.DIRECT && post.visibleActors.contains(principal.actorId)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (post.visibility == Visibility.FOLLOWERS && inverseRelationshipList.contains(principal.actorId)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return postList
|
||||||
|
.filter {
|
||||||
|
internalAllow(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import dev.usbharu.hideout.core.domain.model.post.Post
|
||||||
import dev.usbharu.hideout.core.domain.model.post.PostId
|
import dev.usbharu.hideout.core.domain.model.post.PostId
|
||||||
import dev.usbharu.hideout.core.domain.model.support.page.Page
|
import dev.usbharu.hideout.core.domain.model.support.page.Page
|
||||||
import dev.usbharu.hideout.core.domain.model.support.page.PaginationList
|
import dev.usbharu.hideout.core.domain.model.support.page.PaginationList
|
||||||
|
import dev.usbharu.hideout.core.domain.model.support.principal.Principal
|
||||||
import dev.usbharu.hideout.core.domain.model.support.timelineobjectdetail.TimelineObjectDetail
|
import dev.usbharu.hideout.core.domain.model.support.timelineobjectdetail.TimelineObjectDetail
|
||||||
import dev.usbharu.hideout.core.domain.model.timeline.Timeline
|
import dev.usbharu.hideout.core.domain.model.timeline.Timeline
|
||||||
import dev.usbharu.hideout.core.domain.model.timelinerelationship.TimelineRelationship
|
import dev.usbharu.hideout.core.domain.model.timelinerelationship.TimelineRelationship
|
||||||
|
@ -22,6 +23,7 @@ interface TimelineStore {
|
||||||
suspend fun readTimeline(
|
suspend fun readTimeline(
|
||||||
timeline: Timeline,
|
timeline: Timeline,
|
||||||
option: ReadTimelineOption? = null,
|
option: ReadTimelineOption? = null,
|
||||||
page: Page? = null
|
page: Page? = null,
|
||||||
|
principal: Principal
|
||||||
): PaginationList<TimelineObjectDetail, PostId>
|
): PaginationList<TimelineObjectDetail, PostId>
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,6 +67,26 @@ class ExposedRelationshipRepository(override val domainEventPublisher: DomainEve
|
||||||
}.singleOrNull()?.toRelationships()
|
}.singleOrNull()?.toRelationships()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun findByActorIdsAndTargetIdAndBlocking(
|
||||||
|
actorIds: List<ActorId>,
|
||||||
|
targetId: ActorId,
|
||||||
|
blocking: Boolean
|
||||||
|
): List<Relationship> = query {
|
||||||
|
Relationships.selectAll().where {
|
||||||
|
Relationships.actorId inList actorIds.map { it.id } and (Relationships.targetActorId eq targetId.id)
|
||||||
|
}.map { it.toRelationships() }
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun findByActorIdAndTargetIdsAndFollowing(
|
||||||
|
actorId: ActorId,
|
||||||
|
targetIds: List<ActorId>,
|
||||||
|
following: Boolean
|
||||||
|
): List<Relationship> = query {
|
||||||
|
Relationships.selectAll().where {
|
||||||
|
Relationships.actorId eq actorId.id and (Relationships.targetActorId inList targetIds.map { it.id })
|
||||||
|
}.map { it.toRelationships() }
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun findByTargetId(
|
override suspend fun findByTargetId(
|
||||||
targetId: ActorId,
|
targetId: ActorId,
|
||||||
option: FindRelationshipOption?,
|
option: FindRelationshipOption?,
|
||||||
|
|
|
@ -4,11 +4,14 @@ import dev.usbharu.hideout.core.domain.model.actor.Actor
|
||||||
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
||||||
import dev.usbharu.hideout.core.domain.model.filter.Filter
|
import dev.usbharu.hideout.core.domain.model.filter.Filter
|
||||||
import dev.usbharu.hideout.core.domain.model.filter.FilteredPost
|
import dev.usbharu.hideout.core.domain.model.filter.FilteredPost
|
||||||
|
import dev.usbharu.hideout.core.domain.model.media.Media
|
||||||
|
import dev.usbharu.hideout.core.domain.model.media.MediaId
|
||||||
import dev.usbharu.hideout.core.domain.model.post.Post
|
import dev.usbharu.hideout.core.domain.model.post.Post
|
||||||
import dev.usbharu.hideout.core.domain.model.post.PostId
|
import dev.usbharu.hideout.core.domain.model.post.PostId
|
||||||
import dev.usbharu.hideout.core.domain.model.post.Visibility
|
import dev.usbharu.hideout.core.domain.model.post.Visibility
|
||||||
import dev.usbharu.hideout.core.domain.model.support.page.Page
|
import dev.usbharu.hideout.core.domain.model.support.page.Page
|
||||||
import dev.usbharu.hideout.core.domain.model.support.page.PaginationList
|
import dev.usbharu.hideout.core.domain.model.support.page.PaginationList
|
||||||
|
import dev.usbharu.hideout.core.domain.model.support.principal.Principal
|
||||||
import dev.usbharu.hideout.core.domain.model.support.timelineobjectdetail.TimelineObjectDetail
|
import dev.usbharu.hideout.core.domain.model.support.timelineobjectdetail.TimelineObjectDetail
|
||||||
import dev.usbharu.hideout.core.domain.model.timeline.Timeline
|
import dev.usbharu.hideout.core.domain.model.timeline.Timeline
|
||||||
import dev.usbharu.hideout.core.domain.model.timeline.TimelineId
|
import dev.usbharu.hideout.core.domain.model.timeline.TimelineId
|
||||||
|
@ -105,7 +108,7 @@ abstract class AbstractTimelineStore(private val idGenerateService: IdGenerateSe
|
||||||
|
|
||||||
protected abstract suspend fun getPostsByTimelineRelationshipList(timelineRelationshipList: List<TimelineRelationship>): List<Post>
|
protected abstract suspend fun getPostsByTimelineRelationshipList(timelineRelationshipList: List<TimelineRelationship>): List<Post>
|
||||||
|
|
||||||
protected abstract suspend fun getPostsByPostId(postIds: List<PostId>): List<Post>
|
protected abstract suspend fun getPostsByPostId(postIds: List<PostId>, principal: Principal): List<Post>
|
||||||
|
|
||||||
protected abstract suspend fun getTimelineObject(
|
protected abstract suspend fun getTimelineObject(
|
||||||
timelineId: TimelineId,
|
timelineId: TimelineId,
|
||||||
|
@ -205,7 +208,8 @@ abstract class AbstractTimelineStore(private val idGenerateService: IdGenerateSe
|
||||||
override suspend fun readTimeline(
|
override suspend fun readTimeline(
|
||||||
timeline: Timeline,
|
timeline: Timeline,
|
||||||
option: ReadTimelineOption?,
|
option: ReadTimelineOption?,
|
||||||
page: Page?
|
page: Page?,
|
||||||
|
principal: Principal
|
||||||
): PaginationList<TimelineObjectDetail, PostId> {
|
): PaginationList<TimelineObjectDetail, PostId> {
|
||||||
val timelineObjectList = getTimelineObject(timeline.id, option, page)
|
val timelineObjectList = getTimelineObject(timeline.id, option, page)
|
||||||
val lastUpdatedAt = timelineObjectList.minBy { it.lastUpdatedAt }.lastUpdatedAt
|
val lastUpdatedAt = timelineObjectList.minBy { it.lastUpdatedAt }.lastUpdatedAt
|
||||||
|
@ -216,7 +220,8 @@ abstract class AbstractTimelineStore(private val idGenerateService: IdGenerateSe
|
||||||
getPostsByPostId(
|
getPostsByPostId(
|
||||||
timelineObjectList.map {
|
timelineObjectList.map {
|
||||||
it.postId
|
it.postId
|
||||||
} + timelineObjectList.mapNotNull { it.repostId } + timelineObjectList.mapNotNull { it.replyId }
|
} + timelineObjectList.mapNotNull { it.repostId } + timelineObjectList.mapNotNull { it.replyId },
|
||||||
|
principal
|
||||||
)
|
)
|
||||||
|
|
||||||
val userDetails = getUserDetails(timelineObjectList.map { it.userDetailId })
|
val userDetails = getUserDetails(timelineObjectList.map { it.userDetailId })
|
||||||
|
@ -232,24 +237,35 @@ abstract class AbstractTimelineStore(private val idGenerateService: IdGenerateSe
|
||||||
post.id to applyFilters(post, newerFilters)
|
post.id to applyFilters(post, newerFilters)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val mediaMap = getMedias(posts.flatMap { it.mediaIds } + actors.mapNotNull { it.value.icon })
|
||||||
|
|
||||||
return PaginationList(
|
return PaginationList(
|
||||||
timelineObjectList.mapNotNull<TimelineObject, TimelineObjectDetail> {
|
timelineObjectList.mapNotNull<TimelineObject, TimelineObjectDetail> {
|
||||||
val timelineUserDetail = userDetails[it.userDetailId] ?: return@mapNotNull null
|
val timelineUserDetail = userDetails[it.userDetailId] ?: return@mapNotNull null
|
||||||
val actor = actors[it.postActorId] ?: return@mapNotNull null
|
val actor = actors[it.postActorId] ?: return@mapNotNull null
|
||||||
val post = postMap[it.postId] ?: return@mapNotNull null
|
val post = postMap[it.postId] ?: return@mapNotNull null
|
||||||
|
val postMedias = post.post.mediaIds.mapNotNull { mediaId -> mediaMap[mediaId] }
|
||||||
val reply = postMap[it.replyId]
|
val reply = postMap[it.replyId]
|
||||||
|
val replyMedias = reply?.post?.mediaIds?.mapNotNull { mediaId -> mediaMap[mediaId] }
|
||||||
val replyActor = actors[it.replyActorId]
|
val replyActor = actors[it.replyActorId]
|
||||||
val repost = postMap[it.repostId]
|
val repost = postMap[it.repostId]
|
||||||
|
val repostMedias = repost?.post?.mediaIds?.mapNotNull { mediaId -> mediaMap[mediaId] }
|
||||||
val repostActor = actors[it.repostActorId]
|
val repostActor = actors[it.repostActorId]
|
||||||
TimelineObjectDetail.of(
|
TimelineObjectDetail.of(
|
||||||
timelineObject = it,
|
timelineObject = it,
|
||||||
timelineUserDetail = timelineUserDetail,
|
timelineUserDetail = timelineUserDetail,
|
||||||
post = post.post,
|
post = post.post,
|
||||||
|
postMedias = postMedias,
|
||||||
postActor = actor,
|
postActor = actor,
|
||||||
|
postActorIconMedia = mediaMap[actor.icon],
|
||||||
replyPost = reply?.post,
|
replyPost = reply?.post,
|
||||||
|
replyPostMedias = replyMedias,
|
||||||
replyPostActor = replyActor,
|
replyPostActor = replyActor,
|
||||||
|
replyPostActorIconMedia = mediaMap[replyActor?.icon],
|
||||||
repostPost = repost?.post,
|
repostPost = repost?.post,
|
||||||
|
repostPostMedias = repostMedias,
|
||||||
repostPostActor = repostActor,
|
repostPostActor = repostActor,
|
||||||
|
repostPostActorIconMedia = mediaMap[repostActor?.icon],
|
||||||
warnFilter = it.warnFilters + post.filterResults.map {
|
warnFilter = it.warnFilters + post.filterResults.map {
|
||||||
TimelineObjectWarnFilter(
|
TimelineObjectWarnFilter(
|
||||||
it.filter.id,
|
it.filter.id,
|
||||||
|
@ -265,5 +281,7 @@ abstract class AbstractTimelineStore(private val idGenerateService: IdGenerateSe
|
||||||
|
|
||||||
protected abstract suspend fun getActors(actorIds: List<ActorId>): Map<ActorId, Actor>
|
protected abstract suspend fun getActors(actorIds: List<ActorId>): Map<ActorId, Actor>
|
||||||
|
|
||||||
|
protected abstract suspend fun getMedias(mediaIds: List<MediaId>): Map<MediaId, Media>
|
||||||
|
|
||||||
protected abstract suspend fun getUserDetails(userDetailIdList: List<UserDetailId>): Map<UserDetailId, UserDetail>
|
protected abstract suspend fun getUserDetails(userDetailIdList: List<UserDetailId>): Map<UserDetailId, UserDetail>
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,12 +8,16 @@ import dev.usbharu.hideout.core.domain.model.filter.Filter
|
||||||
import dev.usbharu.hideout.core.domain.model.filter.FilterContext
|
import dev.usbharu.hideout.core.domain.model.filter.FilterContext
|
||||||
import dev.usbharu.hideout.core.domain.model.filter.FilterRepository
|
import dev.usbharu.hideout.core.domain.model.filter.FilterRepository
|
||||||
import dev.usbharu.hideout.core.domain.model.filter.FilteredPost
|
import dev.usbharu.hideout.core.domain.model.filter.FilteredPost
|
||||||
|
import dev.usbharu.hideout.core.domain.model.media.Media
|
||||||
|
import dev.usbharu.hideout.core.domain.model.media.MediaId
|
||||||
|
import dev.usbharu.hideout.core.domain.model.media.MediaRepository
|
||||||
import dev.usbharu.hideout.core.domain.model.post.Post
|
import dev.usbharu.hideout.core.domain.model.post.Post
|
||||||
import dev.usbharu.hideout.core.domain.model.post.PostId
|
import dev.usbharu.hideout.core.domain.model.post.PostId
|
||||||
import dev.usbharu.hideout.core.domain.model.post.PostRepository
|
import dev.usbharu.hideout.core.domain.model.post.PostRepository
|
||||||
import dev.usbharu.hideout.core.domain.model.post.Visibility
|
import dev.usbharu.hideout.core.domain.model.post.Visibility
|
||||||
import dev.usbharu.hideout.core.domain.model.support.page.Page
|
import dev.usbharu.hideout.core.domain.model.support.page.Page
|
||||||
import dev.usbharu.hideout.core.domain.model.support.page.PaginationList
|
import dev.usbharu.hideout.core.domain.model.support.page.PaginationList
|
||||||
|
import dev.usbharu.hideout.core.domain.model.support.principal.Principal
|
||||||
import dev.usbharu.hideout.core.domain.model.timeline.Timeline
|
import dev.usbharu.hideout.core.domain.model.timeline.Timeline
|
||||||
import dev.usbharu.hideout.core.domain.model.timeline.TimelineId
|
import dev.usbharu.hideout.core.domain.model.timeline.TimelineId
|
||||||
import dev.usbharu.hideout.core.domain.model.timeline.TimelineRepository
|
import dev.usbharu.hideout.core.domain.model.timeline.TimelineRepository
|
||||||
|
@ -24,6 +28,7 @@ import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail
|
||||||
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
|
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
|
||||||
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
|
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
|
||||||
import dev.usbharu.hideout.core.domain.service.filter.FilterDomainService
|
import dev.usbharu.hideout.core.domain.service.filter.FilterDomainService
|
||||||
|
import dev.usbharu.hideout.core.domain.service.post.IPostReadAccessControl
|
||||||
import dev.usbharu.hideout.core.domain.shared.id.IdGenerateService
|
import dev.usbharu.hideout.core.domain.shared.id.IdGenerateService
|
||||||
import dev.usbharu.hideout.core.external.timeline.ReadTimelineOption
|
import dev.usbharu.hideout.core.external.timeline.ReadTimelineOption
|
||||||
import org.springframework.stereotype.Component
|
import org.springframework.stereotype.Component
|
||||||
|
@ -40,7 +45,9 @@ open class DefaultTimelineStore(
|
||||||
private val defaultTimelineStoreConfig: DefaultTimelineStoreConfig,
|
private val defaultTimelineStoreConfig: DefaultTimelineStoreConfig,
|
||||||
private val internalTimelineObjectRepository: InternalTimelineObjectRepository,
|
private val internalTimelineObjectRepository: InternalTimelineObjectRepository,
|
||||||
private val userDetailRepository: UserDetailRepository,
|
private val userDetailRepository: UserDetailRepository,
|
||||||
private val actorRepository: ActorRepository
|
private val actorRepository: ActorRepository,
|
||||||
|
private val mediaRepository: MediaRepository,
|
||||||
|
private val postIPostReadAccessControl: IPostReadAccessControl
|
||||||
) : AbstractTimelineStore(idGenerateService) {
|
) : AbstractTimelineStore(idGenerateService) {
|
||||||
override suspend fun getTimelines(actorId: ActorId): List<Timeline> {
|
override suspend fun getTimelines(actorId: ActorId): List<Timeline> {
|
||||||
return timelineRepository.findByIds(
|
return timelineRepository.findByIds(
|
||||||
|
@ -99,8 +106,9 @@ open class DefaultTimelineStore(
|
||||||
return timelineRelationshipList.flatMap { getActorPost(it.actorId, visibilities(it)) }
|
return timelineRelationshipList.flatMap { getActorPost(it.actorId, visibilities(it)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getPostsByPostId(postIds: List<PostId>): List<Post> {
|
override suspend fun getPostsByPostId(postIds: List<PostId>, principal: Principal): List<Post> {
|
||||||
return postRepository.findAllById(postIds)
|
val findAllById = postRepository.findAllById(postIds)
|
||||||
|
return postIPostReadAccessControl.areAllows(findAllById, principal)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getTimelineObject(
|
override suspend fun getTimelineObject(
|
||||||
|
@ -131,6 +139,10 @@ open class DefaultTimelineStore(
|
||||||
return actorRepository.findAllById(actorIds).associateBy { it.id }
|
return actorRepository.findAllById(actorIds).associateBy { it.id }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun getMedias(mediaIds: List<MediaId>): Map<MediaId, Media> {
|
||||||
|
return mediaRepository.findByIds(mediaIds).associateBy { it.id }
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun getUserDetails(userDetailIdList: List<UserDetailId>): Map<UserDetailId, UserDetail> {
|
override suspend fun getUserDetails(userDetailIdList: List<UserDetailId>): Map<UserDetailId, UserDetail> {
|
||||||
return userDetailRepository.findAllById(userDetailIdList).associateBy { it.id }
|
return userDetailRepository.findAllById(userDetailIdList).associateBy { it.id }
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package dev.usbharu.hideout.core.infrastructure.timeline
|
||||||
import dev.usbharu.hideout.core.config.DefaultTimelineStoreConfig
|
import dev.usbharu.hideout.core.config.DefaultTimelineStoreConfig
|
||||||
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
|
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
|
||||||
import dev.usbharu.hideout.core.domain.model.filter.*
|
import dev.usbharu.hideout.core.domain.model.filter.*
|
||||||
|
import dev.usbharu.hideout.core.domain.model.media.MediaRepository
|
||||||
import dev.usbharu.hideout.core.domain.model.post.PostRepository
|
import dev.usbharu.hideout.core.domain.model.post.PostRepository
|
||||||
import dev.usbharu.hideout.core.domain.model.post.TestPostFactory
|
import dev.usbharu.hideout.core.domain.model.post.TestPostFactory
|
||||||
import dev.usbharu.hideout.core.domain.model.post.Visibility
|
import dev.usbharu.hideout.core.domain.model.post.Visibility
|
||||||
|
@ -16,6 +17,7 @@ import dev.usbharu.hideout.core.domain.model.timelinerelationship.Visible
|
||||||
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
|
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId
|
||||||
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
|
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
|
||||||
import dev.usbharu.hideout.core.domain.service.filter.FilterDomainService
|
import dev.usbharu.hideout.core.domain.service.filter.FilterDomainService
|
||||||
|
import dev.usbharu.hideout.core.domain.service.post.IPostReadAccessControl
|
||||||
import dev.usbharu.hideout.core.infrastructure.other.TwitterSnowflakeIdGenerateService
|
import dev.usbharu.hideout.core.infrastructure.other.TwitterSnowflakeIdGenerateService
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
@ -56,6 +58,12 @@ class DefaultTimelineStoreTest {
|
||||||
@Mock
|
@Mock
|
||||||
lateinit var actorRepository: ActorRepository
|
lateinit var actorRepository: ActorRepository
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
lateinit var mediaRepository: MediaRepository
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
lateinit var iPostReadAccessControl: IPostReadAccessControl
|
||||||
|
|
||||||
@Spy
|
@Spy
|
||||||
val defaultTimelineStoreConfig = DefaultTimelineStoreConfig(500)
|
val defaultTimelineStoreConfig = DefaultTimelineStoreConfig(500)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue