From caa93f76562b718d670b05dad2ad22e7a3011d8b Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 22 May 2023 15:35:45 +0900 Subject: [PATCH 1/4] =?UTF-8?q?refactor:=20ActivityPubFollowService?= =?UTF-8?q?=E3=82=92ActivityPubReceiveFollowService=E3=81=AB=E3=83=AA?= =?UTF-8?q?=E3=83=8D=E3=83=BC=E3=83=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...FollowService.kt => ActivityPubReceiveFollowService.kt} | 2 +- ...rviceImpl.kt => ActivityPubReceiveFollowServiceImpl.kt} | 4 ++-- .../hideout/service/activitypub/ActivityPubServiceImpl.kt | 6 +++--- ...lTest.kt => ActivityPubReceiveFollowServiceImplTest.kt} | 7 ++++--- 4 files changed, 10 insertions(+), 9 deletions(-) rename src/main/kotlin/dev/usbharu/hideout/service/activitypub/{ActivityPubFollowService.kt => ActivityPubReceiveFollowService.kt} (89%) rename src/main/kotlin/dev/usbharu/hideout/service/activitypub/{ActivityPubFollowServiceImpl.kt => ActivityPubReceiveFollowServiceImpl.kt} (96%) rename src/test/kotlin/dev/usbharu/hideout/service/activitypub/{ActivityPubFollowServiceImplTest.kt => ActivityPubReceiveFollowServiceImplTest.kt} (96%) diff --git a/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubFollowService.kt b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubReceiveFollowService.kt similarity index 89% rename from src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubFollowService.kt rename to src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubReceiveFollowService.kt index 40e45767..378b0db6 100644 --- a/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubFollowService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubReceiveFollowService.kt @@ -5,7 +5,7 @@ import dev.usbharu.hideout.domain.model.ap.Follow import dev.usbharu.hideout.domain.model.job.ReceiveFollowJob import kjob.core.job.JobProps -interface ActivityPubFollowService { +interface ActivityPubReceiveFollowService { suspend fun receiveFollow(follow: Follow): ActivityPubResponse suspend fun receiveFollowJob(props: JobProps) } diff --git a/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubFollowServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubReceiveFollowServiceImpl.kt similarity index 96% rename from src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubFollowServiceImpl.kt rename to src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubReceiveFollowServiceImpl.kt index 478629d8..31a64738 100644 --- a/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubFollowServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubReceiveFollowServiceImpl.kt @@ -16,12 +16,12 @@ import kjob.core.job.JobProps import org.koin.core.annotation.Single @Single -class ActivityPubFollowServiceImpl( +class ActivityPubReceiveFollowServiceImpl( private val jobQueueParentService: JobQueueParentService, private val activityPubUserService: ActivityPubUserService, private val userService: IUserService, private val httpClient: HttpClient -) : ActivityPubFollowService { +) : ActivityPubReceiveFollowService { override suspend fun receiveFollow(follow: Follow): ActivityPubResponse { // TODO: Verify HTTP Signature jobQueueParentService.schedule(ReceiveFollowJob) { diff --git a/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubServiceImpl.kt index 38bb6bf4..dbcc3af2 100644 --- a/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubServiceImpl.kt @@ -17,7 +17,7 @@ import org.slf4j.LoggerFactory @Single class ActivityPubServiceImpl( - private val activityPubFollowService: ActivityPubFollowService, + private val activityPubReceiveFollowService: ActivityPubReceiveFollowService, private val activityPubNoteService: ActivityPubNoteService, private val activityPubUndoService: ActivityPubUndoService ) : ActivityPubService { @@ -50,7 +50,7 @@ class ActivityPubServiceImpl( ActivityType.Delete -> TODO() ActivityType.Dislike -> TODO() ActivityType.Flag -> TODO() - ActivityType.Follow -> activityPubFollowService.receiveFollow( + ActivityType.Follow -> activityPubReceiveFollowService.receiveFollow( Config.configData.objectMapper.readValue( json, Follow::class.java @@ -82,7 +82,7 @@ class ActivityPubServiceImpl( override suspend fun processActivity(job: JobContextWithProps, hideoutJob: HideoutJob) { logger.debug("processActivity: ${hideoutJob.name}") when (hideoutJob) { - ReceiveFollowJob -> activityPubFollowService.receiveFollowJob(job.props as JobProps) + ReceiveFollowJob -> activityPubReceiveFollowService.receiveFollowJob(job.props as JobProps) DeliverPostJob -> activityPubNoteService.createNoteJob(job.props as JobProps) } } diff --git a/src/test/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubFollowServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubReceiveFollowServiceImplTest.kt similarity index 96% rename from src/test/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubFollowServiceImplTest.kt rename to src/test/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubReceiveFollowServiceImplTest.kt index 2e5b3af0..29525057 100644 --- a/src/test/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubFollowServiceImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubReceiveFollowServiceImplTest.kt @@ -25,13 +25,14 @@ import org.mockito.kotlin.* import utils.JsonObjectMapper import java.time.Instant -class ActivityPubFollowServiceImplTest { +class ActivityPubReceiveFollowServiceImplTest { @Test fun `receiveFollow フォロー受付処理`() = runTest { val jobQueueParentService = mock { onBlocking { schedule(eq(ReceiveFollowJob), any()) } doReturn Unit } - val activityPubFollowService = ActivityPubFollowServiceImpl(jobQueueParentService, mock(), mock(), mock()) + val activityPubFollowService = + ActivityPubReceiveFollowServiceImpl(jobQueueParentService, mock(), mock(), mock()) activityPubFollowService.receiveFollow( Follow( emptyList(), @@ -118,7 +119,7 @@ class ActivityPubFollowServiceImplTest { onBlocking { follow(any(), any()) } doReturn false } val activityPubFollowService = - ActivityPubFollowServiceImpl( + ActivityPubReceiveFollowServiceImpl( mock(), activityPubUserService, userService, From 4ab747825eecfda0c1202df766e346412b47bc45 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 22 May 2023 16:08:28 +0900 Subject: [PATCH 2/4] =?UTF-8?q?feat:=20Follow=E3=82=92=E9=80=81=E3=82=8C?= =?UTF-8?q?=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 --- .../domain/model/hideout/dto/SendFollowDto.kt | 5 ++++ .../ActivityPubSendFollowService.kt | 7 ++++++ .../ActivityPubSendFollowServiceImpl.kt | 23 ++++++++++++++++++ .../hideout/service/impl/UserService.kt | 24 +++++++++++++++---- .../hideout/service/impl/UserServiceTest.kt | 4 ++-- 5 files changed, 57 insertions(+), 6 deletions(-) create mode 100644 src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/dto/SendFollowDto.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubSendFollowService.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubSendFollowServiceImpl.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/dto/SendFollowDto.kt b/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/dto/SendFollowDto.kt new file mode 100644 index 00000000..595bb695 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/dto/SendFollowDto.kt @@ -0,0 +1,5 @@ +package dev.usbharu.hideout.domain.model.hideout.dto + +import dev.usbharu.hideout.domain.model.hideout.entity.User + +data class SendFollowDto(val userId: User, val followTargetUserId: User) diff --git a/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubSendFollowService.kt b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubSendFollowService.kt new file mode 100644 index 00000000..8d0dd1f2 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubSendFollowService.kt @@ -0,0 +1,7 @@ +package dev.usbharu.hideout.service.activitypub + +import dev.usbharu.hideout.domain.model.hideout.dto.SendFollowDto + +interface ActivityPubSendFollowService { + suspend fun sendFollow(sendFollowDto: SendFollowDto) +} diff --git a/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubSendFollowServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubSendFollowServiceImpl.kt new file mode 100644 index 00000000..e73e9266 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubSendFollowServiceImpl.kt @@ -0,0 +1,23 @@ +package dev.usbharu.hideout.service.activitypub + +import dev.usbharu.hideout.domain.model.ap.Follow +import dev.usbharu.hideout.domain.model.hideout.dto.SendFollowDto +import dev.usbharu.hideout.plugins.postAp +import io.ktor.client.* +import org.koin.core.annotation.Single + +@Single +class ActivityPubSendFollowServiceImpl(private val httpClient: HttpClient) : ActivityPubSendFollowService { + override suspend fun sendFollow(sendFollowDto: SendFollowDto) { + val follow = Follow( + name = "Follow", + `object` = sendFollowDto.followTargetUserId.url, + actor = sendFollowDto.userId.url + ) + httpClient.postAp( + urlString = sendFollowDto.followTargetUserId.inbox, + username = sendFollowDto.userId.url, + jsonLd = follow + ) + } +} diff --git a/src/main/kotlin/dev/usbharu/hideout/service/impl/UserService.kt b/src/main/kotlin/dev/usbharu/hideout/service/impl/UserService.kt index d2037fa7..23ccaa6c 100644 --- a/src/main/kotlin/dev/usbharu/hideout/service/impl/UserService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/service/impl/UserService.kt @@ -2,17 +2,23 @@ package dev.usbharu.hideout.service.impl import dev.usbharu.hideout.config.Config import dev.usbharu.hideout.domain.model.hideout.dto.RemoteUserCreateDto +import dev.usbharu.hideout.domain.model.hideout.dto.SendFollowDto import dev.usbharu.hideout.domain.model.hideout.dto.UserCreateDto import dev.usbharu.hideout.domain.model.hideout.entity.User import dev.usbharu.hideout.exception.UserNotFoundException import dev.usbharu.hideout.repository.IUserRepository import dev.usbharu.hideout.service.IUserAuthService +import dev.usbharu.hideout.service.activitypub.ActivityPubSendFollowService import org.koin.core.annotation.Single import java.lang.Integer.min import java.time.Instant @Single -class UserService(private val userRepository: IUserRepository, private val userAuthService: IUserAuthService) : +class UserService( + private val userRepository: IUserRepository, + private val userAuthService: IUserAuthService, + private val activityPubSendFollowService: ActivityPubSendFollowService +) : IUserService { private val maxLimit = 100 @@ -105,9 +111,19 @@ class UserService(private val userRepository: IUserRepository, private val userA } // TODO APのフォロー処理を作る - override suspend fun follow(id: Long, follower: Long): Boolean { - userRepository.createFollower(id, follower) - return false + override suspend fun follow(id: Long, followerId: Long): Boolean { + val user = userRepository.findById(id) ?: throw UserNotFoundException("$id was not found.") + val follower = userRepository.findById(followerId) ?: throw UserNotFoundException("$followerId was not found.") + if (follower.domain != Config.configData.domain) { + throw IllegalArgumentException("follower is not local user.") + } + return if (user.domain == Config.configData.domain) { + userRepository.createFollower(id, followerId) + true + } else { + activityPubSendFollowService.sendFollow(SendFollowDto(follower, user)) + false + } } override suspend fun unfollow(id: Long, follower: Long): Boolean { diff --git a/src/test/kotlin/dev/usbharu/hideout/service/impl/UserServiceTest.kt b/src/test/kotlin/dev/usbharu/hideout/service/impl/UserServiceTest.kt index deab1ce6..3b182e3b 100644 --- a/src/test/kotlin/dev/usbharu/hideout/service/impl/UserServiceTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/service/impl/UserServiceTest.kt @@ -29,7 +29,7 @@ class UserServiceTest { onBlocking { hash(anyString()) } doReturn "hashedPassword" onBlocking { generateKeyPair() } doReturn generateKeyPair } - val userService = UserService(userRepository, userAuthService) + val userService = UserService(userRepository, userAuthService, mock()) userService.createLocalUser(UserCreateDto("test", "testUser", "XXXXXXXXXXXXX", "test")) verify(userRepository, times(1)).save(any()) argumentCaptor { @@ -55,7 +55,7 @@ class UserServiceTest { val userRepository = mock { onBlocking { nextId() } doReturn 113345L } - val userService = UserService(userRepository, mock()) + val userService = UserService(userRepository, mock(), mock()) val user = RemoteUserCreateDto( "test", "example.com", From 0f234e01a56be7d1dd27f62ee8fb57084de56cd5 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 22 May 2023 16:36:49 +0900 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20Accept=E3=82=92=E5=8F=97=E3=81=91?= =?UTF-8?q?=E5=8F=96=E3=81=A3=E3=81=9F=E3=81=A8=E3=81=8D=E3=81=AE=E5=87=A6?= =?UTF-8?q?=E7=90=86=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../activitypub/ActivityPubAcceptService.kt | 8 ++++++ .../ActivityPubAcceptServiceImpl.kt | 28 +++++++++++++++++++ .../activitypub/ActivityPubServiceImpl.kt | 13 +++++---- 3 files changed, 43 insertions(+), 6 deletions(-) create mode 100644 src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubAcceptService.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubAcceptServiceImpl.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubAcceptService.kt b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubAcceptService.kt new file mode 100644 index 00000000..d0746c44 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubAcceptService.kt @@ -0,0 +1,8 @@ +package dev.usbharu.hideout.service.activitypub + +import dev.usbharu.hideout.domain.model.ActivityPubResponse +import dev.usbharu.hideout.domain.model.ap.Accept + +interface ActivityPubAcceptService { + suspend fun receiveAccept(accept: Accept): ActivityPubResponse +} diff --git a/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubAcceptServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubAcceptServiceImpl.kt new file mode 100644 index 00000000..f37428c2 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubAcceptServiceImpl.kt @@ -0,0 +1,28 @@ +package dev.usbharu.hideout.service.activitypub + +import dev.usbharu.hideout.domain.model.ActivityPubResponse +import dev.usbharu.hideout.domain.model.ActivityPubStringResponse +import dev.usbharu.hideout.domain.model.ap.Accept +import dev.usbharu.hideout.domain.model.ap.Follow +import dev.usbharu.hideout.exception.ap.IllegalActivityPubObjectException +import dev.usbharu.hideout.service.impl.IUserService +import io.ktor.http.* +import org.koin.core.annotation.Single + +@Single +class ActivityPubAcceptServiceImpl(private val userService: IUserService) : ActivityPubAcceptService { + override suspend fun receiveAccept(accept: Accept): ActivityPubResponse { + val value = accept.`object` ?: throw IllegalActivityPubObjectException("object is null") + if (value.type.contains("Follow").not()) { + throw IllegalActivityPubObjectException("Invalid type ${value.type}") + } + + val follow = value as Follow + val userUrl = follow.`object` ?: throw IllegalActivityPubObjectException("object is null") + val followerUrl = follow.actor ?: throw IllegalActivityPubObjectException("actor is null") + val user = userService.findByUrl(userUrl) + val follower = userService.findByUrl(followerUrl) + userService.follow(user.id, follower.id) + return ActivityPubStringResponse(HttpStatusCode.OK, "accepted") + } +} diff --git a/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubServiceImpl.kt index dbcc3af2..1c9a11e7 100644 --- a/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/service/activitypub/ActivityPubServiceImpl.kt @@ -2,7 +2,7 @@ package dev.usbharu.hideout.service.activitypub import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.module.kotlin.readValue -import dev.usbharu.hideout.config.Config +import dev.usbharu.hideout.config.Config.configData import dev.usbharu.hideout.domain.model.ActivityPubResponse import dev.usbharu.hideout.domain.model.ap.Follow import dev.usbharu.hideout.domain.model.job.DeliverPostJob @@ -19,12 +19,13 @@ import org.slf4j.LoggerFactory class ActivityPubServiceImpl( private val activityPubReceiveFollowService: ActivityPubReceiveFollowService, private val activityPubNoteService: ActivityPubNoteService, - private val activityPubUndoService: ActivityPubUndoService + private val activityPubUndoService: ActivityPubUndoService, + private val activityPubAcceptService: ActivityPubAcceptService ) : ActivityPubService { val logger: Logger = LoggerFactory.getLogger(this::class.java) override fun parseActivity(json: String): ActivityType { - val readTree = Config.configData.objectMapper.readTree(json) + val readTree = configData.objectMapper.readTree(json) logger.debug("readTree: {}", readTree) if (readTree.isObject.not()) { throw JsonParseException("Json is not object.") @@ -41,7 +42,7 @@ class ActivityPubServiceImpl( @Suppress("CyclomaticComplexMethod", "NotImplementedDeclaration") override suspend fun processActivity(json: String, type: ActivityType): ActivityPubResponse { return when (type) { - ActivityType.Accept -> TODO() + ActivityType.Accept -> activityPubAcceptService.receiveAccept(configData.objectMapper.readValue(json)) ActivityType.Add -> TODO() ActivityType.Announce -> TODO() ActivityType.Arrive -> TODO() @@ -51,7 +52,7 @@ class ActivityPubServiceImpl( ActivityType.Dislike -> TODO() ActivityType.Flag -> TODO() ActivityType.Follow -> activityPubReceiveFollowService.receiveFollow( - Config.configData.objectMapper.readValue( + configData.objectMapper.readValue( json, Follow::class.java ) @@ -72,7 +73,7 @@ class ActivityPubServiceImpl( ActivityType.TentativeReject -> TODO() ActivityType.TentativeAccept -> TODO() ActivityType.Travel -> TODO() - ActivityType.Undo -> activityPubUndoService.receiveUndo(Config.configData.objectMapper.readValue(json)) + ActivityType.Undo -> activityPubUndoService.receiveUndo(configData.objectMapper.readValue(json)) ActivityType.Update -> TODO() ActivityType.View -> TODO() ActivityType.Other -> TODO() From 37c49328cfa78553b643abbd409a26d9ee01000c Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Mon, 22 May 2023 16:39:21 +0900 Subject: [PATCH 4/4] =?UTF-8?q?feat:=20=E7=A7=98=E5=AF=86=E3=81=AB?= =?UTF-8?q?=E3=81=99=E3=81=B9=E3=81=8D=E6=83=85=E5=A0=B1=E3=82=92=E8=A6=8B?= =?UTF-8?q?=E3=81=88=E3=81=AA=E3=81=84=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dev/usbharu/hideout/domain/model/hideout/entity/User.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/entity/User.kt b/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/entity/User.kt index 319d1f8c..1c6dd4a6 100644 --- a/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/entity/User.kt +++ b/src/main/kotlin/dev/usbharu/hideout/domain/model/hideout/entity/User.kt @@ -15,4 +15,8 @@ data class User( val publicKey: String, val privateKey: String? = null, val createdAt: Instant -) +) { + override fun toString(): String { + return "User(id=$id, name='$name', domain='$domain', screenName='$screenName', description='$description', password=****, inbox='$inbox', outbox='$outbox', url='$url', publicKey='$publicKey', privateKey=****, createdAt=$createdAt)" + } +}