feat: Reactionを追加

This commit is contained in:
usbharu 2024-09-08 00:58:18 +09:00
parent 6c83306f0d
commit 72c9b8b7c5
Signed by: usbharu
GPG Key ID: 6556747BF94EEBC8
17 changed files with 116 additions and 34 deletions

View File

@ -0,0 +1,22 @@
package dev.usbharu.hideout.core.domain.event.reaction
import dev.usbharu.hideout.core.domain.model.reaction.Reaction
import dev.usbharu.hideout.core.domain.model.reaction.ReactionId
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEvent
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventBody
class ReactionEventFactory(private val reaction: Reaction) {
fun createEvent(reactionEvent: ReactionEvent): DomainEvent<ReactionEventBody> =
DomainEvent.create(reactionEvent.eventName, ReactionEventBody(reaction))
}
class ReactionEventBody(
reaction: Reaction
) : DomainEventBody(mapOf("reactionId" to reaction.id)) {
fun getReactionId(): ReactionId = toMap()["reactionId"] as ReactionId
}
enum class ReactionEvent(val eventName: String) {
CREATE("ReactionCreate"),
DELETE("ReactionDelete"),
}

View File

@ -18,7 +18,7 @@ package dev.usbharu.hideout.core.domain.model.actor
import dev.usbharu.hideout.core.domain.event.actor.ActorDomainEventFactory
import dev.usbharu.hideout.core.domain.event.actor.ActorEvent.*
import dev.usbharu.hideout.core.domain.model.emoji.EmojiId
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiId
import dev.usbharu.hideout.core.domain.model.instance.InstanceId
import dev.usbharu.hideout.core.domain.model.media.MediaId
import dev.usbharu.hideout.core.domain.model.support.domain.Domain
@ -52,7 +52,7 @@ class Actor(
var lastUpdateAt: Instant = createdAt,
alsoKnownAs: Set<ActorId> = emptySet(),
moveTo: ActorId? = null,
emojiIds: Set<EmojiId>,
emojiIds: Set<CustomEmojiId>,
deleted: Boolean,
icon: MediaId?,
banner: MediaId?,

View File

@ -36,7 +36,7 @@ sealed class Emoji {
}
data class CustomEmoji(
val id: EmojiId,
val id: CustomEmojiId,
override val name: String,
override val domain: Domain,
val instanceId: InstanceId,

View File

@ -17,7 +17,7 @@
package dev.usbharu.hideout.core.domain.model.emoji
@JvmInline
value class EmojiId(val emojiId: Long) {
value class CustomEmojiId(val emojiId: Long) {
init {
require(0 <= emojiId)
}

View File

@ -20,7 +20,7 @@ import dev.usbharu.hideout.core.domain.event.post.PostDomainEventFactory
import dev.usbharu.hideout.core.domain.event.post.PostEvent
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.emoji.EmojiId
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiId
import dev.usbharu.hideout.core.domain.model.instance.InstanceId
import dev.usbharu.hideout.core.domain.model.media.MediaId
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventStorable
@ -138,7 +138,7 @@ class Post(
return content.text
}
val emojiIds: List<EmojiId>
val emojiIds: List<CustomEmojiId>
get() {
if (hide) {
return PostContent.empty.emojiIds
@ -217,7 +217,7 @@ class Post(
override fun hashCode(): Int = id.hashCode()
fun reconstructWith(mediaIds: List<MediaId>, emojis: List<EmojiId>, visibleActors: Set<ActorId>): Post {
fun reconstructWith(mediaIds: List<MediaId>, emojis: List<CustomEmojiId>, visibleActors: Set<ActorId>): Post {
return Post(
id = id,
actorId = actorId,

View File

@ -16,9 +16,9 @@
package dev.usbharu.hideout.core.domain.model.post
import dev.usbharu.hideout.core.domain.model.emoji.EmojiId
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiId
data class PostContent(val text: String, val content: String, val emojiIds: List<EmojiId>) {
data class PostContent(val text: String, val content: String, val emojiIds: List<CustomEmojiId>) {
companion object {
val empty = PostContent("", "", emptyList())

View File

@ -0,0 +1,49 @@
package dev.usbharu.hideout.core.domain.model.reaction
import dev.usbharu.hideout.core.domain.event.reaction.ReactionEvent
import dev.usbharu.hideout.core.domain.event.reaction.ReactionEventFactory
import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiId
import dev.usbharu.hideout.core.domain.model.emoji.UnicodeEmoji
import dev.usbharu.hideout.core.domain.model.post.PostId
import dev.usbharu.hideout.core.domain.shared.domainevent.DomainEventStorable
import java.time.Instant
class Reaction(
val id: ReactionId,
val postId: PostId,
val actorId: ActorId,
val customEmojiId: CustomEmojiId?,
val unicodeEmoji: UnicodeEmoji,
val createdAt: Instant
) : DomainEventStorable() {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Reaction
return id == other.id
}
override fun hashCode(): Int {
return id.hashCode()
}
companion object {
fun create(
id: ReactionId,
postId: PostId,
actorId: ActorId,
customEmojiId: CustomEmojiId?,
unicodeEmoji: UnicodeEmoji,
createdAt: Instant
): Reaction {
return Reaction(
id, postId, actorId, customEmojiId, unicodeEmoji, createdAt
).apply { addDomainEvent(ReactionEventFactory(this).createEvent(ReactionEvent.CREATE)) }
}
}
}

View File

@ -0,0 +1,4 @@
package dev.usbharu.hideout.core.domain.model.reaction
@JvmInline
value class ReactionId(val value: Long)

View File

@ -0,0 +1,7 @@
package dev.usbharu.hideout.core.domain.model.reaction
interface ReactionRepository {
suspend fun save(reaction: Reaction): Reaction
suspend fun findById(reactionId: String): Reaction?
suspend fun delete(reaction: Reaction)
}

View File

@ -1,7 +1,7 @@
package dev.usbharu.hideout.core.domain.model.timelineobject
import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.emoji.EmojiId
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiId
import dev.usbharu.hideout.core.domain.model.filter.FilterResult
import dev.usbharu.hideout.core.domain.model.media.MediaId
import dev.usbharu.hideout.core.domain.model.post.Post
@ -28,13 +28,13 @@ class TimelineObject(
visibility: Visibility,
isPureRepost: Boolean,
mediaIds: List<MediaId>,
emojiIds: List<EmojiId>,
emojiIds: List<CustomEmojiId>,
visibleActors: List<ActorId>,
hasMediaInRepost: Boolean,
lastUpdatedAt: Instant,
var warnFilters: List<TimelineObjectWarnFilter>,
) {
) {
var isPureRepost = isPureRepost
private set
var visibleActors = visibleActors

View File

@ -17,7 +17,7 @@
package dev.usbharu.hideout.core.infrastructure.exposed
import dev.usbharu.hideout.core.domain.model.actor.*
import dev.usbharu.hideout.core.domain.model.emoji.EmojiId
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiId
import dev.usbharu.hideout.core.domain.model.instance.InstanceId
import dev.usbharu.hideout.core.domain.model.media.MediaId
import dev.usbharu.hideout.core.domain.model.support.domain.Domain
@ -57,7 +57,7 @@ class ActorResultRowMapper : ResultRowMapper<Actor> {
emojiIds = resultRow[Actors.emojis]
.split(",")
.filter { it.isNotEmpty() }
.map { EmojiId(it.toLong()) }
.map { CustomEmojiId(it.toLong()) }
.toSet(),
deleted = resultRow[Actors.deleted],
icon = resultRow[Actors.icon]?.let { MediaId(it) },

View File

@ -17,7 +17,7 @@
package dev.usbharu.hideout.core.infrastructure.exposed
import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.emoji.EmojiId
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiId
import dev.usbharu.hideout.core.domain.model.media.MediaId
import dev.usbharu.hideout.core.domain.model.post.Post
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Posts
@ -55,7 +55,7 @@ class PostQueryMapper(private val postResultRowMapper: ResultRowMapper<Post>) :
.mapNotNull { resultRow: ResultRow ->
resultRow
.getOrNull(PostsEmojis.emojiId)
?.let { emojiId -> EmojiId(emojiId) }
?.let { emojiId -> CustomEmojiId(emojiId) }
},
visibleActors = it.mapNotNull { resultRow: ResultRow ->
resultRow

View File

@ -17,8 +17,8 @@
package dev.usbharu.hideout.core.infrastructure.exposedrepository
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmoji
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiId
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiRepository
import dev.usbharu.hideout.core.domain.model.emoji.EmojiId
import dev.usbharu.hideout.core.domain.model.instance.InstanceId
import dev.usbharu.hideout.core.domain.model.support.domain.Domain
import org.jetbrains.exposed.sql.*
@ -81,7 +81,7 @@ class CustomEmojiRepositoryImpl : CustomEmojiRepository,
}
fun ResultRow.toCustomEmoji(): CustomEmoji = CustomEmoji(
id = EmojiId(this[CustomEmojis.id]),
id = CustomEmojiId(this[CustomEmojis.id]),
name = this[CustomEmojis.name],
domain = Domain(this[CustomEmojis.domain]),
instanceId = InstanceId(this[CustomEmojis.instanceId]),
@ -92,7 +92,7 @@ fun ResultRow.toCustomEmoji(): CustomEmoji = CustomEmoji(
fun ResultRow.toCustomEmojiOrNull(): CustomEmoji? {
return CustomEmoji(
id = EmojiId(this.getOrNull(CustomEmojis.id) ?: return null),
id = CustomEmojiId(this.getOrNull(CustomEmojis.id) ?: return null),
name = this.getOrNull(CustomEmojis.name) ?: return null,
domain = Domain(this.getOrNull(CustomEmojis.domain) ?: return null),
instanceId = InstanceId(this.getOrNull(CustomEmojis.instanceId) ?: return null),

View File

@ -1,7 +1,7 @@
package dev.usbharu.hideout.core.infrastructure.mongorepository
import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.emoji.EmojiId
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiId
import dev.usbharu.hideout.core.domain.model.filter.FilterId
import dev.usbharu.hideout.core.domain.model.media.MediaId
import dev.usbharu.hideout.core.domain.model.post.PostId
@ -151,7 +151,7 @@ data class SpringDataMongoTimelineObject(
visibility = visibility,
isPureRepost = isPureRepost,
mediaIds = mediaIds.map { MediaId(it) },
emojiIds = emojiIds.map { EmojiId(it) },
emojiIds = emojiIds.map { CustomEmojiId(it) },
visibleActors = visibleActors.map { ActorId(it) },
hasMediaInRepost = hasMediaInRepost,
lastUpdatedAt = Instant.ofEpochSecond(lastUpdatedAt),

View File

@ -1,6 +1,6 @@
package dev.usbharu.hideout.core.domain.model.actor
import dev.usbharu.hideout.core.domain.model.emoji.EmojiId
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiId
import dev.usbharu.hideout.core.domain.model.instance.InstanceId
import dev.usbharu.hideout.core.domain.model.support.domain.Domain
import dev.usbharu.hideout.core.infrastructure.other.TwitterSnowflakeIdGenerateService
@ -35,7 +35,7 @@ object TestActorFactory {
suspend: Boolean = false,
alsoKnownAs: Set<ActorId> = emptySet(),
moveTo: Long? = null,
emojiIds: Set<EmojiId> = emptySet(),
emojiIds: Set<CustomEmojiId> = emptySet(),
deleted: Boolean = false,
roles: Set<Role> = emptySet(),
): Actor {

View File

@ -3,18 +3,18 @@ package dev.usbharu.hideout.core.domain.model.emoji
import org.junit.jupiter.api.Assertions.assertDoesNotThrow
import org.junit.jupiter.api.Test
class EmojiIdTest {
class CustomEmojiIdTest {
@Test
fun emojiIdは0以上である必要がある() {
org.junit.jupiter.api.assertThrows<IllegalArgumentException> {
EmojiId(-1)
CustomEmojiId(-1)
}
}
@Test
fun emojiIdは0以上なら設定できる() {
assertDoesNotThrow {
EmojiId(1)
CustomEmojiId(1)
}
}
}

View File

@ -4,7 +4,7 @@ import dev.usbharu.hideout.core.domain.event.post.PostEvent
import dev.usbharu.hideout.core.domain.model.actor.ActorId
import dev.usbharu.hideout.core.domain.model.actor.ActorPublicKey
import dev.usbharu.hideout.core.domain.model.actor.TestActorFactory
import dev.usbharu.hideout.core.domain.model.emoji.EmojiId
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiId
import dev.usbharu.hideout.core.domain.model.media.MediaId
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
@ -447,7 +447,7 @@ class PostTest {
@Test
fun `emojiIds hideがtrueの時empty`() {
val actor = TestActorFactory.create()
val emojiIds = listOf(EmojiId(1), EmojiId(2))
val emojiIds = listOf(CustomEmojiId(1), CustomEmojiId(2))
val post = Post.create(
id = PostId(1),
actorId = actor.id,
@ -473,7 +473,7 @@ class PostTest {
@Test
fun `emojiIds hideがfalseの時中身が返される`() {
val actor = TestActorFactory.create()
val emojiIds = listOf(EmojiId(1), EmojiId(2))
val emojiIds = listOf(CustomEmojiId(1), CustomEmojiId(2))
val post = Post.create(
id = PostId(1),
actorId = actor.id,
@ -500,7 +500,7 @@ class PostTest {
val post = TestPostFactory.create()
val mediaIds = listOf<MediaId>(MediaId(1))
val visibleActors = setOf<ActorId>((ActorId(2)))
val emojis = listOf<EmojiId>(EmojiId(3))
val emojis = listOf<CustomEmojiId>(CustomEmojiId(3))
val reconstructWith = post.reconstructWith(mediaIds, emojis, visibleActors)
assertEquals(mediaIds, reconstructWith.mediaIds)
@ -511,7 +511,7 @@ class PostTest {
@Test
fun `mediaIds hideがtrueの時emptyが返される`() {
val actor = TestActorFactory.create()
val emojiIds = listOf(EmojiId(1), EmojiId(2))
val emojiIds = listOf(CustomEmojiId(1), CustomEmojiId(2))
val mediaIds = listOf(MediaId(1))
val post = Post.create(
id = PostId(1),
@ -538,7 +538,7 @@ class PostTest {
@Test
fun `mediaIds hideがfalseの時中身が返される`() {
val actor = TestActorFactory.create()
val emojiIds = listOf(EmojiId(1), EmojiId(2))
val emojiIds = listOf(CustomEmojiId(1), CustomEmojiId(2))
val mediaIds = listOf(MediaId(2))
val post = Post.create(
id = PostId(1),
@ -603,7 +603,7 @@ class PostTest {
fun `restore 指定された引数で再構成されCHECKUPDATEイベントが発生する`() {
val post = TestPostFactory.create(deleted = true)
val postContent = PostContent("aiueo", "aiueo", listOf(EmojiId(1)))
val postContent = PostContent("aiueo", "aiueo", listOf(CustomEmojiId(1)))
val overview = PostOverview("overview")
val mediaIds = listOf(MediaId(1))
post.restore(
@ -622,7 +622,7 @@ class PostTest {
fun deletedがfalseの時失敗する() {
val post = TestPostFactory.create(deleted = false)
val postContent = PostContent("aiueo", "aiueo", listOf(EmojiId(1)))
val postContent = PostContent("aiueo", "aiueo", listOf(CustomEmojiId(1)))
val overview = PostOverview("overview")
val mediaIds = listOf(MediaId(1))
assertThrows<IllegalArgumentException> {