Compare commits

..

4 Commits

9 changed files with 185 additions and 20 deletions

View File

@ -2,6 +2,7 @@ package dev.usbharu.hideout.activitypub.interfaces.api.actor
import dev.usbharu.hideout.activitypub.domain.model.Person
import dev.usbharu.hideout.activitypub.service.objects.user.APUserService
import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.RestController
@ -9,7 +10,11 @@ import org.springframework.web.bind.annotation.RestController
@RestController
class UserAPControllerImpl(private val apUserService: APUserService) : UserAPController {
override suspend fun userAp(username: String): ResponseEntity<Person> {
val person = apUserService.getPersonByName(username)
val person = try {
apUserService.getPersonByName(username)
} catch (e: FailedToGetResourcesException) {
return ResponseEntity.notFound().build()
}
person.context += listOf("https://www.w3.org/ns/activitystreams")
return ResponseEntity(person, HttpStatus.OK)
}

View File

@ -15,7 +15,7 @@ interface InboxController {
"application/activity+json",
"application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
],
method = [RequestMethod.GET, RequestMethod.POST]
method = [RequestMethod.POST]
)
suspend fun inbox(@RequestBody string: String): ResponseEntity<Unit>
}

View File

@ -1,7 +1,6 @@
package dev.usbharu.hideout.activitypub.interfaces.api.outbox
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestMethod
import org.springframework.web.bind.annotation.RestController
@ -9,5 +8,5 @@ import org.springframework.web.bind.annotation.RestController
@RestController
interface OutboxController {
@RequestMapping("/outbox", "/users/{username}/outbox", method = [RequestMethod.POST, RequestMethod.GET])
suspend fun outbox(@RequestBody string: String): ResponseEntity<Unit>
suspend fun outbox(): ResponseEntity<Unit>
}

View File

@ -2,11 +2,10 @@ package dev.usbharu.hideout.activitypub.interfaces.api.outbox
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RestController
@RestController
class OutboxControllerImpl : OutboxController {
override suspend fun outbox(@RequestBody string: String): ResponseEntity<Unit> =
override suspend fun outbox(): ResponseEntity<Unit> =
ResponseEntity(HttpStatus.NOT_IMPLEMENTED)
}

View File

