From 3b577869840b1082147345a3da326bb309306a4b Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 9 Sep 2024 16:15:20 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20Mastodon=E3=81=A7=E3=83=9B=E3=83=BC?= =?UTF-8?q?=E3=83=A0=E3=82=BF=E3=82=A4=E3=83=A0=E3=83=A9=E3=82=A4=E3=83=B3?= =?UTF-8?q?=E3=82=92=E8=AA=AD=E3=82=81=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 --- .../MongoInternalTimelineObjectRepository.kt | 3 + .../src/main/resources/templates/index.html | 6 +- .../timeline/MastodonReadTimeline.kt | 13 ++ .../MastodonReadTimelineApplicationService.kt | 113 ++++++++++++++++++ .../interfaces/api/SpringTimelineApi.kt | 53 +++++++- .../src/main/resources/openapi/mastodon.yaml | 2 +- 6 files changed, 185 insertions(+), 5 deletions(-) create mode 100644 hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/timeline/MastodonReadTimeline.kt create mode 100644 hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/timeline/MastodonReadTimelineApplicationService.kt diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/mongorepository/MongoInternalTimelineObjectRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/mongorepository/MongoInternalTimelineObjectRepository.kt index 2e66bbbd..c6dec873 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/mongorepository/MongoInternalTimelineObjectRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/mongorepository/MongoInternalTimelineObjectRepository.kt @@ -25,6 +25,7 @@ import org.springframework.data.mongodb.core.MongoTemplate import org.springframework.data.mongodb.core.mapping.Document import org.springframework.data.mongodb.core.query.Criteria import org.springframework.data.mongodb.core.query.Query +import org.springframework.data.mongodb.core.query.isEqualTo import org.springframework.data.repository.kotlin.CoroutineCrudRepository import org.springframework.stereotype.Repository import java.time.Instant @@ -84,6 +85,8 @@ class MongoInternalTimelineObjectRepository( ): PaginationList { val query = Query() + query.addCriteria(Criteria.where("timelineId").isEqualTo(timelineId.value)) + if (page?.minId != null) { query.with(Sort.by(Sort.Direction.ASC, "postCreatedAt")) page.minId?.let { query.addCriteria(Criteria.where("postId").gt(it)) } diff --git a/hideout-core/src/main/resources/templates/index.html b/hideout-core/src/main/resources/templates/index.html index 06321a91..f739ba1a 100644 --- a/hideout-core/src/main/resources/templates/index.html +++ b/hideout-core/src/main/resources/templates/index.html @@ -14,8 +14,8 @@ - + +No Script + \ No newline at end of file diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/timeline/MastodonReadTimeline.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/timeline/MastodonReadTimeline.kt new file mode 100644 index 00000000..4d76bda5 --- /dev/null +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/timeline/MastodonReadTimeline.kt @@ -0,0 +1,13 @@ +package dev.usbharu.hideout.mastodon.application.timeline + +import dev.usbharu.hideout.core.domain.model.support.page.Page + +class MastodonReadTimeline( + val timelineId: Long, + val mediaOnly: Boolean, + val localOnly: Boolean, + val remoteOnly: Boolean, + val page: Page +) { + +} diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/timeline/MastodonReadTimelineApplicationService.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/timeline/MastodonReadTimelineApplicationService.kt new file mode 100644 index 00000000..9858228b --- /dev/null +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/timeline/MastodonReadTimelineApplicationService.kt @@ -0,0 +1,113 @@ +package dev.usbharu.hideout.mastodon.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.Visibility.* +import dev.usbharu.hideout.core.domain.model.support.acct.Acct +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.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 dev.usbharu.hideout.mastodon.interfaces.api.generated.model.Account +import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.MediaAttachment +import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.Status +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service + +@Service +class MastodonReadTimelineApplicationService( + transaction: Transaction, + private val timelineRepository: TimelineRepository, + private val timelineStore: TimelineStore +) : + AbstractApplicationService>(transaction, logger) { + override suspend fun internalExecute( + command: MastodonReadTimeline, + principal: Principal + ): PaginationList { + val timeline = timelineRepository.findById(TimelineId(command.timelineId)) + ?: throw IllegalArgumentException("Timeline ${command.timelineId} not found.") + + val readTimelineOption = ReadTimelineOption( + command.mediaOnly, + command.localOnly, + command.remoteOnly + ) + + val readTimeline = timelineStore.readTimeline(timeline, readTimelineOption, command.page, principal) + + return PaginationList(readTimeline.map { + Status( + it.postId.id.toString(), + it.post.url.toString(), + it.post.createdAt.toString(), + account = Account( + id = it.postActor.id.id.toString(), + username = it.postActor.name.name, + acct = Acct(it.postActor.name.name, it.postActor.domain.domain).toString(), + url = it.postActor.url.toString(), + displayName = it.postActor.screenName.screenName, + note = it.postActor.description.description, + avatar = it.postActorIconMedia?.url.toString(), + avatarStatic = it.postActorIconMedia?.thumbnailUrl.toString(), + header = "", + headerStatic = "", + locked = false, + fields = emptyList(), + emojis = emptyList(), + bot = false, + group = false, + discoverable = true, + createdAt = it.postActor.createdAt.toString(), + statusesCount = it.postActor.postsCount.postsCount, + noindex = true, + moved = it.postActor.moveTo != null, + suspended = it.postActor.suspend, + limited = false, + lastStatusAt = it.postActor.lastPostAt?.toString(), + followersCount = it.postActor.followersCount?.relationshipCount, + followingCount = it.postActor.followingCount?.relationshipCount, + source = null + ), + content = it.post.content.content, + visibility = when (it.post.visibility) { + PUBLIC -> Status.Visibility.public + UNLISTED -> Status.Visibility.unlisted + FOLLOWERS -> Status.Visibility.private + DIRECT -> Status.Visibility.direct + }, + sensitive = it.post.sensitive, + spoilerText = it.post.overview?.overview.orEmpty(), + mediaAttachments = it.postMedias.map { MediaAttachment(it.id.id.toString()) }, + mentions = emptyList(), + tags = emptyList(), + emojis = emptyList(), + reblogsCount = 0, + favouritesCount = it.reactionsList.sumOf { it.count }, + repliesCount = 0, + url = it.post.url.toString(), + text = it.post.content.text, + application = null, + inReplyToId = it.replyPost?.id?.toString(), + inReplyToAccountId = it.replyPostActor?.id?.toString(), + reblog = null, + poll = null, + card = null, + language = null, + editedAt = null, + favourited = it.favourited, + reblogged = false, + muted = false, + bookmarked = false, + pinned = false, + filtered = emptyList(), + ) + }, readTimeline.next?.id, readTimeline.prev?.id) + } + + companion object { + private val logger = LoggerFactory.getLogger(MastodonReadTimelineApplicationService::class.java) + } +} \ No newline at end of file diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringTimelineApi.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringTimelineApi.kt index 2a27d263..9f259736 100644 --- a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringTimelineApi.kt +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringTimelineApi.kt @@ -16,8 +16,59 @@ package dev.usbharu.hideout.mastodon.interfaces.api +import dev.usbharu.hideout.core.application.exception.InternalServerException +import dev.usbharu.hideout.core.application.shared.Transaction +import dev.usbharu.hideout.core.domain.model.support.page.Page +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository +import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.SpringSecurityOauth2PrincipalContextHolder +import dev.usbharu.hideout.mastodon.application.timeline.MastodonReadTimeline +import dev.usbharu.hideout.mastodon.application.timeline.MastodonReadTimelineApplicationService import dev.usbharu.hideout.mastodon.interfaces.api.generated.TimelineApi +import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.Status +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.asFlow +import kotlinx.coroutines.runBlocking +import org.springframework.http.ResponseEntity import org.springframework.stereotype.Controller @Controller -class SpringTimelineApi : TimelineApi +class SpringTimelineApi( + private val mastodonReadTimelineApplicationService: MastodonReadTimelineApplicationService, + + private val principalContextHolder: SpringSecurityOauth2PrincipalContextHolder, + private val userDetailRepository: UserDetailRepository, + private val transaction: Transaction, +) : TimelineApi { + override fun apiV1TimelinesHomeGet( + maxId: String?, + sinceId: String?, + minId: String?, + limit: Int? + ): ResponseEntity> = runBlocking { + val principal = principalContextHolder.getPrincipal() + val userDetail = transaction.transaction { + userDetailRepository.findByActorId(principal.actorId.id) + ?: throw InternalServerException("UserDetail not found.") + } + + val homeTimelineId = + userDetail.homeTimelineId ?: throw InternalServerException("HomeTimeline ${userDetail.id} is null.") + + ResponseEntity.ok( + mastodonReadTimelineApplicationService.execute( + MastodonReadTimeline( + timelineId = homeTimelineId.value, + mediaOnly = false, + localOnly = false, + remoteOnly = false, + page = Page.of( + maxId?.toLongOrNull(), + sinceId?.toLongOrNull(), + minId?.toLongOrNull(), + limit + ) + ), principal + ).asFlow() + ) + } +} diff --git a/hideout-mastodon/src/main/resources/openapi/mastodon.yaml b/hideout-mastodon/src/main/resources/openapi/mastodon.yaml index 76d8f5aa..1fb96267 100644 --- a/hideout-mastodon/src/main/resources/openapi/mastodon.yaml +++ b/hideout-mastodon/src/main/resources/openapi/mastodon.yaml @@ -2020,7 +2020,7 @@ components: type: object properties: filter: - $ref: "#/components/schemas/FilterResult" + $ref: "#/components/schemas/Filter" keyword_matches: type: array items: From 91848717fe35f2971d57816146f81f783377ef56 Mon Sep 17 00:00:00 2001 From: usbharu Date: Mon, 9 Sep 2024 07:30:05 +0000 Subject: [PATCH 2/3] style: fix lint (CI) --- .../UserCreateReactionApplicationService.kt | 6 +++--- .../UserRemoveReactionApplicationService.kt | 5 +++-- .../domain/event/reaction/ReactionEvent.kt | 2 +- .../core/domain/model/emoji/CustomEmoji.kt | 6 +++--- .../core/domain/model/reaction/Reaction.kt | 10 +++++++--- .../core/domain/model/reaction/ReactionId.kt | 2 +- .../model/reaction/ReactionRepository.kt | 2 +- .../model/timelineobject/TimelineObject.kt | 12 ++++++------ .../service/emoji/UnicodeEmojiService.kt | 2 +- .../emojikt/EmojiKtUnicodeEmojiService.kt | 2 +- .../ExposedReactionsQueryService.kt | 3 +-- .../ExposedUserTimelineQueryService.kt | 17 ++++++++++------- .../ExposedReactionRepository.kt | 19 +++++++++++-------- .../timeline/AbstractTimelineStore.kt | 6 +++--- .../interfaces/web/posts/PostsController.kt | 6 ++++-- .../query/reactions/ReactionsQueryService.kt | 2 +- 16 files changed, 57 insertions(+), 45 deletions(-) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/reaction/UserCreateReactionApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/reaction/UserCreateReactionApplicationService.kt index 1ca68aef..f0a7f2dd 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/reaction/UserCreateReactionApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/reaction/UserCreateReactionApplicationService.kt @@ -29,10 +29,10 @@ class UserCreateReactionApplicationService( private val unicodeEmojiService: UnicodeEmojiService ) : LocalUserAbstractApplicationService( - transaction, logger + transaction, + logger ) { override suspend fun internalExecute(command: CreateReaction, principal: LocalUser) { - val postId = PostId(command.postId) val post = postRepository.findById(postId) ?: throw IllegalArgumentException("Post $postId not found.") if (postReadAccessControl.isAllow(post, principal).not()) { @@ -62,4 +62,4 @@ class UserCreateReactionApplicationService( companion object { private val logger = LoggerFactory.getLogger(UserCreateReactionApplicationService::class.java) } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/reaction/UserRemoveReactionApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/reaction/UserRemoveReactionApplicationService.kt index b53f21f8..4335ee63 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/reaction/UserRemoveReactionApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/reaction/UserRemoveReactionApplicationService.kt @@ -18,7 +18,8 @@ class UserRemoveReactionApplicationService( private val unicodeEmojiService: UnicodeEmojiService ) : LocalUserAbstractApplicationService( - transaction, logger + transaction, + logger ) { override suspend fun internalExecute(command: RemoveReaction, principal: LocalUser) { val postId = PostId(command.postId) @@ -47,4 +48,4 @@ class UserRemoveReactionApplicationService( companion object { private val logger = LoggerFactory.getLogger(UserRemoveReactionApplicationService::class.java) } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/reaction/ReactionEvent.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/reaction/ReactionEvent.kt index 7e89361e..6da7a58c 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/reaction/ReactionEvent.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/event/reaction/ReactionEvent.kt @@ -19,4 +19,4 @@ class ReactionEventBody( enum class ReactionEvent(val eventName: String) { CREATE("ReactionCreate"), DELETE("ReactionDelete"), -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmoji.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmoji.kt index 5a64a948..b80d06cf 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmoji.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/emoji/CustomEmoji.kt @@ -29,9 +29,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/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/Reaction.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/Reaction.kt index 93b43000..c85c061d 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/Reaction.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/Reaction.kt @@ -18,7 +18,6 @@ class Reaction( val createdAt: Instant ) : DomainEventStorable() { - override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false @@ -46,8 +45,13 @@ class Reaction( createdAt: Instant ): Reaction { return Reaction( - id, postId, actorId, customEmojiId, unicodeEmoji, createdAt + id, + postId, + actorId, + customEmojiId, + unicodeEmoji, + createdAt ).apply { addDomainEvent(ReactionEventFactory(this).createEvent(ReactionEvent.CREATE)) } } } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionId.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionId.kt index af369ff0..948fb1ea 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionId.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionId.kt @@ -1,4 +1,4 @@ package dev.usbharu.hideout.core.domain.model.reaction @JvmInline -value class ReactionId(val value: Long) \ No newline at end of file +value class ReactionId(val value: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt index 9aa1f6aa..01e893a7 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/reaction/ReactionRepository.kt @@ -23,4 +23,4 @@ interface ReactionRepository { ): Reaction? suspend fun delete(reaction: Reaction) -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/timelineobject/TimelineObject.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/timelineobject/TimelineObject.kt index a6fbe671..f7f57f22 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/timelineobject/TimelineObject.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/timelineobject/TimelineObject.kt @@ -61,9 +61,9 @@ class TimelineObject( lastUpdatedAt = Instant.now() isPureRepost = post.repostId != null && - post.replyId == null && - post.text.isEmpty() && - post.overview?.overview.isNullOrEmpty() + post.replyId == null && + post.text.isEmpty() && + post.overview?.overview.isNullOrEmpty() warnFilters = filterResults.map { TimelineObjectWarnFilter(it.filter.id, it.matchedKeyword) } } @@ -133,9 +133,9 @@ class TimelineObject( repostActorId = repost.actorId, visibility = post.visibility, isPureRepost = repost.mediaIds.isEmpty() && - repost.overview == null && - repost.content == PostContent.empty && - repost.replyId == null, + repost.overview == null && + repost.content == PostContent.empty && + repost.replyId == null, mediaIds = post.mediaIds, emojiIds = post.emojiIds, visibleActors = post.visibleActors.toList(), diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/emoji/UnicodeEmojiService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/emoji/UnicodeEmojiService.kt index add7bdbe..35a44ca5 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/emoji/UnicodeEmojiService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/emoji/UnicodeEmojiService.kt @@ -2,4 +2,4 @@ package dev.usbharu.hideout.core.domain.service.emoji interface UnicodeEmojiService { fun isUnicodeEmoji(emoji: String): Boolean -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/emojikt/EmojiKtUnicodeEmojiService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/emojikt/EmojiKtUnicodeEmojiService.kt index 3eef34aa..3e0fdc43 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/emojikt/EmojiKtUnicodeEmojiService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/emojikt/EmojiKtUnicodeEmojiService.kt @@ -9,4 +9,4 @@ class EmojiKtUnicodeEmojiService : UnicodeEmojiService { override fun isUnicodeEmoji(emoji: String): Boolean { return Emojis.allEmojis.singleOrNull { it.char == emoji } != null } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedReactionsQueryService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedReactionsQueryService.kt index b4d7705f..d22184e7 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedReactionsQueryService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedReactionsQueryService.kt @@ -37,7 +37,6 @@ class ExposedReactionsQueryService : ReactionsQueryService, AbstractRepository() override suspend fun findAllByPostIdIn(postIds: List): List { return query { - val actorIdsQuery = ExposedrepositoryReactions.actorId.castTo(VarCharColumnType()).groupConcat(",", true) @@ -71,4 +70,4 @@ class ExposedReactionsQueryService : ReactionsQueryService, AbstractRepository() companion object { private val logger = LoggerFactory.getLogger(ExposedReactionsQueryService::class.java) } -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedUserTimelineQueryService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedUserTimelineQueryService.kt index 6b4338fb..db3fb0f2 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedUserTimelineQueryService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedUserTimelineQueryService.kt @@ -42,10 +42,10 @@ class ExposedUserTimelineQueryService : UserTimelineQueryService, AbstractReposi .select(Posts.columns) .where { Posts.visibility eq Visibility.PUBLIC.name or - (Posts.visibility eq Visibility.UNLISTED.name) or - (Posts.visibility eq Visibility.DIRECT.name and (PostsVisibleActors.actorId eq principal.actorId.id)) or - (Posts.visibility eq Visibility.FOLLOWERS.name and (Relationships.blocking eq false and (relationshipsAlias[Relationships.following] eq true))) or - (Posts.actorId eq principal.actorId.id) + (Posts.visibility eq Visibility.UNLISTED.name) or + (Posts.visibility eq Visibility.DIRECT.name and (PostsVisibleActors.actorId eq principal.actorId.id)) or + (Posts.visibility eq Visibility.FOLLOWERS.name and (Relationships.blocking eq false and (relationshipsAlias[Relationships.following] eq true))) or + (Posts.actorId eq principal.actorId.id) } .alias("authorized_table") } @@ -61,10 +61,12 @@ class ExposedUserTimelineQueryService : UserTimelineQueryService, AbstractReposi .leftJoin(iconMedia, { Actors.icon }, { iconMedia[Media.id] }) .leftJoin(PostsMedia, { authorizedQuery[Posts.id] }, { PostsMedia.postId }) .leftJoin(Media, { PostsMedia.mediaId }, { Media.id }) - .leftJoin(Reactions, + .leftJoin( + Reactions, { authorizedQuery[Posts.id] }, { Reactions.postId }, - { Reactions.id isDistinctFrom principal.actorId.id }) + { Reactions.id isDistinctFrom principal.actorId.id } + ) .selectAll() .where { authorizedQuery[Posts.id] inList idList.map { it.id } } .groupBy { it[authorizedQuery[Posts.id]] } @@ -73,7 +75,8 @@ class ExposedUserTimelineQueryService : UserTimelineQueryService, AbstractReposi toPostDetail(it.first(), authorizedQuery, iconMedia).copy( mediaDetailList = it.mapNotNull { resultRow -> resultRow.toMediaOrNull()?.let { it1 -> MediaDetail.of(it1) } - }, favourited = it.any { it.getOrNull(Reactions.actorId) != null } + }, + favourited = it.any { it.getOrNull(Reactions.actorId) != null } ) } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedReactionRepository.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedReactionRepository.kt index f3fc6886..d44bca1f 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedReactionRepository.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/ExposedReactionRepository.kt @@ -17,13 +17,14 @@ import org.slf4j.LoggerFactory import org.springframework.stereotype.Repository @Repository -class ExposedReactionRepository(override val domainEventPublisher: DomainEventPublisher) : ReactionRepository, - AbstractRepository(), DomainEventPublishableRepository { +class ExposedReactionRepository(override val domainEventPublisher: DomainEventPublisher) : + ReactionRepository, + AbstractRepository(), + DomainEventPublishableRepository { override val logger: Logger get() = Companion.logger - override suspend fun save(reaction: Reaction): Reaction { return query { Reactions.upsert { @@ -66,7 +67,9 @@ class ExposedReactionRepository(override val domainEventPublisher: DomainEventPu return query { Reactions.selectAll().where { Reactions.postId.eq(postId.id).and(Reactions.actorId eq actorId.id) - .and((Reactions.customEmojiId eq customEmojiId?.emojiId or (Reactions.unicodeEmoji eq unicodeEmoji))) + .and( + (Reactions.customEmojiId eq customEmojiId?.emojiId or (Reactions.unicodeEmoji eq unicodeEmoji)) + ) }.empty().not() } } @@ -89,10 +92,11 @@ class ExposedReactionRepository(override val domainEventPublisher: DomainEventPu unicodeEmoji: String ): Reaction? { return query { - Reactions.selectAll().where { Reactions.postId.eq(postId.id).and(Reactions.actorId eq actorId.id) - .and((Reactions.customEmojiId eq customEmojiId?.emojiId or (Reactions.unicodeEmoji eq unicodeEmoji))) + .and( + (Reactions.customEmojiId eq customEmojiId?.emojiId or (Reactions.unicodeEmoji eq unicodeEmoji)) + ) }.limit(1).singleOrNull()?.toReaction() } } @@ -111,7 +115,6 @@ fun ResultRow.toReaction(): Reaction { UnicodeEmoji(this[Reactions.unicodeEmoji]), this[Reactions.createdAt] ) - } object Reactions : Table("reactions") { @@ -122,4 +125,4 @@ object Reactions : Table("reactions") { val unicodeEmoji = varchar("unicode_emoji", 100) val createdAt = timestamp("created_at") override val primaryKey: PrimaryKey = PrimaryKey(id) -} \ No newline at end of file +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/AbstractTimelineStore.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/AbstractTimelineStore.kt index 9ddcc131..b22d0121 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/AbstractTimelineStore.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/AbstractTimelineStore.kt @@ -246,8 +246,8 @@ abstract class AbstractTimelineStore(private val idGenerateService: IdGenerateSe val actors = getActors( timelineObjectList.map { it.postActorId } + - timelineObjectList.mapNotNull { it.repostActorId } + - timelineObjectList.mapNotNull { it.replyActorId } + timelineObjectList.mapNotNull { it.repostActorId } + + timelineObjectList.mapNotNull { it.replyActorId } ) val postMap = posts.associate { post -> @@ -256,7 +256,7 @@ abstract class AbstractTimelineStore(private val idGenerateService: IdGenerateSe val mediaMap = getMedias( posts.flatMap { it.mediaIds } + - actors.mapNotNull { it.value.icon } + actors.mapNotNull { it.value.icon } ) val reactions = getReactions(posts.map { it.id }) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/web/posts/PostsController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/web/posts/PostsController.kt index 26e31f3c..f1005327 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/web/posts/PostsController.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/web/posts/PostsController.kt @@ -47,7 +47,8 @@ class PostsController( id, null, "❤" - ), principal + ), + principal ) return "redirect:/users/$name/posts/$id" } @@ -60,7 +61,8 @@ class PostsController( id, null, "❤" - ), principal + ), + principal ) return "redirect:/users/$name/posts/$id" } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/reactions/ReactionsQueryService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/reactions/ReactionsQueryService.kt index 42164bc0..e410c4ae 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/reactions/ReactionsQueryService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/reactions/ReactionsQueryService.kt @@ -6,4 +6,4 @@ import dev.usbharu.hideout.core.domain.model.post.PostId interface ReactionsQueryService { suspend fun findAllByPostId(postId: PostId): List suspend fun findAllByPostIdIn(postIds: List): List -} \ No newline at end of file +} From 1544a161e6e39622b400424ae0f6d42431016587 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 9 Sep 2024 18:39:33 +0900 Subject: [PATCH 3/3] =?UTF-8?q?fix:=20Mastodon=E4=BA=92=E6=8F=9BAPI?= =?UTF-8?q?=E3=81=8B=E3=82=89=E3=83=95=E3=82=A9=E3=83=AD=E3=83=BC=E7=AD=89?= =?UTF-8?q?=E3=81=8C=E3=81=A7=E3=81=8D=E3=81=AA=E3=81=84=E5=95=8F=E9=A1=8C?= =?UTF-8?q?=E3=82=92=E3=82=B7=E3=83=A5=E3=82=B9=E3=82=A8=E3=82=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../accounts/GetAccountApplicationService.kt | 8 +-- .../MastodonReadTimelineApplicationService.kt | 1 - .../ExposedAccountQueryServiceImpl.kt | 12 ++-- .../interfaces/api/SpringAccountApi.kt | 61 +++++++++++++------ .../src/main/resources/openapi/mastodon.yaml | 2 - 5 files changed, 54 insertions(+), 30 deletions(-) diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/accounts/GetAccountApplicationService.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/accounts/GetAccountApplicationService.kt index 0696b53a..6e699fe1 100644 --- a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/accounts/GetAccountApplicationService.kt +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/accounts/GetAccountApplicationService.kt @@ -16,9 +16,9 @@ package dev.usbharu.hideout.mastodon.application.accounts -import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService +import dev.usbharu.hideout.core.application.shared.AbstractApplicationService import dev.usbharu.hideout.core.application.shared.Transaction -import dev.usbharu.hideout.core.domain.model.support.principal.LocalUser +import dev.usbharu.hideout.core.domain.model.support.principal.Principal import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.Account import dev.usbharu.hideout.mastodon.query.AccountQueryService import org.slf4j.LoggerFactory @@ -26,11 +26,11 @@ import org.springframework.stereotype.Service @Service class GetAccountApplicationService(private val accountQueryService: AccountQueryService, transaction: Transaction) : - LocalUserAbstractApplicationService( + AbstractApplicationService( transaction, logger ) { - override suspend fun internalExecute(command: GetAccount, principal: LocalUser): Account { + override suspend fun internalExecute(command: GetAccount, principal: Principal): Account { return accountQueryService.findById(command.accountId.toLong()) ?: throw IllegalArgumentException("Account ${command.accountId} not found") } diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/timeline/MastodonReadTimelineApplicationService.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/timeline/MastodonReadTimelineApplicationService.kt index 9858228b..be0319c8 100644 --- a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/timeline/MastodonReadTimelineApplicationService.kt +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/application/timeline/MastodonReadTimelineApplicationService.kt @@ -69,7 +69,6 @@ class MastodonReadTimelineApplicationService( lastStatusAt = it.postActor.lastPostAt?.toString(), followersCount = it.postActor.followersCount?.relationshipCount, followingCount = it.postActor.followingCount?.relationshipCount, - source = null ), content = it.post.content.content, visibility = when (it.post.visibility) { diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/ExposedAccountQueryServiceImpl.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/ExposedAccountQueryServiceImpl.kt index f4332cc9..4645c3af 100644 --- a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/ExposedAccountQueryServiceImpl.kt +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/infrastructure/exposedquery/ExposedAccountQueryServiceImpl.kt @@ -53,10 +53,10 @@ class ExposedAccountQueryServiceImpl(private val applicationConfig: ApplicationC url = resultRow[Actors.url], displayName = resultRow[Actors.screenName], note = resultRow[Actors.description], - avatar = userUrl + "/icon.jpg", - avatarStatic = userUrl + "/icon.jpg", - header = userUrl + "/header.jpg", - headerStatic = userUrl + "/header.jpg", + avatar = "$userUrl/icon.jpg", + avatarStatic = "$userUrl/icon.jpg", + header = "$userUrl/header.jpg", + headerStatic = "$userUrl/header.jpg", locked = resultRow[Actors.locked], fields = emptyList(), emojis = emptyList(), @@ -68,6 +68,10 @@ class ExposedAccountQueryServiceImpl(private val applicationConfig: ApplicationC statusesCount = resultRow[Actors.postsCount], followersCount = resultRow[Actors.followersCount], followingCount = resultRow[Actors.followingCount], + noindex = false, + moved = false, + suspended = resultRow[Actors.suspend], + limited = false, ) } } diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAccountApi.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAccountApi.kt index 6723e9ec..caac2717 100644 --- a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAccountApi.kt +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAccountApi.kt @@ -39,11 +39,16 @@ import dev.usbharu.hideout.core.application.relationship.unfollow.Unfollow import dev.usbharu.hideout.core.application.relationship.unfollow.UserUnfollowApplicationService import dev.usbharu.hideout.core.application.relationship.unmute.Unmute import dev.usbharu.hideout.core.application.relationship.unmute.UserUnmuteApplicationService +import dev.usbharu.hideout.core.domain.model.support.principal.Principal import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.SpringSecurityOauth2PrincipalContextHolder import dev.usbharu.hideout.mastodon.application.accounts.GetAccount import dev.usbharu.hideout.mastodon.application.accounts.GetAccountApplicationService import dev.usbharu.hideout.mastodon.interfaces.api.generated.AccountApi import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.* +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.asFlow +import kotlinx.coroutines.flow.mapNotNull +import kotlinx.coroutines.runBlocking import org.springframework.http.ResponseEntity import org.springframework.stereotype.Controller @@ -66,27 +71,30 @@ class SpringAccountApi( ) : AccountApi { override suspend fun apiV1AccountsIdBlockPost(id: String): ResponseEntity { - userBlockApplicationService.execute(Block(id.toLong()), principalContextHolder.getPrincipal()) - return fetchRelationship(id) + val principal = principalContextHolder.getPrincipal() + userBlockApplicationService.execute(Block(id.toLong()), principal) + return fetchRelationship(id, principal) } override suspend fun apiV1AccountsIdFollowPost( id: String, followRequestBody: FollowRequestBody?, ): ResponseEntity { + val principal = principalContextHolder.getPrincipal() userFollowRequestApplicationService.execute( FollowRequest(id.toLong()), - principalContextHolder.getPrincipal() + principal ) - return fetchRelationship(id) + return fetchRelationship(id, principal) } private suspend fun fetchRelationship( id: String, + principal: Principal ): ResponseEntity { val relationship = getRelationshipApplicationService.execute( GetRelationship(id.toLong()), - principalContextHolder.getPrincipal() + principal ) return ResponseEntity.ok( Relationship( @@ -117,43 +125,56 @@ class SpringAccountApi( } override suspend fun apiV1AccountsIdMutePost(id: String): ResponseEntity { + val principal = principalContextHolder.getPrincipal() userMuteApplicationService.execute( Mute(id.toLong()), - principalContextHolder.getPrincipal() + principal ) - return fetchRelationship(id) + return fetchRelationship(id, principal) } override suspend fun apiV1AccountsIdRemoveFromFollowersPost(id: String): ResponseEntity { + val principal = principalContextHolder.getPrincipal() userRemoveFromFollowersApplicationService.execute( RemoveFromFollowers(id.toLong()), - principalContextHolder.getPrincipal() + principal ) - return fetchRelationship(id) + return fetchRelationship(id, principal) } override suspend fun apiV1AccountsIdUnblockPost(id: String): ResponseEntity { + val principal = principalContextHolder.getPrincipal() userUnblockApplicationService.execute( Unblock(id.toLong()), - principalContextHolder.getPrincipal() + principal ) - return fetchRelationship(id) + return fetchRelationship(id, principal) } override suspend fun apiV1AccountsIdUnfollowPost(id: String): ResponseEntity { + val principal = principalContextHolder.getPrincipal() userUnfollowApplicationService.execute( Unfollow(id.toLong()), - principalContextHolder.getPrincipal() + principal ) - return fetchRelationship(id) + return fetchRelationship(id, principal) + } + + override fun apiV1AccountsRelationshipsGet( + id: List?, + withSuspended: Boolean + ): ResponseEntity> { + val principal = runBlocking { principalContextHolder.getPrincipal() } + return ResponseEntity.ok(id.orEmpty().asFlow().mapNotNull { fetchRelationship(it, principal).body }) } override suspend fun apiV1AccountsIdUnmutePost(id: String): ResponseEntity { + val principal = principalContextHolder.getPrincipal() userUnmuteApplicationService.execute( Unmute(id.toLong()), - principalContextHolder.getPrincipal() + principal ) - return fetchRelationship(id) + return fetchRelationship(id, principal) } override suspend fun apiV1AccountsPost(accountsCreateRequest: AccountsCreateRequest): ResponseEntity = @@ -220,18 +241,20 @@ class SpringAccountApi( } override suspend fun apiV1FollowRequestsAccountIdAuthorizePost(accountId: String): ResponseEntity { + val principal = principalContextHolder.getPrincipal() userAcceptFollowRequestApplicationService.execute( AcceptFollowRequest(accountId.toLong()), - principalContextHolder.getPrincipal() + principal ) - return fetchRelationship(accountId) + return fetchRelationship(accountId, principal) } override suspend fun apiV1FollowRequestsAccountIdRejectPost(accountId: String): ResponseEntity { + val principal = principalContextHolder.getPrincipal() userRejectFollowRequestApplicationService.execute( RejectFollowRequest(accountId.toLong()), - principalContextHolder.getPrincipal() + principal ) - return fetchRelationship(accountId) + return fetchRelationship(accountId, principal) } } diff --git a/hideout-mastodon/src/main/resources/openapi/mastodon.yaml b/hideout-mastodon/src/main/resources/openapi/mastodon.yaml index 1fb96267..4461417e 100644 --- a/hideout-mastodon/src/main/resources/openapi/mastodon.yaml +++ b/hideout-mastodon/src/main/resources/openapi/mastodon.yaml @@ -1597,8 +1597,6 @@ components: type: integer following_count: type: integer - source: - $ref: "#/components/schemas/AccountSource" required: - id