mirror of https://github.com/usbharu/Hideout.git
feat: 絵文字リアクションAPIを修正
This commit is contained in:
parent
a5618e3307
commit
f06961b0bd
|
@ -1,7 +1,15 @@
|
|||
package mastodon.status
|
||||
|
||||
import dev.usbharu.hideout.SpringApplication
|
||||
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmoji
|
||||
import dev.usbharu.hideout.core.domain.model.emoji.UnicodeEmoji
|
||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.CustomEmojis
|
||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.Reactions
|
||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.toReaction
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.flywaydb.core.Flyway
|
||||
import org.jetbrains.exposed.sql.and
|
||||
import org.jetbrains.exposed.sql.select
|
||||
import org.junit.jupiter.api.AfterAll
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
|
@ -13,19 +21,24 @@ import org.springframework.security.core.authority.SimpleGrantedAuthority
|
|||
import org.springframework.security.test.context.support.WithAnonymousUser
|
||||
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors
|
||||
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf
|
||||
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.jwt
|
||||
import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers
|
||||
import org.springframework.test.context.jdbc.Sql
|
||||
import org.springframework.test.web.servlet.MockMvc
|
||||
import org.springframework.test.web.servlet.post
|
||||
import org.springframework.test.web.servlet.put
|
||||
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders
|
||||
import org.springframework.transaction.annotation.Transactional
|
||||
import org.springframework.web.context.WebApplicationContext
|
||||
import java.time.Instant
|
||||
|
||||
@SpringBootTest(classes = [SpringApplication::class])
|
||||
@AutoConfigureMockMvc
|
||||
@Transactional
|
||||
@Sql("/sql/test-user.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS)
|
||||
@Sql("/sql/test-post.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS)
|
||||
@Sql("/sql/test-custom-emoji.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS)
|
||||
class StatusTest {
|
||||
|
||||
@Autowired
|
||||
|
@ -124,7 +137,6 @@ class StatusTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Sql("/sql/test-post.sql")
|
||||
fun in_reply_to_idを指定したら返信として処理される() {
|
||||
mockMvc
|
||||
.post("/api/v1/statuses") {
|
||||
|
@ -145,6 +157,64 @@ class StatusTest {
|
|||
.andExpect { jsonPath("\$.in_reply_to_id") { value("1") } }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun ユニコード絵文字をリアクションできる() {
|
||||
mockMvc
|
||||
.put("/api/v1/statuses/1/emoji_reactions/😭") {
|
||||
with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write")))
|
||||
}
|
||||
.andDo { print() }
|
||||
.asyncDispatch()
|
||||
.andExpect { status { isOk() } }
|
||||
|
||||
val reaction = Reactions.select { Reactions.postId eq 1 and (Reactions.actorId eq 1) }.single().toReaction()
|
||||
assertThat(reaction.emoji).isEqualTo(UnicodeEmoji("😭"))
|
||||
assertThat(reaction.postId).isEqualTo(1)
|
||||
assertThat(reaction.actorId).isEqualTo(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun 存在しない絵文字はフォールバックされる() {
|
||||
mockMvc
|
||||
.put("/api/v1/statuses/1/emoji_reactions/hoge") {
|
||||
with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write")))
|
||||
}
|
||||
.andDo { print() }
|
||||
.asyncDispatch()
|
||||
.andExpect { status { isOk() } }
|
||||
|
||||
val reaction = Reactions.select { Reactions.postId eq 1 and (Reactions.actorId eq 1) }.single().toReaction()
|
||||
assertThat(reaction.emoji).isEqualTo(UnicodeEmoji("❤"))
|
||||
assertThat(reaction.postId).isEqualTo(1)
|
||||
assertThat(reaction.actorId).isEqualTo(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun カスタム絵文字をリアクションできる() {
|
||||
mockMvc
|
||||
.put("/api/v1/statuses/1/emoji_reactions/kotlin") {
|
||||
with(jwt().jwt { it.claim("uid", "1") }.authorities(SimpleGrantedAuthority("SCOPE_write")))
|
||||
}
|
||||
.andDo { print() }
|
||||
.asyncDispatch()
|
||||
.andExpect { status { isOk() } }
|
||||
|
||||
val reaction =
|
||||
Reactions.leftJoin(CustomEmojis).select { Reactions.postId eq 1 and (Reactions.actorId eq 1) }.single()
|
||||
.toReaction()
|
||||
assertThat(reaction.emoji).isEqualTo(
|
||||
CustomEmoji(
|
||||
1,
|
||||
"kotlin",
|
||||
"example.com",
|
||||
null,
|
||||
"https://example.com/emojis/kotlin",
|
||||
null,
|
||||
Instant.ofEpochMilli(1704700290036)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
@AfterAll
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
insert into emojis(id, name, domain, instance_id, url, category, created_at)
|
||||
VALUES (1, 'kotlin', 'example.com', null, 'https://example.com/emojis/kotlin', null,
|
||||
TIMESTAMP '2024-01-08 16:51:30.036');
|
|
@ -74,6 +74,18 @@ fun ResultRow.toCustomEmoji(): CustomEmoji = CustomEmoji(
|
|||
this[CustomEmojis.createdAt]
|
||||
)
|
||||
|
||||
fun ResultRow.toCustomEmojiOrNull(): CustomEmoji? {
|
||||
return CustomEmoji(
|
||||
id = this.getOrNull(CustomEmojis.id) ?: return null,
|
||||
name = this.getOrNull(CustomEmojis.name) ?: return null,
|
||||
domain = this.getOrNull(CustomEmojis.domain) ?: return null,
|
||||
instanceId = this[CustomEmojis.instanceId],
|
||||
url = this.getOrNull(CustomEmojis.url) ?: return null,
|
||||
category = this[CustomEmojis.category],
|
||||
createdAt = this.getOrNull(CustomEmojis.createdAt) ?: return null
|
||||
)
|
||||
}
|
||||
|
||||
object CustomEmojis : Table("emojis") {
|
||||
val id = long("id")
|
||||
val name = varchar("name", 1000)
|
||||
|
|
|
@ -109,7 +109,22 @@ class StatusQueryServiceImpl : StatusQueryService {
|
|||
}
|
||||
|
||||
override suspend fun findByPostId(id: Long): Status {
|
||||
TODO("Not yet implemented")
|
||||
val map = Posts
|
||||
.leftJoin(PostsMedia)
|
||||
.leftJoin(Actors)
|
||||
.leftJoin(Media)
|
||||
.select { Posts.id eq id }
|
||||
.groupBy { it[Posts.id] }
|
||||
.map { it.value }
|
||||
.map {
|
||||
toStatus(it.first()).copy(
|
||||
mediaAttachments = it.mapNotNull { resultRow ->
|
||||
resultRow.toMediaOrNull()?.toMediaAttachments()
|
||||
},
|
||||
emojis = it.mapNotNull { resultRow -> resultRow.toCustomEmojiOrNull()?.toMastodonEmoji() }
|
||||
) to it.first()[Posts.repostId]
|
||||
}
|
||||
return resolveReplyAndRepost(map).single()
|
||||
}
|
||||
|
||||
private fun resolveReplyAndRepost(pairs: List<Pair<Status, Long?>>): List<Status> {
|
||||
|
|
|
@ -26,11 +26,17 @@ class MastodonStatusesApiContoller(private val statusesApiService: StatusesApiSe
|
|||
}
|
||||
|
||||
override suspend fun apiV1StatusesIdEmojiReactionsEmojiDelete(id: String, emoji: String): ResponseEntity<Status> {
|
||||
return super.apiV1StatusesIdEmojiReactionsEmojiDelete(id, emoji)
|
||||
val uid =
|
||||
(SecurityContextHolder.getContext().authentication.principal as Jwt).getClaim<String>("uid").toLong()
|
||||
|
||||
return ResponseEntity.ok(statusesApiService.removeEmojiReactions(id.toLong(), uid, emoji))
|
||||
}
|
||||
|
||||
override suspend fun apiV1StatusesIdEmojiReactionsEmojiPut(id: String, emoji: String): ResponseEntity<Status> {
|
||||
return super.apiV1StatusesIdEmojiReactionsEmojiPut(id, emoji)
|
||||
val uid =
|
||||
(SecurityContextHolder.getContext().authentication.principal as Jwt).getClaim<String>("uid").toLong()
|
||||
|
||||
return ResponseEntity.ok(statusesApiService.emojiReactions(id.toLong(), uid, emoji))
|
||||
}
|
||||
|
||||
override suspend fun apiV1StatusesIdGet(id: String): ResponseEntity<Status> {
|
||||
|
|
Loading…
Reference in New Issue