@ -3,6 +3,7 @@ package dev.usbharu.hideout.activitypub.interfaces.api.webfinger
import dev.usbharu.hideout.activitypub.domain.model.webfinger.WebFinger
import dev.usbharu.hideout.activitypub.service.webfinger.WebFingerApiService
import dev.usbharu.hideout.application.config.ApplicationConfig
import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException
import dev.usbharu.hideout.util.AcctUtil
import kotlinx.coroutines.runBlocking
import org.slf4j.LoggerFactory
@ -26,8 +27,11 @@ class WebFingerController(
logger.warn("FAILED Parse acct.", e)
return@runBlocking ResponseEntity.badRequest().build()
}
val user =
val user = try {
webFingerApiService.findByNameAndDomain(acct.username, acct.domain ?: applicationConfig.url.host)
} catch (_: FailedToGetResourcesException) {
return@runBlocking ResponseEntity.notFound().build()
}
val webFinger = WebFinger(
"acct:${user.name}@${user.domain}",
listOf(

View File

@ -1,6 +1,7 @@
package dev.usbharu.hideout.core.service.reaction
import dev.usbharu.hideout.activitypub.service.activity.like.APReactionService
import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException
import dev.usbharu.hideout.core.domain.model.reaction.Reaction
import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository
import dev.usbharu.hideout.core.query.ReactionQueryService
@ -26,18 +27,26 @@ class ReactionServiceImpl(
}
override suspend fun sendReaction(name: String, userId: Long, postId: Long) {
if (reactionQueryService.reactionAlreadyExist(postId, userId, 0)) {
// delete
reactionQueryService.deleteByPostIdAndUserId(postId, userId)
} else {
val reaction = Reaction(reactionRepository.generateId(), 0, postId, userId)
reactionRepository.save(reaction)
apReactionService.reaction(reaction)
try {
val findByPostIdAndUserIdAndEmojiId =
reactionQueryService.findByPostIdAndUserIdAndEmojiId(postId, userId, 0)
apReactionService.removeReaction(findByPostIdAndUserIdAndEmojiId)
reactionRepository.delete(findByPostIdAndUserIdAndEmojiId)
} catch (_: FailedToGetResourcesException) {
}
val reaction = Reaction(reactionRepository.generateId(), 0, postId, userId)
reactionRepository.save(reaction)
apReactionService.reaction(reaction)
}
override suspend fun removeReaction(userId: Long, postId: Long) {
reactionQueryService.deleteByPostIdAndUserId(postId, userId)
try {
val findByPostIdAndUserIdAndEmojiId =
reactionQueryService.findByPostIdAndUserIdAndEmojiId(postId, userId, 0)
reactionRepository.delete(findByPostIdAndUserIdAndEmojiId)
apReactionService.removeReaction(findByPostIdAndUserIdAndEmojiId)
} catch (e: FailedToGetResourcesException) {
}
}
companion object {

View File

@ -0,0 +1,62 @@
package dev.usbharu.hideout.activitypub.interfaces.api.outbox
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.InjectMocks
import org.mockito.junit.jupiter.MockitoExtension
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.get
import org.springframework.test.web.servlet.post
import org.springframework.test.web.servlet.setup.MockMvcBuilders
@ExtendWith(MockitoExtension::class)
class OutboxControllerImplTest {
private lateinit var mockMvc: MockMvc
@InjectMocks
private lateinit var outboxController: OutboxControllerImpl
@BeforeEach
fun setUp() {
mockMvc =
MockMvcBuilders.standaloneSetup(outboxController).build()
}
@Test
fun `outbox GETに501を返す`() {
mockMvc
.get("/outbox")
.asyncDispatch()
.andDo { print() }
.andExpect { status { isNotImplemented() } }
}
@Test
fun `user-outbox GETに501を返す`() {
mockMvc
.get("/users/hoge/outbox")
.asyncDispatch()
.andDo { print() }
.andExpect { status { isNotImplemented() } }
}
@Test
fun `outbox POSTに501を返す`() {
mockMvc
.post("/outbox")
.asyncDispatch()
.andDo { print() }
.andExpect { status { isNotImplemented() } }
}
@Test
fun `user-outbox POSTに501を返す`() {
mockMvc
.post("/users/hoge/outbox")
.asyncDispatch()
.andDo { print() }
.andExpect { status { isNotImplemented() } }
}
}

View File

@ -0,0 +1,79 @@
package dev.usbharu.hideout.activitypub.service.activity.create
import com.fasterxml.jackson.databind.ObjectMapper
import dev.usbharu.hideout.activitypub.domain.model.Note
import dev.usbharu.hideout.activitypub.query.NoteQueryService
import dev.usbharu.hideout.activitypub.service.objects.note.APNoteServiceImpl
import dev.usbharu.hideout.application.config.ActivityPubConfig
import dev.usbharu.hideout.application.config.ApplicationConfig
import dev.usbharu.hideout.core.external.job.DeliverPostJob
import dev.usbharu.hideout.core.query.FollowerQueryService
import dev.usbharu.hideout.core.query.UserQueryService
import dev.usbharu.hideout.core.service.job.JobQueueParentService
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.Spy
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.*
import utils.PostBuilder
import utils.UserBuilder
import java.net.URL
import java.time.Instant
@ExtendWith(MockitoExtension::class)
class ApSendCreateServiceImplTest {
@Mock
private lateinit var followerQueryService: FollowerQueryService
@Spy
private val objectMapper: ObjectMapper = ActivityPubConfig().objectMapper()
@Mock
private lateinit var jobQueueParentService: JobQueueParentService
@Mock
private lateinit var userQueryService: UserQueryService
@Mock
private lateinit var noteQueryService: NoteQueryService
@Spy
private val applicationConfig: ApplicationConfig = ApplicationConfig(URL("https://example.com"))
@InjectMocks
private lateinit var apSendCreateServiceImpl: ApSendCreateServiceImpl
@Test
fun `createNote 正常なPostでCreateのジョブを発行できる`() = runTest {
val post = PostBuilder.of()
val user = UserBuilder.localUserOf(id = post.userId)
val note = Note(
name = "Post",
id = post.apId,
attributedTo = user.url,
content = post.text,
published = Instant.ofEpochMilli(post.createdAt).toString(),
to = listOfNotNull(APNoteServiceImpl.public, user.followers),
sensitive = post.sensitive,
cc = listOfNotNull(APNoteServiceImpl.public, user.followers),
inReplyTo = null
)
val followers = listOf(
UserBuilder.remoteUserOf(),
UserBuilder.remoteUserOf(),
UserBuilder.remoteUserOf()
)
whenever(followerQueryService.findFollowersById(eq(post.userId))).doReturn(followers)
whenever(userQueryService.findById(eq(post.userId))).doReturn(user)
whenever(noteQueryService.findById(eq(post.id))).doReturn(note to post)
apSendCreateServiceImpl.createNote(post)
verify(jobQueueParentService, times(followers.size)).schedule(eq(DeliverPostJob), any())
}
}

View File

@ -3,6 +3,7 @@ package dev.usbharu.hideout.core.service.reaction
import dev.usbharu.hideout.activitypub.service.activity.like.APReactionService
import dev.usbharu.hideout.application.service.id.TwitterSnowflakeIdGenerateService
import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException
import dev.usbharu.hideout.core.domain.model.reaction.Reaction
import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository
import dev.usbharu.hideout.core.query.ReactionQueryService
@ -88,7 +89,9 @@ class ReactionServiceImplTest {
@Test
fun `sendReaction リアクションが存在しないとき保存して配送する`() = runTest {
val post = PostBuilder.of()
whenever(reactionQueryService.reactionAlreadyExist(eq(post.id), eq(post.userId), eq(0))).doReturn(false)
whenever(reactionQueryService.findByPostIdAndUserIdAndEmojiId(eq(post.id), eq(post.userId), eq(0))).doThrow(
FailedToGetResourcesException::class
)
val generateId = TwitterSnowflakeIdGenerateService.generateId()
whenever(reactionRepository.generateId()).doReturn(generateId)
@ -101,23 +104,28 @@ class ReactionServiceImplTest {
@Test
fun `sendReaction リアクションが存在するときは削除して保存して配送する`() = runTest {
val post = PostBuilder.of()
whenever(reactionQueryService.reactionAlreadyExist(eq(post.id), eq(post.userId), eq(0))).doReturn(true)
val id = TwitterSnowflakeIdGenerateService.generateId()
whenever(reactionQueryService.findByPostIdAndUserIdAndEmojiId(eq(post.id), eq(post.userId), eq(0))).doReturn(
Reaction(id, 0, post.id, post.userId)
)
val generateId = TwitterSnowflakeIdGenerateService.generateId()
whenever(reactionRepository.generateId()).doReturn(generateId)
reactionServiceImpl.sendReaction("", post.userId, post.id)
verify(reactionRepository, times(1)).delete(eq(Reaction(generateId, 0, post.id, post.userId)))
verify(reactionRepository, times(1)).delete(eq(Reaction(id, 0, post.id, post.userId)))
verify(reactionRepository, times(1)).save(eq(Reaction(generateId, 0, post.id, post.userId)))
verify(apReactionService, times(1)).removeReaction(eq(Reaction(generateId, 0, post.id, post.userId)))
verify(apReactionService, times(1)).removeReaction(eq(Reaction(id, 0, post.id, post.userId)))
verify(apReactionService, times(1)).reaction(eq(Reaction(generateId, 0, post.id, post.userId)))
}
@Test
fun `removeReaction リアクションが存在する場合削除して配送`() = runTest {
val post = PostBuilder.of()
whenever(reactionQueryService.reactionAlreadyExist(eq(post.id), eq(post.userId), eq(0))).doReturn(true)
whenever(reactionQueryService.findByPostIdAndUserIdAndEmojiId(eq(post.id), eq(post.userId), eq(0))).doReturn(
Reaction(0, 0, post.id, post.userId)
)
reactionServiceImpl.removeReaction(post.userId, post.id)