From c056b7598b1a18b7d55437e6e91a99111f70e975 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 6 Aug 2024 13:00:00 +0900 Subject: [PATCH 1/9] =?UTF-8?q?feat:=20=E6=96=B0=E3=81=97=E3=81=84?= =?UTF-8?q?=E8=AA=8D=E5=8F=AF=E3=82=B7=E3=82=B9=E3=83=86=E3=83=A0=E3=82=92?= =?UTF-8?q?=E7=B5=84=E3=81=BF=E8=BE=BC=E3=81=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/application/filter/RegisterFilter.kt | 2 - .../UserRegisterFilterApplicationService.kt | 10 +-- .../application/post/RegisterLocalPost.kt | 1 - .../RegisterLocalPostApplicationService.kt | 11 ++-- .../AcceptFollowRequest.kt | 4 +- ...erAcceptFollowRequestApplicationService.kt | 10 +-- .../application/relationship/block/Block.kt | 4 +- .../block/UserBlockApplicationService.kt | 10 +-- .../followrequest/FollowRequest.kt | 4 +- .../UserFollowRequestApplicationService.kt | 10 +-- .../relationship/get/GetRelationship.kt | 4 +- .../get/GetRelationshipApplicationService.kt | 10 +-- .../application/relationship/mute/Mute.kt | 4 +- .../mute/UserMuteApplicationService.kt | 10 +-- .../RejectFollowRequest.kt | 4 +- ...erRejectFollowRequestApplicationService.kt | 10 +-- .../RemoveFromFollowers.kt | 2 +- ...erRemoveFromFollowersApplicationService.kt | 11 ++-- .../relationship/unblock/Unblock.kt | 4 +- .../unblock/UserUnblockApplicationService.kt | 10 +-- .../relationship/unfollow/Unfollow.kt | 4 +- .../UserUnfollowApplicationService.kt | 10 +-- .../application/relationship/unmute/Unmute.kt | 4 +- .../unmute/UserUnmuteApplicationService.kt | 10 +-- .../application/shared/CommandExecutor.kt | 30 --------- .../domain/model/support/principal/FromApi.kt | 9 ++- .../principal/PrincipalContextHolder.kt | 5 ++ .../ExposedPrincipalQueryService.kt | 37 +++++++++++ .../DelegateCommandExecutorFactory.kt | 36 ---------- .../springframework/HttpCommandExecutor.kt | 27 -------- .../SpringMvcCommandExecutorFactory.kt | 31 --------- .../oauth2/Oauth2CommandExecutor.kt | 24 ------- .../oauth2/Oauth2CommandExecutorFactory.kt | 33 ---------- ...ingSecurityOauth2PrincipalContextHolder.kt | 27 ++++++++ .../interfaces/api/auth/AuthController.kt | 2 - .../core/query/principal/PrincipalDTO.kt | 6 ++ .../query/principal/PrincipalQueryService.kt | 7 ++ .../interfaces/api/SpringAccountApi.kt | 65 +++++++++---------- .../interfaces/api/SpringFilterApi.kt | 22 +++---- .../mastodon/interfaces/api/SpringMediaApi.kt | 6 +- .../interfaces/api/SpringStatusApi.kt | 15 ++--- 41 files changed, 210 insertions(+), 335 deletions(-) delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/CommandExecutor.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/support/principal/PrincipalContextHolder.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedPrincipalQueryService.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/DelegateCommandExecutorFactory.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/HttpCommandExecutor.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/SpringMvcCommandExecutorFactory.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/Oauth2CommandExecutor.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/Oauth2CommandExecutorFactory.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/SpringSecurityOauth2PrincipalContextHolder.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/principal/PrincipalDTO.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/principal/PrincipalQueryService.kt diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/RegisterFilter.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/RegisterFilter.kt index f4f8dd91..3fd9a35f 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/RegisterFilter.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/RegisterFilter.kt @@ -18,12 +18,10 @@ package dev.usbharu.hideout.core.application.filter import dev.usbharu.hideout.core.domain.model.filter.FilterAction import dev.usbharu.hideout.core.domain.model.filter.FilterContext -import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId data class RegisterFilter( val filterName: String, val filterContext: Set, val filterAction: FilterAction, val filterKeywords: Set, - val userDetailId: UserDetailId ) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserRegisterFilterApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserRegisterFilterApplicationService.kt index 8bf5b3a3..bf12c041 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserRegisterFilterApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserRegisterFilterApplicationService.kt @@ -16,11 +16,11 @@ package dev.usbharu.hideout.core.application.filter -import dev.usbharu.hideout.core.application.shared.AbstractApplicationService +import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.domain.model.filter.* import dev.usbharu.hideout.core.domain.model.filter.FilterKeyword -import dev.usbharu.hideout.core.domain.model.support.principal.Principal +import dev.usbharu.hideout.core.domain.model.support.principal.FromApi import dev.usbharu.hideout.core.domain.shared.id.IdGenerateService import org.slf4j.LoggerFactory import org.springframework.stereotype.Service @@ -31,16 +31,16 @@ class UserRegisterFilterApplicationService( private val filterRepository: FilterRepository, transaction: Transaction, ) : - AbstractApplicationService( + LocalUserAbstractApplicationService( transaction, logger ) { - override suspend fun internalExecute(command: RegisterFilter, principal: Principal): Filter { + override suspend fun internalExecute(command: RegisterFilter, principal: FromApi): Filter { val filter = dev.usbharu.hideout.core.domain.model.filter.Filter.create( id = FilterId(idGenerateService.generateId()), - userDetailId = command.userDetailId, + userDetailId = principal.userDetailId, name = FilterName(command.filterName), filterContext = command.filterContext, filterAction = command.filterAction, diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPost.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPost.kt index 16f1092e..b5fd2f71 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPost.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPost.kt @@ -19,7 +19,6 @@ package dev.usbharu.hideout.core.application.post import dev.usbharu.hideout.core.domain.model.post.Visibility data class RegisterLocalPost( - val userDetailId: Long, val content: String, val overview: String?, val visibility: Visibility, diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationService.kt index 95bfe505..d0658179 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationService.kt @@ -16,15 +16,14 @@ package dev.usbharu.hideout.core.application.post -import dev.usbharu.hideout.core.application.shared.AbstractApplicationService +import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.media.MediaId import dev.usbharu.hideout.core.domain.model.post.PostId import dev.usbharu.hideout.core.domain.model.post.PostOverview import dev.usbharu.hideout.core.domain.model.post.PostRepository -import dev.usbharu.hideout.core.domain.model.support.principal.Principal -import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId +import dev.usbharu.hideout.core.domain.model.support.principal.FromApi import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository import dev.usbharu.hideout.core.infrastructure.factory.PostFactoryImpl import org.slf4j.Logger @@ -38,11 +37,11 @@ class RegisterLocalPostApplicationService( private val postRepository: PostRepository, private val userDetailRepository: UserDetailRepository, transaction: Transaction, -) : AbstractApplicationService(transaction, Companion.logger) { +) : LocalUserAbstractApplicationService(transaction, Companion.logger) { - override suspend fun internalExecute(command: RegisterLocalPost, principal: Principal): Long { + override suspend fun internalExecute(command: RegisterLocalPost, principal: FromApi): Long { val actorId = ( - userDetailRepository.findById(UserDetailId(command.userDetailId)) + userDetailRepository.findById(principal.userDetailId) ?: throw IllegalStateException("actor not found") ).actorId diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/acceptfollowrequest/AcceptFollowRequest.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/acceptfollowrequest/AcceptFollowRequest.kt index 16085792..6c0d9f20 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/acceptfollowrequest/AcceptFollowRequest.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/acceptfollowrequest/AcceptFollowRequest.kt @@ -16,6 +16,4 @@ package dev.usbharu.hideout.core.application.relationship.acceptfollowrequest -import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId - -data class AcceptFollowRequest(val sourceActorId: Long, val userDetailId: UserDetailId) +data class AcceptFollowRequest(val sourceActorId: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/acceptfollowrequest/UserAcceptFollowRequestApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/acceptfollowrequest/UserAcceptFollowRequestApplicationService.kt index ef7a382a..a80497e6 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/acceptfollowrequest/UserAcceptFollowRequestApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/acceptfollowrequest/UserAcceptFollowRequestApplicationService.kt @@ -17,12 +17,12 @@ package dev.usbharu.hideout.core.application.relationship.acceptfollowrequest import dev.usbharu.hideout.core.application.relationship.block.UserBlockApplicationService -import dev.usbharu.hideout.core.application.shared.AbstractApplicationService +import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository -import dev.usbharu.hideout.core.domain.model.support.principal.Principal +import dev.usbharu.hideout.core.domain.model.support.principal.FromApi import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository import org.slf4j.LoggerFactory import org.springframework.stereotype.Service @@ -34,10 +34,10 @@ class UserAcceptFollowRequestApplicationService( private val actorRepository: ActorRepository, private val userDetailRepository: UserDetailRepository, ) : - AbstractApplicationService(transaction, logger) { - override suspend fun internalExecute(command: AcceptFollowRequest, principal: Principal) { + LocalUserAbstractApplicationService(transaction, logger) { + override suspend fun internalExecute(command: AcceptFollowRequest, principal: FromApi) { - val userDetail = userDetailRepository.findById(command.userDetailId)!! + val userDetail = userDetailRepository.findById(principal.userDetailId)!! val actor = actorRepository.findById(userDetail.actorId)!! val targetId = ActorId(command.sourceActorId) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/block/Block.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/block/Block.kt index 8441162c..7a095b92 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/block/Block.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/block/Block.kt @@ -16,6 +16,4 @@ package dev.usbharu.hideout.core.application.relationship.block -import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId - -data class Block(val targetActorId: Long, val userDetailId: UserDetailId) +data class Block(val targetActorId: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/block/UserBlockApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/block/UserBlockApplicationService.kt index f8baa681..91a99c4f 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/block/UserBlockApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/block/UserBlockApplicationService.kt @@ -16,13 +16,13 @@ package dev.usbharu.hideout.core.application.relationship.block -import dev.usbharu.hideout.core.application.shared.AbstractApplicationService +import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.relationship.Relationship import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository -import dev.usbharu.hideout.core.domain.model.support.principal.Principal +import dev.usbharu.hideout.core.domain.model.support.principal.FromApi import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository import dev.usbharu.hideout.core.domain.service.relationship.RelationshipDomainService import org.slf4j.LoggerFactory @@ -36,10 +36,10 @@ class UserBlockApplicationService( private val userDetailRepository: UserDetailRepository, private val relationshipDomainService: RelationshipDomainService, ) : - AbstractApplicationService(transaction, logger) { - override suspend fun internalExecute(command: Block, principal: Principal) { + LocalUserAbstractApplicationService(transaction, logger) { + override suspend fun internalExecute(command: Block, principal: FromApi) { - val userDetail = userDetailRepository.findById(command.userDetailId)!! + val userDetail = userDetailRepository.findById(principal.userDetailId)!! val actor = actorRepository.findById(userDetail.actorId)!! val targetId = ActorId(command.targetActorId) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/followrequest/FollowRequest.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/followrequest/FollowRequest.kt index 1c83f259..3f8de0a7 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/followrequest/FollowRequest.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/followrequest/FollowRequest.kt @@ -16,6 +16,4 @@ package dev.usbharu.hideout.core.application.relationship.followrequest -import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId - -data class FollowRequest(val targetActorId: Long, val userDetailId: UserDetailId) +data class FollowRequest(val targetActorId: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/followrequest/UserFollowRequestApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/followrequest/UserFollowRequestApplicationService.kt index b1dfe181..0c9d967d 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/followrequest/UserFollowRequestApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/followrequest/UserFollowRequestApplicationService.kt @@ -16,13 +16,13 @@ package dev.usbharu.hideout.core.application.relationship.followrequest -import dev.usbharu.hideout.core.application.shared.AbstractApplicationService +import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.relationship.Relationship import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository -import dev.usbharu.hideout.core.domain.model.support.principal.Principal +import dev.usbharu.hideout.core.domain.model.support.principal.FromApi import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository import org.slf4j.LoggerFactory import org.springframework.stereotype.Service @@ -33,14 +33,14 @@ class UserFollowRequestApplicationService( transaction: Transaction, private val actorRepository: ActorRepository, private val userDetailRepository: UserDetailRepository, -) : AbstractApplicationService( +) : LocalUserAbstractApplicationService( transaction, logger ) { - override suspend fun internalExecute(command: FollowRequest, principal: Principal) { + override suspend fun internalExecute(command: FollowRequest, principal: FromApi) { - val userDetail = userDetailRepository.findById(command.userDetailId)!! + val userDetail = userDetailRepository.findById(principal.userDetailId)!! val actor = actorRepository.findById(userDetail.actorId)!! val targetId = ActorId(command.targetActorId) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/get/GetRelationship.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/get/GetRelationship.kt index 49427257..90df1b82 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/get/GetRelationship.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/get/GetRelationship.kt @@ -16,6 +16,4 @@ package dev.usbharu.hideout.core.application.relationship.get -import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId - -data class GetRelationship(val targetActorId: Long, val userDetailId: UserDetailId) +data class GetRelationship(val targetActorId: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/get/GetRelationshipApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/get/GetRelationshipApplicationService.kt index ecc8b6e7..b832710d 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/get/GetRelationshipApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/get/GetRelationshipApplicationService.kt @@ -16,14 +16,14 @@ package dev.usbharu.hideout.core.application.relationship.get -import dev.usbharu.hideout.core.application.shared.AbstractApplicationService +import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.actorinstancerelationship.ActorInstanceRelationship import dev.usbharu.hideout.core.domain.model.actorinstancerelationship.ActorInstanceRelationshipRepository import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository -import dev.usbharu.hideout.core.domain.model.support.principal.Principal +import dev.usbharu.hideout.core.domain.model.support.principal.FromApi import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository import org.slf4j.LoggerFactory import org.springframework.stereotype.Service @@ -36,12 +36,12 @@ class GetRelationshipApplicationService( private val actorInstanceRelationshipRepository: ActorInstanceRelationshipRepository, transaction: Transaction, ) : - AbstractApplicationService( + LocalUserAbstractApplicationService( transaction, logger ) { - override suspend fun internalExecute(command: GetRelationship, principal: Principal): Relationship { - val userDetail = userDetailRepository.findById(command.userDetailId)!! + override suspend fun internalExecute(command: GetRelationship, principal: FromApi): Relationship { + val userDetail = userDetailRepository.findById(principal.userDetailId)!! val actor = actorRepository.findById(userDetail.actorId)!! val targetId = ActorId(command.targetActorId) val target = actorRepository.findById(targetId)!! diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/mute/Mute.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/mute/Mute.kt index fffd5258..79a56830 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/mute/Mute.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/mute/Mute.kt @@ -16,6 +16,4 @@ package dev.usbharu.hideout.core.application.relationship.mute -import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId - -data class Mute(val targetActorId: Long, val userDetailId: UserDetailId) +data class Mute(val targetActorId: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/mute/UserMuteApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/mute/UserMuteApplicationService.kt index 999a76fc..7c611d24 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/mute/UserMuteApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/mute/UserMuteApplicationService.kt @@ -17,13 +17,13 @@ package dev.usbharu.hideout.core.application.relationship.mute import dev.usbharu.hideout.core.application.relationship.block.UserBlockApplicationService -import dev.usbharu.hideout.core.application.shared.AbstractApplicationService +import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.relationship.Relationship import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository -import dev.usbharu.hideout.core.domain.model.support.principal.Principal +import dev.usbharu.hideout.core.domain.model.support.principal.FromApi import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository import org.slf4j.LoggerFactory import org.springframework.stereotype.Service @@ -35,10 +35,10 @@ class UserMuteApplicationService( private val actorRepository: ActorRepository, private val userDetailRepository: UserDetailRepository, ) : - AbstractApplicationService(transaction, logger) { - override suspend fun internalExecute(command: Mute, principal: Principal) { + LocalUserAbstractApplicationService(transaction, logger) { + override suspend fun internalExecute(command: Mute, principal: FromApi) { - val userDetail = userDetailRepository.findById(command.userDetailId)!! + val userDetail = userDetailRepository.findById(principal.userDetailId)!! val actor = actorRepository.findById(userDetail.actorId)!! val targetId = ActorId(command.targetActorId) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/rejectfollowrequest/RejectFollowRequest.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/rejectfollowrequest/RejectFollowRequest.kt index e22b8e05..4662eff1 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/rejectfollowrequest/RejectFollowRequest.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/rejectfollowrequest/RejectFollowRequest.kt @@ -16,6 +16,4 @@ package dev.usbharu.hideout.core.application.relationship.rejectfollowrequest -import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId - -data class RejectFollowRequest(val sourceActorId: Long, val userDetailId: UserDetailId) +data class RejectFollowRequest(val sourceActorId: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/rejectfollowrequest/UserRejectFollowRequestApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/rejectfollowrequest/UserRejectFollowRequestApplicationService.kt index b2f39da4..97adbdd0 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/rejectfollowrequest/UserRejectFollowRequestApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/rejectfollowrequest/UserRejectFollowRequestApplicationService.kt @@ -17,12 +17,12 @@ package dev.usbharu.hideout.core.application.relationship.rejectfollowrequest import dev.usbharu.hideout.core.application.relationship.block.UserBlockApplicationService -import dev.usbharu.hideout.core.application.shared.AbstractApplicationService +import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository -import dev.usbharu.hideout.core.domain.model.support.principal.Principal +import dev.usbharu.hideout.core.domain.model.support.principal.FromApi import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository import org.slf4j.LoggerFactory import org.springframework.stereotype.Service @@ -34,10 +34,10 @@ class UserRejectFollowRequestApplicationService( private val actorRepository: ActorRepository, private val userDetailRepository: UserDetailRepository, ) : - AbstractApplicationService(transaction, logger) { - override suspend fun internalExecute(command: RejectFollowRequest, principal: Principal) { + LocalUserAbstractApplicationService(transaction, logger) { + override suspend fun internalExecute(command: RejectFollowRequest, principal: FromApi) { - val userDetail = userDetailRepository.findById(command.userDetailId)!! + val userDetail = userDetailRepository.findById(principal.userDetailId)!! val actor = actorRepository.findById(userDetail.actorId)!! val targetId = ActorId(command.sourceActorId) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/removefromfollowers/RemoveFromFollowers.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/removefromfollowers/RemoveFromFollowers.kt index ff8bdd63..f9642099 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/removefromfollowers/RemoveFromFollowers.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/removefromfollowers/RemoveFromFollowers.kt @@ -16,4 +16,4 @@ package dev.usbharu.hideout.core.application.relationship.removefromfollowers -data class RemoveFromFollowers(val targetActorId: Long, val userDetailId: Long) +data class RemoveFromFollowers(val targetActorId: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/removefromfollowers/UserRemoveFromFollowersApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/removefromfollowers/UserRemoveFromFollowersApplicationService.kt index 2d6ab718..d3e50e50 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/removefromfollowers/UserRemoveFromFollowersApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/removefromfollowers/UserRemoveFromFollowersApplicationService.kt @@ -17,14 +17,13 @@ package dev.usbharu.hideout.core.application.relationship.removefromfollowers import dev.usbharu.hideout.core.application.relationship.block.UserBlockApplicationService -import dev.usbharu.hideout.core.application.shared.AbstractApplicationService +import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.relationship.Relationship import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository -import dev.usbharu.hideout.core.domain.model.support.principal.Principal -import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId +import dev.usbharu.hideout.core.domain.model.support.principal.FromApi import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository import org.slf4j.LoggerFactory import org.springframework.stereotype.Service @@ -36,10 +35,10 @@ class UserRemoveFromFollowersApplicationService( private val actorRepository: ActorRepository, private val userDetailRepository: UserDetailRepository, ) : - AbstractApplicationService(transaction, logger) { - override suspend fun internalExecute(command: RemoveFromFollowers, principal: Principal) { + LocalUserAbstractApplicationService(transaction, logger) { + override suspend fun internalExecute(command: RemoveFromFollowers, principal: FromApi) { - val userDetail = userDetailRepository.findById(UserDetailId(command.userDetailId))!! + val userDetail = userDetailRepository.findById(principal.userDetailId)!! val actor = actorRepository.findById(userDetail.actorId)!! val targetId = ActorId(command.targetActorId) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unblock/Unblock.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unblock/Unblock.kt index 7b376ce1..7b85c603 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unblock/Unblock.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unblock/Unblock.kt @@ -16,6 +16,4 @@ package dev.usbharu.hideout.core.application.relationship.unblock -import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId - -data class Unblock(val targetActorId: Long, val userDetailId: UserDetailId) +data class Unblock(val targetActorId: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unblock/UserUnblockApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unblock/UserUnblockApplicationService.kt index fd8a1962..d5417799 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unblock/UserUnblockApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unblock/UserUnblockApplicationService.kt @@ -17,13 +17,13 @@ package dev.usbharu.hideout.core.application.relationship.unblock import dev.usbharu.hideout.core.application.relationship.block.UserBlockApplicationService -import dev.usbharu.hideout.core.application.shared.AbstractApplicationService +import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.relationship.Relationship import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository -import dev.usbharu.hideout.core.domain.model.support.principal.Principal +import dev.usbharu.hideout.core.domain.model.support.principal.FromApi import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository import org.slf4j.LoggerFactory import org.springframework.stereotype.Service @@ -35,10 +35,10 @@ class UserUnblockApplicationService( private val actorRepository: ActorRepository, private val userDetailRepository: UserDetailRepository, ) : - AbstractApplicationService(transaction, logger) { - override suspend fun internalExecute(command: Unblock, principal: Principal) { + LocalUserAbstractApplicationService(transaction, logger) { + override suspend fun internalExecute(command: Unblock, principal: FromApi) { - val userDetail = userDetailRepository.findById(command.userDetailId)!! + val userDetail = userDetailRepository.findById(principal.userDetailId)!! val actor = actorRepository.findById(userDetail.actorId)!! val targetId = ActorId(command.targetActorId) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unfollow/Unfollow.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unfollow/Unfollow.kt index 36353933..60190dab 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unfollow/Unfollow.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unfollow/Unfollow.kt @@ -16,6 +16,4 @@ package dev.usbharu.hideout.core.application.relationship.unfollow -import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId - -data class Unfollow(val targetActorId: Long, val userDetailId: UserDetailId) +data class Unfollow(val targetActorId: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unfollow/UserUnfollowApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unfollow/UserUnfollowApplicationService.kt index 9c44a183..a7068d00 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unfollow/UserUnfollowApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unfollow/UserUnfollowApplicationService.kt @@ -17,13 +17,13 @@ package dev.usbharu.hideout.core.application.relationship.unfollow import dev.usbharu.hideout.core.application.relationship.block.UserBlockApplicationService -import dev.usbharu.hideout.core.application.shared.AbstractApplicationService +import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.relationship.Relationship import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository -import dev.usbharu.hideout.core.domain.model.support.principal.Principal +import dev.usbharu.hideout.core.domain.model.support.principal.FromApi import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository import org.slf4j.LoggerFactory import org.springframework.stereotype.Service @@ -35,10 +35,10 @@ class UserUnfollowApplicationService( private val actorRepository: ActorRepository, private val userDetailRepository: UserDetailRepository, ) : - AbstractApplicationService(transaction, logger) { - override suspend fun internalExecute(command: Unfollow, principal: Principal) { + LocalUserAbstractApplicationService(transaction, logger) { + override suspend fun internalExecute(command: Unfollow, principal: FromApi) { - val userDetail = userDetailRepository.findById(command.userDetailId)!! + val userDetail = userDetailRepository.findById(principal.userDetailId)!! val actor = actorRepository.findById(userDetail.actorId)!! val targetId = ActorId(command.targetActorId) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unmute/Unmute.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unmute/Unmute.kt index 8df2fbab..1939ee25 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unmute/Unmute.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unmute/Unmute.kt @@ -16,6 +16,4 @@ package dev.usbharu.hideout.core.application.relationship.unmute -import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId - -data class Unmute(val targetActorId: Long, val userDetailId: UserDetailId) +data class Unmute(val targetActorId: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unmute/UserUnmuteApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unmute/UserUnmuteApplicationService.kt index 2ddcd20a..b90ba059 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unmute/UserUnmuteApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/unmute/UserUnmuteApplicationService.kt @@ -17,13 +17,13 @@ package dev.usbharu.hideout.core.application.relationship.unmute import dev.usbharu.hideout.core.application.relationship.block.UserBlockApplicationService -import dev.usbharu.hideout.core.application.shared.AbstractApplicationService +import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.relationship.Relationship import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository -import dev.usbharu.hideout.core.domain.model.support.principal.Principal +import dev.usbharu.hideout.core.domain.model.support.principal.FromApi import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository import org.slf4j.LoggerFactory import org.springframework.stereotype.Service @@ -35,14 +35,14 @@ class UserUnmuteApplicationService( private val actorRepository: ActorRepository, private val userDetailRepository: UserDetailRepository, ) : - AbstractApplicationService(transaction, logger) { + LocalUserAbstractApplicationService(transaction, logger) { companion object { private val logger = LoggerFactory.getLogger(UserBlockApplicationService::class.java) } - override suspend fun internalExecute(command: Unmute, principal: Principal) { + override suspend fun internalExecute(command: Unmute, principal: FromApi) { - val userDetail = userDetailRepository.findById(command.userDetailId)!! + val userDetail = userDetailRepository.findById(principal.userDetailId)!! val actor = actorRepository.findById(userDetail.actorId)!! val targetId = ActorId(command.targetActorId) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/CommandExecutor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/CommandExecutor.kt deleted file mode 100644 index 1974ba16..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/CommandExecutor.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.application.shared - -interface CommandExecutor { - val executor: String -} - -interface UserDetailGettableCommandExecutor : CommandExecutor { - val userDetailId: Long -} - -data class DomainEventCommandExecutor( - override val executor: String, - val commandExecutor: CommandExecutor? -) : CommandExecutor \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/support/principal/FromApi.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/support/principal/FromApi.kt index 93215882..2ce5f785 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/support/principal/FromApi.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/support/principal/FromApi.kt @@ -4,7 +4,12 @@ import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.support.acct.Acct import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId -class FromApi(actorId: ActorId, override val userDetailId: UserDetailId, override val acct: Acct) : Principal( - actorId, userDetailId, +class FromApi( + actorId: ActorId, + override val userDetailId: UserDetailId, + override val acct: Acct +) : Principal( + actorId, + userDetailId, acct ) \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/support/principal/PrincipalContextHolder.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/support/principal/PrincipalContextHolder.kt new file mode 100644 index 00000000..464363ce --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/support/principal/PrincipalContextHolder.kt @@ -0,0 +1,5 @@ +package dev.usbharu.hideout.core.domain.model.support.principal + +interface PrincipalContextHolder { + suspend fun getPrincipal(): Principal +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedPrincipalQueryService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedPrincipalQueryService.kt new file mode 100644 index 00000000..413ccc67 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/ExposedPrincipalQueryService.kt @@ -0,0 +1,37 @@ +package dev.usbharu.hideout.core.infrastructure.exposedquery + +import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId +import dev.usbharu.hideout.core.infrastructure.exposedrepository.AbstractRepository +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Actors +import dev.usbharu.hideout.core.infrastructure.exposedrepository.UserDetails +import dev.usbharu.hideout.core.query.principal.PrincipalDTO +import dev.usbharu.hideout.core.query.principal.PrincipalQueryService +import org.jetbrains.exposed.sql.selectAll +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Repository + +@Repository +class ExposedPrincipalQueryService : PrincipalQueryService, AbstractRepository() { + override suspend fun findByUserDetailId(userDetailId: UserDetailId): PrincipalDTO { + return query { + UserDetails.leftJoin(Actors).selectAll().where { UserDetails.id eq userDetailId.id }.single() + .let { + PrincipalDTO( + UserDetailId(it[UserDetails.id]), + ActorId(it[UserDetails.actorId]), + it[Actors.name], + it[Actors.domain] + ) + } + } + } + + override val logger: Logger + get() = Companion.logger + + companion object { + private val logger: Logger = LoggerFactory.getLogger(ExposedPrincipalQueryService::class.java) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/DelegateCommandExecutorFactory.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/DelegateCommandExecutorFactory.kt deleted file mode 100644 index cedebc83..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/DelegateCommandExecutorFactory.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.springframework - -import dev.usbharu.hideout.core.application.shared.CommandExecutor -import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.Oauth2CommandExecutorFactory -import org.springframework.security.core.context.SecurityContextHolder -import org.springframework.security.oauth2.jwt.Jwt -import org.springframework.stereotype.Component - -@Component -class DelegateCommandExecutorFactory( - private val oauth2CommandExecutorFactory: Oauth2CommandExecutorFactory, - private val mvcCommandExecutorFactory: SpringMvcCommandExecutorFactory, -) { - fun getCommandExecutor(): CommandExecutor { - if (SecurityContextHolder.getContext().authentication.principal is Jwt) { - return oauth2CommandExecutorFactory.getCommandExecutor() - } - return mvcCommandExecutorFactory.getCommandExecutor() - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/HttpCommandExecutor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/HttpCommandExecutor.kt deleted file mode 100644 index e7b28ed5..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/HttpCommandExecutor.kt +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.springframework - -import dev.usbharu.hideout.core.application.shared.CommandExecutor - -open class HttpCommandExecutor( - override val executor: String, - val ip: String, - val userAgent: String, -) : CommandExecutor { - override fun toString(): String = "HttpCommandExecutor(executor='$executor', ip='$ip', userAgent='$userAgent')" -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/SpringMvcCommandExecutorFactory.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/SpringMvcCommandExecutorFactory.kt deleted file mode 100644 index 7a9b5940..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/SpringMvcCommandExecutorFactory.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.springframework - -import org.springframework.security.core.context.SecurityContextHolder -import org.springframework.stereotype.Component -import org.springframework.web.context.request.RequestContextHolder -import org.springframework.web.context.request.ServletRequestAttributes - -@Component -class SpringMvcCommandExecutorFactory { - fun getCommandExecutor(): HttpCommandExecutor { - val name = SecurityContextHolder.getContext().authentication?.name ?: "ANONYMOUS" - val request = (RequestContextHolder.currentRequestAttributes() as ServletRequestAttributes).request - return HttpCommandExecutor(name, request.remoteAddr, request.getHeader("user-agent").orEmpty()) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/Oauth2CommandExecutor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/Oauth2CommandExecutor.kt deleted file mode 100644 index 9cf58a40..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/Oauth2CommandExecutor.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.springframework.oauth2 - -import dev.usbharu.hideout.core.application.shared.CommandExecutor -import dev.usbharu.hideout.core.application.shared.UserDetailGettableCommandExecutor - -class Oauth2CommandExecutor(override val executor: String, override val userDetailId: Long) : - CommandExecutor, - UserDetailGettableCommandExecutor diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/Oauth2CommandExecutorFactory.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/Oauth2CommandExecutorFactory.kt deleted file mode 100644 index 1416a2a3..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/Oauth2CommandExecutorFactory.kt +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.infrastructure.springframework.oauth2 - -import org.springframework.security.core.context.SecurityContextHolder -import org.springframework.security.oauth2.jwt.Jwt -import org.springframework.stereotype.Component - -@Component -class Oauth2CommandExecutorFactory { - fun getCommandExecutor(): Oauth2CommandExecutor { - val principal = SecurityContextHolder.getContext().authentication.principal as Jwt - - return Oauth2CommandExecutor( - principal.subject, - principal.getClaim("uid").toLong() - ) - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/SpringSecurityOauth2PrincipalContextHolder.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/SpringSecurityOauth2PrincipalContextHolder.kt new file mode 100644 index 00000000..68341349 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/SpringSecurityOauth2PrincipalContextHolder.kt @@ -0,0 +1,27 @@ +package dev.usbharu.hideout.core.infrastructure.springframework.oauth2 + +import dev.usbharu.hideout.core.domain.model.support.acct.Acct +import dev.usbharu.hideout.core.domain.model.support.principal.FromApi +import dev.usbharu.hideout.core.domain.model.support.principal.PrincipalContextHolder +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId +import dev.usbharu.hideout.core.query.principal.PrincipalQueryService +import org.springframework.security.core.context.SecurityContextHolder +import org.springframework.security.oauth2.jwt.Jwt +import org.springframework.stereotype.Component + +@Component +class SpringSecurityOauth2PrincipalContextHolder(private val principalQueryService: PrincipalQueryService) : + PrincipalContextHolder { + override suspend fun getPrincipal(): FromApi { + val principal = SecurityContextHolder.getContext().authentication?.principal as Jwt + + val id = principal.getClaim("uid").toLong() + val userDetail = principalQueryService.findByUserDetailId(UserDetailId(id)) + + return FromApi( + userDetail.actorId, + userDetail.userDetailId, + Acct(userDetail.username, userDetail.host) + ) + } +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt index 64687efd..8872d60d 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/interfaces/api/auth/AuthController.kt @@ -19,7 +19,6 @@ package dev.usbharu.hideout.core.interfaces.api.auth import dev.usbharu.hideout.core.application.actor.RegisterLocalActor import dev.usbharu.hideout.core.application.actor.RegisterLocalActorApplicationService import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous -import dev.usbharu.hideout.core.infrastructure.springframework.SpringMvcCommandExecutorFactory import jakarta.servlet.http.HttpServletRequest import org.springframework.stereotype.Controller import org.springframework.validation.annotation.Validated @@ -30,7 +29,6 @@ import org.springframework.web.bind.annotation.PostMapping @Controller class AuthController( private val registerLocalActorApplicationService: RegisterLocalActorApplicationService, - private val springMvcCommandExecutorFactory: SpringMvcCommandExecutorFactory, ) { @GetMapping("/auth/sign_up") @Suppress("FunctionOnlyReturningConstant") diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/principal/PrincipalDTO.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/principal/PrincipalDTO.kt new file mode 100644 index 00000000..40b0cb90 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/principal/PrincipalDTO.kt @@ -0,0 +1,6 @@ +package dev.usbharu.hideout.core.query.principal + +import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId + +data class PrincipalDTO(val userDetailId: UserDetailId, val actorId: ActorId, val username: String, val host: String) \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/principal/PrincipalQueryService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/principal/PrincipalQueryService.kt new file mode 100644 index 00000000..3aa01531 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/query/principal/PrincipalQueryService.kt @@ -0,0 +1,7 @@ +package dev.usbharu.hideout.core.query.principal + +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId + +interface PrincipalQueryService { + suspend fun findByUserDetailId(userDetailId: UserDetailId): PrincipalDTO +} \ No newline at end of file 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 2f367367..4a3dd110 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 @@ -38,8 +38,7 @@ 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.infrastructure.springframework.oauth2.Oauth2CommandExecutor -import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.Oauth2CommandExecutorFactory +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 @@ -49,7 +48,6 @@ import org.springframework.stereotype.Controller @Controller class SpringAccountApi( - private val oauth2CommandExecutorFactory: Oauth2CommandExecutorFactory, private val getUserDetailApplicationService: GetUserDetailApplicationService, private val getAccountApplicationService: GetAccountApplicationService, private val userFollowRequestApplicationService: UserFollowRequestApplicationService, @@ -62,31 +60,33 @@ class SpringAccountApi( private val userRejectFollowRequestApplicationService: UserRejectFollowRequestApplicationService, private val userRemoveFromFollowersApplicationService: UserRemoveFromFollowersApplicationService, private val userUnfollowApplicationService: UserUnfollowApplicationService, + private val principalContextHolder: SpringSecurityOauth2PrincipalContextHolder ) : AccountApi { override suspend fun apiV1AccountsIdBlockPost(id: String): ResponseEntity { - val executor = oauth2CommandExecutorFactory.getCommandExecutor() - userBlockApplicationService.execute(Block(id.toLong())) - return fetchRelationship(id, executor) + userBlockApplicationService.execute(Block(id.toLong()), principalContextHolder.getPrincipal()) + return fetchRelationship(id) } override suspend fun apiV1AccountsIdFollowPost( id: String, followRequestBody: FollowRequestBody?, ): ResponseEntity { - val executor = oauth2CommandExecutorFactory.getCommandExecutor() + userFollowRequestApplicationService.execute( - FollowRequest(id.toLong()) + FollowRequest(id.toLong()), principalContextHolder.getPrincipal() ) - return fetchRelationship(id, executor) + return fetchRelationship(id) } private suspend fun fetchRelationship( id: String, - executor: Oauth2CommandExecutor, ): ResponseEntity { - val relationship = getRelationshipApplicationService.execute(GetRelationship(id.toLong())) + val relationship = getRelationshipApplicationService.execute( + GetRelationship(id.toLong()), + principalContextHolder.getPrincipal() + ) return ResponseEntity.ok( Relationship( id = relationship.targetId.toString(), @@ -109,49 +109,44 @@ class SpringAccountApi( override suspend fun apiV1AccountsIdGet(id: String): ResponseEntity { return ResponseEntity.ok( getAccountApplicationService.execute( - GetAccount(id) + GetAccount(id), principalContextHolder.getPrincipal() ) ) } override suspend fun apiV1AccountsIdMutePost(id: String): ResponseEntity { - val executor = oauth2CommandExecutorFactory.getCommandExecutor() userMuteApplicationService.execute( - Mute(id.toLong()) + Mute(id.toLong()), principalContextHolder.getPrincipal() ) - return fetchRelationship(id, executor) + return fetchRelationship(id) } override suspend fun apiV1AccountsIdRemoveFromFollowersPost(id: String): ResponseEntity { - val executor = oauth2CommandExecutorFactory.getCommandExecutor() userRemoveFromFollowersApplicationService.execute( - RemoveFromFollowers(id.toLong()) + RemoveFromFollowers(id.toLong()), principalContextHolder.getPrincipal() ) - return fetchRelationship(id, executor) + return fetchRelationship(id) } override suspend fun apiV1AccountsIdUnblockPost(id: String): ResponseEntity { - val executor = oauth2CommandExecutorFactory.getCommandExecutor() userUnblockApplicationService.execute( - Unblock(id.toLong()) + Unblock(id.toLong()), principalContextHolder.getPrincipal() ) - return fetchRelationship(id, executor) + return fetchRelationship(id) } override suspend fun apiV1AccountsIdUnfollowPost(id: String): ResponseEntity { - val executor = oauth2CommandExecutorFactory.getCommandExecutor() userUnfollowApplicationService.execute( - Unfollow(id.toLong()) + Unfollow(id.toLong()), principalContextHolder.getPrincipal() ) - return fetchRelationship(id, executor) + return fetchRelationship(id) } override suspend fun apiV1AccountsIdUnmutePost(id: String): ResponseEntity { - val executor = oauth2CommandExecutorFactory.getCommandExecutor() userUnmuteApplicationService.execute( - Unmute(id.toLong()) + Unmute(id.toLong()), principalContextHolder.getPrincipal() ) - return fetchRelationship(id, executor) + return fetchRelationship(id) } override suspend fun apiV1AccountsPost(accountsCreateRequest: AccountsCreateRequest): ResponseEntity { @@ -163,9 +158,9 @@ class SpringAccountApi( } override suspend fun apiV1AccountsVerifyCredentialsGet(): ResponseEntity { - val commandExecutor = oauth2CommandExecutorFactory.getCommandExecutor() + val principal = principalContextHolder.getPrincipal() val localActor = - getUserDetailApplicationService.execute(GetUserDetail(commandExecutor.userDetailId)) + getUserDetailApplicationService.execute(GetUserDetail(principal.userDetailId.id), principal) return ResponseEntity.ok( CredentialAccount( @@ -215,19 +210,19 @@ class SpringAccountApi( } override suspend fun apiV1FollowRequestsAccountIdAuthorizePost(accountId: String): ResponseEntity { - val executor = oauth2CommandExecutorFactory.getCommandExecutor() + userAcceptFollowRequestApplicationService.execute( - AcceptFollowRequest(accountId.toLong()) + AcceptFollowRequest(accountId.toLong()), principalContextHolder.getPrincipal() ) - return fetchRelationship(accountId, executor) + return fetchRelationship(accountId) } override suspend fun apiV1FollowRequestsAccountIdRejectPost(accountId: String): ResponseEntity { - val executor = oauth2CommandExecutorFactory.getCommandExecutor() + userRejectFollowRequestApplicationService.execute( - RejectFollowRequest(accountId.toLong()) + RejectFollowRequest(accountId.toLong()), principalContextHolder.getPrincipal() ) - return fetchRelationship(accountId, executor) + return fetchRelationship(accountId) } } \ No newline at end of file diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringFilterApi.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringFilterApi.kt index c5b31e19..c5748dab 100644 --- a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringFilterApi.kt +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringFilterApi.kt @@ -20,7 +20,7 @@ import dev.usbharu.hideout.core.application.filter.* import dev.usbharu.hideout.core.domain.model.filter.FilterAction import dev.usbharu.hideout.core.domain.model.filter.FilterContext import dev.usbharu.hideout.core.domain.model.filter.FilterMode -import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.Oauth2CommandExecutorFactory +import dev.usbharu.hideout.core.domain.model.support.principal.PrincipalContextHolder import dev.usbharu.hideout.mastodon.application.filter.DeleteFilterV1 import dev.usbharu.hideout.mastodon.application.filter.DeleteFilterV1ApplicationService import dev.usbharu.hideout.mastodon.application.filter.GetFilterV1 @@ -36,18 +36,18 @@ import org.springframework.stereotype.Controller @Controller class SpringFilterApi( - private val oauth2CommandExecutorFactory: Oauth2CommandExecutorFactory, private val userRegisterFilterApplicationService: UserRegisterFilterApplicationService, private val getFilterV1ApplicationService: GetFilterV1ApplicationService, private val deleteFilterV1ApplicationService: DeleteFilterV1ApplicationService, private val userDeleteFilterApplicationService: UserDeleteFilterApplicationService, private val userGetFilterApplicationService: UserGetFilterApplicationService, + private val principalContextHolder: PrincipalContextHolder ) : FilterApi { override suspend fun apiV1FiltersIdDelete(id: String): ResponseEntity { return ResponseEntity.ok( deleteFilterV1ApplicationService.execute( - DeleteFilterV1(id.toLong()) + DeleteFilterV1(id.toLong()), principalContextHolder.getPrincipal() ) ) } @@ -55,7 +55,7 @@ class SpringFilterApi( override suspend fun apiV1FiltersIdGet(id: String): ResponseEntity { return ResponseEntity.ok( getFilterV1ApplicationService.execute( - GetFilterV1(id.toLong()) + GetFilterV1(id.toLong()), principalContextHolder.getPrincipal() ) ) } @@ -72,7 +72,7 @@ class SpringFilterApi( } override suspend fun apiV1FiltersPost(v1FilterPostRequest: V1FilterPostRequest): ResponseEntity { - val executor = oauth2CommandExecutorFactory.getCommandExecutor() + val filterMode = if (v1FilterPostRequest.wholeWord == true) { FilterMode.WHOLE_WORD } else { @@ -91,11 +91,11 @@ class SpringFilterApi( RegisterFilter( v1FilterPostRequest.phrase, filterContext, FilterAction.WARN, setOf(RegisterFilterKeyword(v1FilterPostRequest.phrase, filterMode)) - ) + ), principalContextHolder.getPrincipal() ) return ResponseEntity.ok( getFilterV1ApplicationService.execute( - GetFilterV1(filter.filterKeywords.first().id) + GetFilterV1(filter.filterKeywords.first().id), principalContextHolder.getPrincipal() ) ) } @@ -116,14 +116,14 @@ class SpringFilterApi( override suspend fun apiV2FiltersIdDelete(id: String): ResponseEntity { userDeleteFilterApplicationService.execute( - DeleteFilter(id.toLong()) + DeleteFilter(id.toLong()), principalContextHolder.getPrincipal() ) return ResponseEntity.ok(Unit) } override suspend fun apiV2FiltersIdGet(id: String): ResponseEntity { val filter = userGetFilterApplicationService.execute( - GetFilter(id.toLong()) + GetFilter(id.toLong()), principalContextHolder.getPrincipal() ) return ResponseEntity.ok( filter(filter) @@ -186,7 +186,7 @@ class SpringFilterApi( } override suspend fun apiV2FiltersPost(filterPostRequest: FilterPostRequest): ResponseEntity { - val executor = oauth2CommandExecutorFactory.getCommandExecutor() + val filter = userRegisterFilterApplicationService.execute( RegisterFilter( filterName = filterPostRequest.title, @@ -216,7 +216,7 @@ class SpringFilterApi( } ) }.toSet() - ) + ), principalContextHolder.getPrincipal() ) return ResponseEntity.ok(filter(filter)) } diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringMediaApi.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringMediaApi.kt index 60f133f9..c22afbc7 100644 --- a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringMediaApi.kt +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringMediaApi.kt @@ -19,7 +19,7 @@ package dev.usbharu.hideout.mastodon.interfaces.api import dev.usbharu.hideout.core.application.media.UploadMedia import dev.usbharu.hideout.core.application.media.UploadMediaApplicationService import dev.usbharu.hideout.core.domain.model.media.FileType.* -import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.Oauth2CommandExecutorFactory +import dev.usbharu.hideout.core.domain.model.support.principal.PrincipalContextHolder import dev.usbharu.hideout.mastodon.interfaces.api.generated.MediaApi import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.MediaAttachment import org.springframework.http.ResponseEntity @@ -30,7 +30,7 @@ import java.nio.file.Files @Controller class SpringMediaApi( private val uploadMediaApplicationService: UploadMediaApplicationService, - private val oauth2CommandExecutorFactory: Oauth2CommandExecutorFactory + private val principalContextHolder: PrincipalContextHolder ) : MediaApi { override suspend fun apiV1MediaPost( file: MultipartFile, @@ -52,7 +52,7 @@ class SpringMediaApi( file.originalFilename ?: file.name, null, description - ) + ), principalContextHolder.getPrincipal() ) return ResponseEntity.ok( diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringStatusApi.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringStatusApi.kt index 09eac878..0fe04e58 100644 --- a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringStatusApi.kt +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringStatusApi.kt @@ -19,8 +19,7 @@ package dev.usbharu.hideout.mastodon.interfaces.api import dev.usbharu.hideout.core.application.post.RegisterLocalPost import dev.usbharu.hideout.core.application.post.RegisterLocalPostApplicationService import dev.usbharu.hideout.core.domain.model.post.Visibility -import dev.usbharu.hideout.core.infrastructure.springframework.DelegateCommandExecutorFactory -import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.Oauth2CommandExecutor +import dev.usbharu.hideout.core.domain.model.support.principal.PrincipalContextHolder import dev.usbharu.hideout.mastodon.application.status.GetStatus import dev.usbharu.hideout.mastodon.application.status.GetStatusApplicationService import dev.usbharu.hideout.mastodon.interfaces.api.generated.StatusApi @@ -32,9 +31,9 @@ import org.springframework.stereotype.Controller @Controller class SpringStatusApi( - private val delegateCommandExecutorFactory: DelegateCommandExecutorFactory, private val registerLocalPostApplicationService: RegisterLocalPostApplicationService, private val getStatusApplicationService: GetStatusApplicationService, + private val principalContextHolder: PrincipalContextHolder ) : StatusApi { override suspend fun apiV1StatusesIdEmojiReactionsEmojiDelete(id: String, emoji: String): ResponseEntity { return super.apiV1StatusesIdEmojiReactionsEmojiDelete(id, emoji) @@ -48,16 +47,15 @@ class SpringStatusApi( return ResponseEntity.ok( getStatusApplicationService.execute( - GetStatus(id) + GetStatus(id), principalContextHolder.getPrincipal() ) ) } override suspend fun apiV1StatusesPost(statusesRequest: StatusesRequest): ResponseEntity { - val executor = delegateCommandExecutorFactory.getCommandExecutor() as Oauth2CommandExecutor + val execute = registerLocalPostApplicationService.execute( RegisterLocalPost( - userDetailId = executor.userDetailId, content = statusesRequest.status.orEmpty(), overview = statusesRequest.spoilerText, visibility = when (statusesRequest.visibility) { @@ -71,11 +69,12 @@ class SpringStatusApi( replyId = statusesRequest.inReplyToId?.toLong(), sensitive = statusesRequest.sensitive == true, mediaIds = statusesRequest.mediaIds.orEmpty().map { it.toLong() } - ) + ), principalContextHolder.getPrincipal() ) - val status = getStatusApplicationService.execute(GetStatus(execute.toString())) + val status = + getStatusApplicationService.execute(GetStatus(execute.toString()), principalContextHolder.getPrincipal()) return ResponseEntity.ok( status ) From 0825be76b32cb59fcfa27deffbf53223964cbf4d Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 6 Aug 2024 17:43:14 +0900 Subject: [PATCH 2/9] =?UTF-8?q?test:=20=E3=83=86=E3=82=B9=E3=83=88?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/actor/DeleteLocalActor.kt | 5 ++ .../DeleteLocalActorApplicationService.kt | 37 --------- .../actor/GetUserDetailApplicationService.kt | 6 +- .../RegisterLocalActorApplicationService.kt | 6 +- ...StartDeleteLocalActorApplicationService.kt | 46 +++++++++++ .../SuspendLocalActorApplicationService.kt | 4 +- .../exception/InternalServerException.kt | 14 ++++ .../exception/PermissionDeniedException.kt | 14 ++++ .../LocalUserAbstractApplicationService.kt | 2 +- .../GetUserDetailApplicationServiceTest.kt | 78 +++++++++++++++++++ ...tDeleteLocalActorApplicationServiceTest.kt | 64 +++++++++++++++ 11 files changed, 232 insertions(+), 44 deletions(-) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/DeleteLocalActor.kt delete mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/DeleteLocalActorApplicationService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/StartDeleteLocalActorApplicationService.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/exception/InternalServerException.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/exception/PermissionDeniedException.kt create mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/actor/GetUserDetailApplicationServiceTest.kt create mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/actor/StartDeleteLocalActorApplicationServiceTest.kt diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/DeleteLocalActor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/DeleteLocalActor.kt new file mode 100644 index 00000000..a00a4556 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/DeleteLocalActor.kt @@ -0,0 +1,5 @@ +package dev.usbharu.hideout.core.application.actor + +import dev.usbharu.hideout.core.domain.model.actor.ActorId + +data class DeleteLocalActor(val actorId: ActorId) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/DeleteLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/DeleteLocalActorApplicationService.kt deleted file mode 100644 index 73ef9151..00000000 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/DeleteLocalActorApplicationService.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2024 usbharu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dev.usbharu.hideout.core.application.actor - -import dev.usbharu.hideout.core.application.shared.Transaction -import dev.usbharu.hideout.core.domain.model.actor.ActorId -import dev.usbharu.hideout.core.domain.model.actor.ActorRepository -import org.springframework.stereotype.Service - -@Service -class DeleteLocalActorApplicationService( - private val transaction: Transaction, - private val actorRepository: ActorRepository, -) { - suspend fun delete(actorId: Long, executor: ActorId) { - transaction.transaction { - val id = ActorId(actorId) - val findById = actorRepository.findById(id)!! - findById.delete() - actorRepository.delete(findById) - } - } -} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/GetUserDetailApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/GetUserDetailApplicationService.kt index 1b025ed4..6d990ee2 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/GetUserDetailApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/GetUserDetailApplicationService.kt @@ -16,6 +16,7 @@ package dev.usbharu.hideout.core.application.actor +import dev.usbharu.hideout.core.application.exception.InternalServerException import dev.usbharu.hideout.core.application.shared.AbstractApplicationService import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.domain.model.actor.ActorRepository @@ -36,8 +37,9 @@ class GetUserDetailApplicationService( AbstractApplicationService(transaction, Companion.logger) { override suspend fun internalExecute(command: GetUserDetail, principal: Principal): UserDetail { val userDetail = userDetailRepository.findById(UserDetailId(command.id)) - ?: throw IllegalArgumentException("actor does not exist") - val actor = actorRepository.findById(userDetail.actorId)!! + ?: throw IllegalArgumentException("User ${command.id} does not exist") + val actor = actorRepository.findById(userDetail.actorId) + ?: throw InternalServerException("Actor ${userDetail.actorId} not found") val emojis = customEmojiRepository.findByIds(actor.emojis.map { it.emojiId }) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/RegisterLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/RegisterLocalActorApplicationService.kt index 5819b4c1..202c82e8 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/RegisterLocalActorApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/RegisterLocalActorApplicationService.kt @@ -16,6 +16,7 @@ package dev.usbharu.hideout.core.application.actor +import dev.usbharu.hideout.core.application.exception.InternalServerException import dev.usbharu.hideout.core.application.shared.AbstractApplicationService import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.config.ApplicationConfig @@ -49,9 +50,10 @@ class RegisterLocalActorApplicationService( override suspend fun internalExecute(command: RegisterLocalActor, principal: Principal): URI { if (actorDomainService.usernameAlreadyUse(command.name)) { // todo 適切な例外を考える - throw Exception("Username already exists") + throw IllegalArgumentException("Username already exists") } - val instance = instanceRepository.findByUrl(applicationConfig.url.toURI())!! + val instance = instanceRepository.findByUrl(applicationConfig.url.toURI()) + ?: throw InternalServerException("Local instance not found.") val actor = actorFactoryImpl.createLocal( command.name, diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/StartDeleteLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/StartDeleteLocalActorApplicationService.kt new file mode 100644 index 00000000..9fd8669b --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/StartDeleteLocalActorApplicationService.kt @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2024 usbharu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.usbharu.hideout.core.application.actor + +import dev.usbharu.hideout.core.application.exception.InternalServerException +import dev.usbharu.hideout.core.application.exception.PermissionDeniedException +import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService +import dev.usbharu.hideout.core.application.shared.Transaction +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository +import dev.usbharu.hideout.core.domain.model.support.principal.FromApi +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service + +@Service +class StartDeleteLocalActorApplicationService( + transaction: Transaction, + private val actorRepository: ActorRepository, +) : LocalUserAbstractApplicationService(transaction, logger) { + override suspend fun internalExecute(command: DeleteLocalActor, principal: FromApi) { + if (command.actorId != principal.actorId) { + throw PermissionDeniedException() + } + val findById = actorRepository.findById(command.actorId) + ?: throw InternalServerException("Actor ${command.actorId} Not found") + findById.delete() + actorRepository.save(findById) + } + + companion object { + private val logger = LoggerFactory.getLogger(StartDeleteLocalActorApplicationService::class.java) + } +} diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/SuspendLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/SuspendLocalActorApplicationService.kt index 082208b3..809b3a36 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/SuspendLocalActorApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/SuspendLocalActorApplicationService.kt @@ -30,8 +30,8 @@ class SuspendLocalActorApplicationService( transaction.transaction { val id = ActorId(actorId) - val findById = actorRepository.findById(id)!! - findById.suspend = true + val actor = actorRepository.findById(id)!! + actor.suspend = true } } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/exception/InternalServerException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/exception/InternalServerException.kt new file mode 100644 index 00000000..8acf6a72 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/exception/InternalServerException.kt @@ -0,0 +1,14 @@ +package dev.usbharu.hideout.core.application.exception + +class InternalServerException : RuntimeException { + constructor() : super() + constructor(message: String?) : super(message) + constructor(message: String?, cause: Throwable?) : super(message, cause) + constructor(cause: Throwable?) : super(cause) + constructor(message: String?, cause: Throwable?, enableSuppression: Boolean, writableStackTrace: Boolean) : super( + message, + cause, + enableSuppression, + writableStackTrace + ) +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/exception/PermissionDeniedException.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/exception/PermissionDeniedException.kt new file mode 100644 index 00000000..c583ae0b --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/exception/PermissionDeniedException.kt @@ -0,0 +1,14 @@ +package dev.usbharu.hideout.core.application.exception + +class PermissionDeniedException : RuntimeException { + constructor() : super() + constructor(message: String?) : super(message) + constructor(message: String?, cause: Throwable?) : super(message, cause) + constructor(cause: Throwable?) : super(cause) + constructor(message: String?, cause: Throwable?, enableSuppression: Boolean, writableStackTrace: Boolean) : super( + message, + cause, + enableSuppression, + writableStackTrace + ) +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/LocalUserAbstractApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/LocalUserAbstractApplicationService.kt index e84702d2..cc4ddef8 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/LocalUserAbstractApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/shared/LocalUserAbstractApplicationService.kt @@ -11,5 +11,5 @@ abstract class LocalUserAbstractApplicationService(transaction: Tran return internalExecute(command, principal) } - abstract suspend fun internalExecute(command: T, principal: FromApi): R + protected abstract suspend fun internalExecute(command: T, principal: FromApi): R } \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/actor/GetUserDetailApplicationServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/actor/GetUserDetailApplicationServiceTest.kt new file mode 100644 index 00000000..494a381c --- /dev/null +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/actor/GetUserDetailApplicationServiceTest.kt @@ -0,0 +1,78 @@ +package dev.usbharu.hideout.core.application.actor + +import dev.usbharu.hideout.core.application.exception.InternalServerException +import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository +import dev.usbharu.hideout.core.domain.model.actor.TestActorFactory +import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiRepository +import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailHashedPassword +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +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.any +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.whenever +import utils.TestTransaction + +@ExtendWith(MockitoExtension::class) +class GetUserDetailApplicationServiceTest { + @InjectMocks + lateinit var service: GetUserDetailApplicationService + + @Mock + lateinit var actorRepository: ActorRepository + + @Mock + lateinit var userDetailRepository: UserDetailRepository + + @Mock + lateinit var customEmojiRepository: CustomEmojiRepository + + @Spy + val transaction = TestTransaction + + @Test + fun userDetailを取得できる() = runTest { + whenever(userDetailRepository.findById(UserDetailId(1))).doReturn( + UserDetail.create( + UserDetailId(1), ActorId(1), + UserDetailHashedPassword("") + ) + ) + whenever(actorRepository.findById(ActorId(1))).doReturn(TestActorFactory.create(1)) + whenever(customEmojiRepository.findByIds(any())).doReturn(listOf()) + + service.execute(GetUserDetail(1), Anonymous) + } + + @Test + fun userDetailが存在しない場合失敗() = runTest { + + assertThrows { + service.execute(GetUserDetail(2), Anonymous) + } + } + + @Test + fun userDetailが存在するけどActorが存在しない場合はInternalServerException() = runTest { + whenever(userDetailRepository.findById(UserDetailId(2))).doReturn( + UserDetail.create( + UserDetailId(2), ActorId(2), + UserDetailHashedPassword("") + ) + ) + + assertThrows { + service.execute(GetUserDetail(2), Anonymous) + } + } +} \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/actor/StartDeleteLocalActorApplicationServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/actor/StartDeleteLocalActorApplicationServiceTest.kt new file mode 100644 index 00000000..f99645bf --- /dev/null +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/actor/StartDeleteLocalActorApplicationServiceTest.kt @@ -0,0 +1,64 @@ +package dev.usbharu.hideout.core.application.actor + +import dev.usbharu.hideout.core.application.exception.InternalServerException +import dev.usbharu.hideout.core.application.exception.PermissionDeniedException +import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository +import dev.usbharu.hideout.core.domain.model.actor.TestActorFactory +import dev.usbharu.hideout.core.domain.model.support.acct.Acct +import dev.usbharu.hideout.core.domain.model.support.principal.FromApi +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +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.doReturn +import org.mockito.kotlin.whenever +import utils.TestTransaction + +@ExtendWith(MockitoExtension::class) +class StartDeleteLocalActorApplicationServiceTest { + + @InjectMocks + lateinit var service: StartDeleteLocalActorApplicationService + + @Mock + lateinit var actorRepository: ActorRepository + + @Spy + val transaction = TestTransaction + + @Test + fun ローカルActorを削除できる() = runTest { + whenever(actorRepository.findById(ActorId(1))).doReturn(TestActorFactory.create(1)) + + service.execute( + DeleteLocalActor(ActorId(1)), + FromApi(ActorId(1), UserDetailId((1)), Acct("test", "example.com")) + ) + } + + @Test + fun ログイン中のユーザーと一致しない場合失敗() = runTest { + assertThrows { + service.execute( + DeleteLocalActor(ActorId(2)), + FromApi(ActorId(1), UserDetailId((1)), Acct("test", "example.com")) + ) + } + } + + @Test + fun ユーザーが存在しない場合失敗() = runTest { + assertThrows { + service.execute( + DeleteLocalActor(ActorId(1)), + FromApi(ActorId(1), UserDetailId((1)), Acct("test", "example.com")) + ) + } + } +} \ No newline at end of file From 4fd97b6182e22f82710bde0a0e566890ca5c6e28 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 7 Aug 2024 11:41:05 +0900 Subject: [PATCH 3/9] =?UTF-8?q?feat:=20Post=E3=81=AE=E3=82=A2=E3=82=AF?= =?UTF-8?q?=E3=82=BB=E3=82=B9=E5=88=B6=E5=BE=A1=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../media/UploadMediaApplicationService.kt | 8 +- .../core/application/post/DeleteLocalPost.kt | 3 + .../post/DeleteLocalPostApplicationService.kt | 27 ++- .../post/GetPostApplicationService.kt | 14 +- .../post/DefaultPostReadAccessControl.kt | 54 ++++++ .../other}/DefaultPostContentFormatter.kt | 4 +- .../DeleteLocalPostApplicationServiceTest.kt | 44 +++++ .../post/GetPostApplicationServiceTest.kt | 64 +++++++ .../post/DefaultPostReadAccessControlTest.kt | 158 ++++++++++++++++++ 9 files changed, 359 insertions(+), 17 deletions(-) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/DeleteLocalPost.kt create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/post/DefaultPostReadAccessControl.kt rename hideout-core/src/main/kotlin/dev/usbharu/hideout/core/{domain/service/post => infrastructure/other}/DefaultPostContentFormatter.kt (94%) create mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/post/DeleteLocalPostApplicationServiceTest.kt create mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/post/GetPostApplicationServiceTest.kt create mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/service/post/DefaultPostReadAccessControlTest.kt diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/UploadMediaApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/UploadMediaApplicationService.kt index 909ab7f4..4773f60f 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/UploadMediaApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/media/UploadMediaApplicationService.kt @@ -16,10 +16,10 @@ package dev.usbharu.hideout.core.application.media -import dev.usbharu.hideout.core.application.shared.AbstractApplicationService +import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.domain.model.media.* -import dev.usbharu.hideout.core.domain.model.support.principal.Principal +import dev.usbharu.hideout.core.domain.model.support.principal.FromApi import dev.usbharu.hideout.core.domain.shared.id.IdGenerateService import dev.usbharu.hideout.core.external.media.MediaProcessor import dev.usbharu.hideout.core.external.mediastore.MediaStore @@ -35,11 +35,11 @@ class UploadMediaApplicationService( private val mediaRepository: MediaRepository, private val idGenerateService: IdGenerateService, transaction: Transaction -) : AbstractApplicationService( +) : LocalUserAbstractApplicationService( transaction, logger ) { - override suspend fun internalExecute(command: UploadMedia, principal: Principal): Media { + override suspend fun internalExecute(command: UploadMedia, principal: FromApi): Media { val process = mediaProcessor.process(command.path, command.name, null) val id = idGenerateService.generateId() val thumbnailUri = if (process.thumbnailPath != null) { diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/DeleteLocalPost.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/DeleteLocalPost.kt new file mode 100644 index 00000000..83da8dd1 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/DeleteLocalPost.kt @@ -0,0 +1,3 @@ +package dev.usbharu.hideout.core.application.post + +data class DeleteLocalPost(val postId: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/DeleteLocalPostApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/DeleteLocalPostApplicationService.kt index b4e6a872..511f9629 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/DeleteLocalPostApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/DeleteLocalPostApplicationService.kt @@ -16,24 +16,33 @@ package dev.usbharu.hideout.core.application.post +import dev.usbharu.hideout.core.application.exception.PermissionDeniedException +import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService +import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.post.PostId import dev.usbharu.hideout.core.domain.model.post.PostRepository -import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId -import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository +import dev.usbharu.hideout.core.domain.model.support.principal.FromApi +import org.slf4j.LoggerFactory import org.springframework.stereotype.Service @Service class DeleteLocalPostApplicationService( private val postRepository: PostRepository, - private val userDetailRepository: UserDetailRepository, - private val actorRepository: ActorRepository, -) { - suspend fun delete(postId: Long, userDetailId: Long) { - val findById = postRepository.findById(PostId(postId))!! - val user = userDetailRepository.findById(UserDetailId(userDetailId))!! - val actor = actorRepository.findById(user.actorId)!! + private val actorRepository: ActorRepository, transaction: Transaction, +) : LocalUserAbstractApplicationService(transaction, logger) { + + override suspend fun internalExecute(command: DeleteLocalPost, principal: FromApi) { + val findById = postRepository.findById(PostId(command.postId))!! + if (findById.actorId != principal.actorId) { + throw PermissionDeniedException() + } + val actor = actorRepository.findById(principal.actorId)!! findById.delete(actor) postRepository.save(findById) } + + companion object { + private val logger = LoggerFactory.getLogger(DeleteLocalPostApplicationService::class.java) + } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/GetPostApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/GetPostApplicationService.kt index 8cfde8e4..6e1f0006 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/GetPostApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/GetPostApplicationService.kt @@ -16,21 +16,29 @@ package dev.usbharu.hideout.core.application.post +import dev.usbharu.hideout.core.application.exception.PermissionDeniedException import dev.usbharu.hideout.core.application.shared.AbstractApplicationService import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.domain.model.post.PostId import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.domain.model.support.principal.Principal +import dev.usbharu.hideout.core.domain.service.post.IPostReadAccessControl import org.slf4j.LoggerFactory import org.springframework.stereotype.Service @Service -class GetPostApplicationService(private val postRepository: PostRepository, transaction: Transaction) : +class GetPostApplicationService( + private val postRepository: PostRepository, + private val iPostReadAccessControl: IPostReadAccessControl, + transaction: Transaction +) : AbstractApplicationService(transaction, logger) { override suspend fun internalExecute(command: GetPost, principal: Principal): Post { - val post = postRepository.findById(PostId(command.postId)) ?: throw Exception("Post not found") - + val post = postRepository.findById(PostId(command.postId)) ?: throw IllegalArgumentException("Post not found") + if (iPostReadAccessControl.isAllow(post, principal).not()) { + throw PermissionDeniedException() + } return Post.of(post) } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/post/DefaultPostReadAccessControl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/post/DefaultPostReadAccessControl.kt new file mode 100644 index 00000000..4e7fba5e --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/post/DefaultPostReadAccessControl.kt @@ -0,0 +1,54 @@ +package dev.usbharu.hideout.core.domain.service.post + +import dev.usbharu.hideout.core.domain.model.post.Post +import dev.usbharu.hideout.core.domain.model.post.Visibility +import dev.usbharu.hideout.core.domain.model.relationship.Relationship +import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository +import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous +import dev.usbharu.hideout.core.domain.model.support.principal.Principal +import org.springframework.stereotype.Component + +interface IPostReadAccessControl { + suspend fun isAllow(post: Post, principal: Principal): Boolean +} + +@Component +class DefaultPostReadAccessControl(private val relationshipRepository: RelationshipRepository) : + IPostReadAccessControl { + override suspend fun isAllow(post: Post, principal: Principal): Boolean { + val relationship = (relationshipRepository.findByActorIdAndTargetId(post.actorId, principal.actorId) + ?: Relationship.default(post.actorId, principal.actorId)) + + //ブロックされてたら見れない + if (relationship.blocking) { + return false + } + + //PublicかUnlistedなら見れる + if (post.visibility == Visibility.PUBLIC || post.visibility == Visibility.UNLISTED) { + return true + } + + //principalがAnonymousなら見れない + if (principal is Anonymous) { + return false + } + + //DirectでvisibleActorsに含まれていたら見れる + if (post.visibility == Visibility.DIRECT && post.visibleActors.contains(principal.actorId)) { + return true + } + + //Followersでフォロワーなら見れる + if (post.visibility == Visibility.FOLLOWERS) { + val inverseRelationship = + relationshipRepository.findByActorIdAndTargetId(principal.actorId, post.actorId) ?: return false + + return inverseRelationship.following + } + + //その他の場合は見れない + return false + } + +} \ No newline at end of file diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/post/DefaultPostContentFormatter.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/other/DefaultPostContentFormatter.kt similarity index 94% rename from hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/post/DefaultPostContentFormatter.kt rename to hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/other/DefaultPostContentFormatter.kt index b1b0f86e..30fbb9e2 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/post/DefaultPostContentFormatter.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/other/DefaultPostContentFormatter.kt @@ -14,8 +14,10 @@ * limitations under the License. */ -package dev.usbharu.hideout.core.domain.service.post +package dev.usbharu.hideout.core.infrastructure.other +import dev.usbharu.hideout.core.domain.service.post.FormattedPostContent +import dev.usbharu.hideout.core.domain.service.post.PostContentFormatter import org.jsoup.Jsoup import org.jsoup.nodes.Document import org.jsoup.nodes.Element diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/post/DeleteLocalPostApplicationServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/post/DeleteLocalPostApplicationServiceTest.kt new file mode 100644 index 00000000..a52808ee --- /dev/null +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/post/DeleteLocalPostApplicationServiceTest.kt @@ -0,0 +1,44 @@ +package dev.usbharu.hideout.core.application.post + +import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository +import dev.usbharu.hideout.core.domain.model.actor.TestActorFactory +import dev.usbharu.hideout.core.domain.model.post.PostId +import dev.usbharu.hideout.core.domain.model.post.PostRepository +import dev.usbharu.hideout.core.domain.model.post.TestPostFactory +import dev.usbharu.hideout.core.domain.model.support.acct.Acct +import dev.usbharu.hideout.core.domain.model.support.principal.FromApi +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId +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.doReturn +import org.mockito.kotlin.whenever +import utils.TestTransaction + +@ExtendWith(MockitoExtension::class) +class DeleteLocalPostApplicationServiceTest { + @InjectMocks + lateinit var service: DeleteLocalPostApplicationService + + @Mock + lateinit var postRepository: PostRepository + + @Mock + lateinit var actorRepository: ActorRepository + + @Spy + val transaction = TestTransaction + + @Test + fun Post主はローカルPostを削除できる() = runTest { + whenever(postRepository.findById(PostId(1))).doReturn(TestPostFactory.create(actorId = 2)) + whenever(actorRepository.findById(ActorId(2))).doReturn(TestActorFactory.create(id = 2)) + + service.execute(DeleteLocalPost(1), FromApi(ActorId(2), UserDetailId(2), Acct("test", "example.com"))) + } +} \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/post/GetPostApplicationServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/post/GetPostApplicationServiceTest.kt new file mode 100644 index 00000000..c8cf9561 --- /dev/null +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/post/GetPostApplicationServiceTest.kt @@ -0,0 +1,64 @@ +package dev.usbharu.hideout.core.application.post + +import dev.usbharu.hideout.core.application.exception.PermissionDeniedException +import dev.usbharu.hideout.core.domain.model.post.PostId +import dev.usbharu.hideout.core.domain.model.post.PostRepository +import dev.usbharu.hideout.core.domain.model.post.TestPostFactory +import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous +import dev.usbharu.hideout.core.domain.service.post.IPostReadAccessControl +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +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.any +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.whenever +import utils.TestTransaction + +@ExtendWith(MockitoExtension::class) +class GetPostApplicationServiceTest { + @InjectMocks + lateinit var service: GetPostApplicationService + + @Mock + lateinit var postRepository: PostRepository + + @Mock + lateinit var iPostReadAccessControl: IPostReadAccessControl + + @Spy + val transaction = TestTransaction + + @Test + fun postReadAccessControlがtrueを返したらPostが返ってくる() = runTest { + val post = TestPostFactory.create(id = 1) + whenever(postRepository.findById(PostId(1))).doReturn(post) + whenever(iPostReadAccessControl.isAllow(any(), any())).doReturn(true) + + val actual = service.execute(GetPost(1), Anonymous) + assertEquals(Post.of(post), actual) + } + + @Test + fun postが見つからない場合失敗() = runTest { + assertThrows { + service.execute(GetPost(2), Anonymous) + } + } + + @Test + fun postReadAccessControlがfalseを返したら失敗() = runTest { + val post = TestPostFactory.create(id = 1) + whenever(postRepository.findById(PostId(1))).doReturn(post) + whenever(iPostReadAccessControl.isAllow(any(), any())).doReturn(false) + assertThrows { + service.execute(GetPost(1), Anonymous) + } + + } +} \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/service/post/DefaultPostReadAccessControlTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/service/post/DefaultPostReadAccessControlTest.kt new file mode 100644 index 00000000..2826cb58 --- /dev/null +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/service/post/DefaultPostReadAccessControlTest.kt @@ -0,0 +1,158 @@ +package dev.usbharu.hideout.core.domain.service.post + +import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.model.post.TestPostFactory +import dev.usbharu.hideout.core.domain.model.post.Visibility +import dev.usbharu.hideout.core.domain.model.relationship.Relationship +import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository +import dev.usbharu.hideout.core.domain.model.support.acct.Acct +import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous +import dev.usbharu.hideout.core.domain.model.support.principal.FromApi +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.InjectMocks +import org.mockito.Mock +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.whenever + +@ExtendWith(MockitoExtension::class) +class DefaultPostReadAccessControlTest { + @InjectMocks + lateinit var service: DefaultPostReadAccessControl + + @Mock + lateinit var relationshipRepository: RelationshipRepository + + @Test + fun ブロックされてたら見れない() = runTest { + whenever(relationshipRepository.findByActorIdAndTargetId(ActorId(1), ActorId(2))).doReturn( + Relationship( + actorId = ActorId(1), + targetActorId = ActorId(2), + following = false, + blocking = true, + muting = false, + followRequesting = false, + mutingFollowRequest = false, + ) + ) + + val actual = service.isAllow( + TestPostFactory.create(actorId = 1), + FromApi(ActorId(2), UserDetailId(2), Acct("test", "example.com")) + ) + + assertFalse(actual) + } + + @Test + fun PublicかUnlistedなら見れる() = runTest { + val actual = service.isAllow(TestPostFactory.create(visibility = Visibility.PUBLIC), Anonymous) + assertTrue(actual) + + val actual2 = service.isAllow(TestPostFactory.create(visibility = Visibility.UNLISTED), Anonymous) + assertTrue(actual2) + } + + @Test + fun FollowersかDirecのときAnonymousなら見れない() = runTest { + val actual = service.isAllow(TestPostFactory.create(visibility = Visibility.FOLLOWERS), Anonymous) + assertFalse(actual) + + val actual2 = service.isAllow(TestPostFactory.create(visibility = Visibility.DIRECT), Anonymous) + assertFalse(actual2) + } + + @Test + fun DirectでvisibleActorsに含まれていたら見れる() = runTest { + val actual = service.isAllow( + TestPostFactory.create(actorId = 1, visibility = Visibility.DIRECT, visibleActors = listOf(2)), + FromApi(ActorId(2), UserDetailId(2), Acct("test", "example.com")) + ) + + assertTrue(actual) + } + + @Test + fun DirectでvisibleActorsに含まれていなかったら見れない() = runTest { + val actual = service.isAllow( + TestPostFactory.create(actorId = 1, visibility = Visibility.DIRECT, visibleActors = listOf(3)), + FromApi(ActorId(2), UserDetailId(2), Acct("test", "example.com")) + ) + + assertFalse(actual) + } + + @Test + fun Followersでフォロワーなら見れる() = runTest { + whenever(relationshipRepository.findByActorIdAndTargetId(ActorId(1), ActorId(2))).doReturn( + Relationship.default( + actorId = ActorId(1), + targetActorId = ActorId(2) + ) + ) + whenever(relationshipRepository.findByActorIdAndTargetId(ActorId(2), ActorId(1))).doReturn( + Relationship( + actorId = ActorId(2), + targetActorId = ActorId(1), + following = true, + blocking = false, + muting = false, + followRequesting = false, + mutingFollowRequest = false + ) + ) + + + val actual = service.isAllow( + TestPostFactory.create(actorId = 1, visibility = Visibility.FOLLOWERS), + FromApi(ActorId(2), UserDetailId(2), Acct("test", "example.com")) + ) + + assertTrue(actual) + } + + @Test + fun relationshipが見つからない場合見れない() = runTest { + val actual = service.isAllow( + TestPostFactory.create(actorId = 1, visibility = Visibility.FOLLOWERS), + FromApi(ActorId(2), UserDetailId(2), Acct("test", "example.com")) + ) + + assertFalse(actual) + } + + @Test + fun フォロワーじゃない場合は見れない() = runTest { + whenever(relationshipRepository.findByActorIdAndTargetId(ActorId(1), ActorId(2))).doReturn( + Relationship.default( + actorId = ActorId(1), + targetActorId = ActorId(2) + ) + ) + whenever(relationshipRepository.findByActorIdAndTargetId(ActorId(2), ActorId(1))).doReturn( + Relationship( + actorId = ActorId(2), + targetActorId = ActorId(1), + following = false, + blocking = false, + muting = false, + followRequesting = false, + mutingFollowRequest = false + ) + ) + + + val actual = service.isAllow( + TestPostFactory.create(actorId = 1, visibility = Visibility.FOLLOWERS), + FromApi(ActorId(2), UserDetailId(2), Acct("test", "example.com")) + ) + + assertFalse(actual) + } +} \ No newline at end of file From 046350ef844c949c3c41b861acf88d92f51db5bf Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 7 Aug 2024 12:13:52 +0900 Subject: [PATCH 4/9] wip --- .../other/DefaultPostContentFormatter.kt | 22 +++++++- .../other/DefaultPostContentFormatterTest.kt | 56 +++++++++++++++++++ 2 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/infrastructure/other/DefaultPostContentFormatterTest.kt diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/other/DefaultPostContentFormatter.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/other/DefaultPostContentFormatter.kt index 30fbb9e2..c6c02dc5 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/other/DefaultPostContentFormatter.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/other/DefaultPostContentFormatter.kt @@ -43,6 +43,8 @@ class DefaultPostContentFormatter(private val policyFactory: PolicyFactory) : Po // 文字だけのHTMLなどはここでpタグで囲む val flattenHtml = unsafeElement.childNodes().mapNotNull { + println(it.toString()) + println(it.javaClass) if (it is Element) { it } else if (it is TextNode) { @@ -57,6 +59,8 @@ class DefaultPostContentFormatter(private val policyFactory: PolicyFactory) : Po val safeHtml = policyFactory.sanitize(unsafeHtml) + println(safeHtml) + val safeDocument = Jsoup.parseBodyFragment(safeHtml).getElementsByTag("body").first() ?: return FormattedPostContent("", "") @@ -71,11 +75,25 @@ class DefaultPostContentFormatter(private val policyFactory: PolicyFactory) : Po if (childNode is Element && childNode.tagName() == "br") { brCount++ } else if (brCount >= 2) { - formattedHtml.add(Element("p").appendChildren(childNodes.subList(prevIndex, index - brCount))) + formattedHtml.add( + Element(element.tag(), element.baseUri(), element.attributes()).appendChildren( + childNodes.subList( + prevIndex, + index - brCount + ) + ) + ) prevIndex = index } } - formattedHtml.add(Element("p").appendChildren(childNodes.subList(prevIndex, childNodes.size))) + formattedHtml.add( + Element(element.tag(), element.baseUri(), element.attributes()).appendChildren( + childNodes.subList( + prevIndex, + childNodes.size + ) + ) + ) } val elements = Elements(formattedHtml) diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/infrastructure/other/DefaultPostContentFormatterTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/infrastructure/other/DefaultPostContentFormatterTest.kt new file mode 100644 index 00000000..05ee9f62 --- /dev/null +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/infrastructure/other/DefaultPostContentFormatterTest.kt @@ -0,0 +1,56 @@ +package dev.usbharu.hideout.core.infrastructure.other + +import dev.usbharu.hideout.core.config.HtmlSanitizeConfig +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.InjectMocks +import org.mockito.Spy +import org.mockito.junit.jupiter.MockitoExtension + +@ExtendWith(MockitoExtension::class) +class DefaultPostContentFormatterTest { + @InjectMocks + lateinit var formatter: DefaultPostContentFormatter + + @Spy + val policyFactory = HtmlSanitizeConfig().policy() + + @Test + fun 文字だけのHTMLをPで囲む() { + formatter.format("a") + } + + @Test + fun エレメントはそのまま() { + formatter.format("

a

") + } + + @Test + fun コメントは無視() { + formatter.format("") + } + + @Test + fun brタグを改行に() { + formatter.format("

a

") + } + + @Test + fun brタグ2連続を段落に() { + val format = formatter.format("

a

a

") + + println(format) + } + + @Test + fun aタグは許可される() { + val format = formatter.format("p") + + println(format) + } + + @Test + fun pの中のaタグも許可される() { + formatter.format("

a

") + } +} \ No newline at end of file From f96c16b2a331b3baef6c2c11440a5efcb1454842 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Wed, 7 Aug 2024 18:47:24 +0900 Subject: [PATCH 5/9] =?UTF-8?q?test:=20=E3=82=BF=E3=82=A4=E3=83=A0?= =?UTF-8?q?=E3=83=A9=E3=82=A4=E3=83=B3=E3=81=AE=E3=83=86=E3=82=B9=E3=83=88?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TimelineObjectWarnFilter.kt | 2 +- .../other/DefaultPostContentFormatter.kt | 7 +- .../timeline/AbstractTimelineStore.kt | 4 +- .../other/DefaultPostContentFormatterTest.kt | 33 +- .../TwitterSnowflakeIdGenerateServiceTest.kt | 30 ++ .../timeline/DefaultTimelineStoreTest.kt | 348 ++++++++++++++++++ 6 files changed, 410 insertions(+), 14 deletions(-) create mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/infrastructure/other/TwitterSnowflakeIdGenerateServiceTest.kt create mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/DefaultTimelineStoreTest.kt diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/timelineobject/TimelineObjectWarnFilter.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/timelineobject/TimelineObjectWarnFilter.kt index 6950d4e9..bdd0764d 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/timelineobject/TimelineObjectWarnFilter.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/timelineobject/TimelineObjectWarnFilter.kt @@ -2,4 +2,4 @@ package dev.usbharu.hideout.core.domain.model.timelineobject import dev.usbharu.hideout.core.domain.model.filter.FilterId -class TimelineObjectWarnFilter(val filterId: FilterId, val matchedKeyword: String) +data class TimelineObjectWarnFilter(val filterId: FilterId, val matchedKeyword: String) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/other/DefaultPostContentFormatter.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/other/DefaultPostContentFormatter.kt index c6c02dc5..0998a495 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/other/DefaultPostContentFormatter.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/other/DefaultPostContentFormatter.kt @@ -43,8 +43,6 @@ class DefaultPostContentFormatter(private val policyFactory: PolicyFactory) : Po // 文字だけのHTMLなどはここでpタグで囲む val flattenHtml = unsafeElement.childNodes().mapNotNull { - println(it.toString()) - println(it.javaClass) if (it is Element) { it } else if (it is TextNode) { @@ -59,8 +57,6 @@ class DefaultPostContentFormatter(private val policyFactory: PolicyFactory) : Po val safeHtml = policyFactory.sanitize(unsafeHtml) - println(safeHtml) - val safeDocument = Jsoup.parseBodyFragment(safeHtml).getElementsByTag("body").first() ?: return FormattedPostContent("", "") @@ -97,6 +93,9 @@ class DefaultPostContentFormatter(private val policyFactory: PolicyFactory) : Po } val elements = Elements(formattedHtml) + val document1 = Document("") + document1.outputSettings().syntax(Document.OutputSettings.Syntax.xml) + document1.insertChildren(0, elements) return FormattedPostContent(elements.outerHtml().replace("\n", ""), printHtml(elements)) } 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 38560c81..564f188a 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 @@ -263,7 +263,7 @@ abstract class AbstractTimelineStore(private val idGenerateService: IdGenerateSe ) } - abstract suspend fun getActors(actorIds: List): Map + protected abstract suspend fun getActors(actorIds: List): Map - abstract suspend fun getUserDetails(userDetailIdList: List): Map + protected abstract suspend fun getUserDetails(userDetailIdList: List): Map } diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/infrastructure/other/DefaultPostContentFormatterTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/infrastructure/other/DefaultPostContentFormatterTest.kt index 05ee9f62..91c6413f 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/infrastructure/other/DefaultPostContentFormatterTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/infrastructure/other/DefaultPostContentFormatterTest.kt @@ -1,11 +1,13 @@ package dev.usbharu.hideout.core.infrastructure.other import dev.usbharu.hideout.core.config.HtmlSanitizeConfig +import dev.usbharu.hideout.core.domain.service.post.FormattedPostContent import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import org.mockito.InjectMocks import org.mockito.Spy import org.mockito.junit.jupiter.MockitoExtension +import kotlin.test.assertEquals @ExtendWith(MockitoExtension::class) class DefaultPostContentFormatterTest { @@ -17,40 +19,57 @@ class DefaultPostContentFormatterTest { @Test fun 文字だけのHTMLをPで囲む() { - formatter.format("a") + val actual = formatter.format("a") + + assertEquals(FormattedPostContent("

a

", "a"), actual) } @Test fun エレメントはそのまま() { - formatter.format("

a

") + val actual = formatter.format("

a

") + + assertEquals(FormattedPostContent("

a

", "a"), actual) } @Test fun コメントは無視() { - formatter.format("") + val actual = formatter.format("") + + assertEquals(FormattedPostContent("", ""), actual) } @Test fun brタグを改行に() { - formatter.format("

a

") + val actual = formatter.format("

a

") + + assertEquals(FormattedPostContent("

a

", "a\n"), actual) } @Test fun brタグ2連続を段落に() { val format = formatter.format("

a

a

") - println(format) + assertEquals(FormattedPostContent("

a

a

", "a\n\na"), format) + } + + @Test + fun brタグ3連続以上を段落にして改行2つに変換() { + val format = formatter.format("

a


a

") + + assertEquals(FormattedPostContent("

a

a

", "a\n\na"), format) } @Test fun aタグは許可される() { val format = formatter.format("p") - println(format) + assertEquals(FormattedPostContent("p", "p"), format) } @Test fun pの中のaタグも許可される() { - formatter.format("

a

") + val actual = formatter.format("

a

") + + assertEquals(FormattedPostContent("

a

", "a"), actual) } } \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/infrastructure/other/TwitterSnowflakeIdGenerateServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/infrastructure/other/TwitterSnowflakeIdGenerateServiceTest.kt new file mode 100644 index 00000000..98855ca8 --- /dev/null +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/infrastructure/other/TwitterSnowflakeIdGenerateServiceTest.kt @@ -0,0 +1,30 @@ +package dev.usbharu.hideout.core.infrastructure.other + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test + +class TwitterSnowflakeIdGenerateServiceTest { + @Test + fun noDuplicateTest() = runBlocking { + val mutex = Mutex() + val mutableListOf = mutableListOf() + coroutineScope { + repeat(500000) { + launch(Dispatchers.IO) { + val id = TwitterSnowflakeIdGenerateService.generateId() + mutex.withLock { + mutableListOf.add(id) + } + } + } + } + + assertEquals(0, mutableListOf.size - mutableListOf.toSet().size) + } +} \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/DefaultTimelineStoreTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/DefaultTimelineStoreTest.kt new file mode 100644 index 00000000..66eeb1a0 --- /dev/null +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/DefaultTimelineStoreTest.kt @@ -0,0 +1,348 @@ +package dev.usbharu.hideout.core.infrastructure.timeline + +import dev.usbharu.hideout.core.config.DefaultTimelineStoreConfig +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository +import dev.usbharu.hideout.core.domain.model.filter.* +import dev.usbharu.hideout.core.domain.model.post.PostRepository +import dev.usbharu.hideout.core.domain.model.post.TestPostFactory +import dev.usbharu.hideout.core.domain.model.post.Visibility +import dev.usbharu.hideout.core.domain.model.timeline.* +import dev.usbharu.hideout.core.domain.model.timelineobject.TimelineObject +import dev.usbharu.hideout.core.domain.model.timelineobject.TimelineObjectWarnFilter +import dev.usbharu.hideout.core.domain.model.timelinerelationship.TimelineRelationship +import dev.usbharu.hideout.core.domain.model.timelinerelationship.TimelineRelationshipId +import dev.usbharu.hideout.core.domain.model.timelinerelationship.TimelineRelationshipRepository +import dev.usbharu.hideout.core.domain.model.timelinerelationship.Visible +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository +import dev.usbharu.hideout.core.domain.service.filter.FilterDomainService +import dev.usbharu.hideout.core.infrastructure.other.TwitterSnowflakeIdGenerateService +import kotlinx.coroutines.test.runTest +import org.assertj.core.api.Assertions.assertThat +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.* + +@ExtendWith(MockitoExtension::class) +class DefaultTimelineStoreTest { + @InjectMocks + lateinit var timelineStore: DefaultTimelineStore + + @Mock + lateinit var timelineRepository: TimelineRepository + + @Mock + lateinit var timelineRelationshipRepository: TimelineRelationshipRepository + + @Mock + lateinit var filterRepository: FilterRepository + + @Mock + lateinit var postRepository: PostRepository + + @Mock + lateinit var filterDomainService: FilterDomainService + + @Mock + lateinit var internalTimelineObjectRepository: InternalTimelineObjectRepository + + @Mock + lateinit var userDetailRepository: UserDetailRepository + + @Mock + lateinit var actorRepository: ActorRepository + + @Spy + val defaultTimelineStoreConfig = DefaultTimelineStoreConfig(500) + + @Spy + val idGenerateService = TwitterSnowflakeIdGenerateService + + @Test + fun addPost() = runTest { + val post = TestPostFactory.create() + whenever(timelineRelationshipRepository.findByActorId(post.actorId)).doReturn( + listOf( + TimelineRelationship( + TimelineRelationshipId(1), + TimelineId(12), + post.actorId, + Visible.PUBLIC + ) + ) + ) + + whenever(timelineRepository.findByIds(listOf(TimelineId(12)))).doReturn( + listOf( + Timeline( + id = TimelineId(12), + userDetailId = UserDetailId(post.actorId.id), + name = TimelineName("timeline"), + visibility = TimelineVisibility.PUBLIC, + isSystem = false + ) + ) + ) + + val filters = listOf( + Filter( + id = FilterId(13), + userDetailId = UserDetailId(post.actorId.id), + name = FilterName("filter"), + filterContext = setOf(FilterContext.HOME), + filterAction = FilterAction.HIDE, + filterKeywords = setOf( + FilterKeyword(FilterKeywordId(14), FilterKeywordKeyword("aa"), FilterMode.NONE) + ) + ) + ) + + whenever(filterRepository.findByUserDetailId(UserDetailId(post.actorId.id))).doReturn(filters) + + whenever(filterDomainService.apply(post, FilterContext.HOME, filters)).doReturn( + FilteredPost( + post, listOf( + FilterResult(filters.first(), "aaa") + ) + ) + ) + + timelineStore.addPost(post) + + argumentCaptor> { + verify(internalTimelineObjectRepository, times(1)).saveAll(capture()) + val timelineObjectList = allValues.first() + + assertThat(timelineObjectList).allSatisfy { + assertThat(it.postId).isEqualTo(post.id) + assertThat(it.postActorId).isEqualTo(post.actorId) + assertThat(it.replyId).isNull() + assertThat(it.replyActorId).isNull() + assertThat(it.repostId).isNull() + assertThat(it.repostActorId).isNull() + + assertThat(it.userDetailId).isEqualTo(UserDetailId(post.actorId.id)) + assertThat(it.timelineId).isEqualTo(TimelineId(12)) + assertThat(it.warnFilters).contains(TimelineObjectWarnFilter(FilterId(13), "aaa")) + } + } + } + + @Test + fun `addPost direct投稿は追加されない`() = runTest { + val post = TestPostFactory.create(visibility = Visibility.DIRECT) + whenever(timelineRelationshipRepository.findByActorId(post.actorId)).doReturn( + listOf( + TimelineRelationship( + TimelineRelationshipId(1), + TimelineId(12), + post.actorId, + Visible.PUBLIC + ) + ) + ) + + whenever(timelineRepository.findByIds(listOf(TimelineId(12)))).doReturn( + listOf( + Timeline( + id = TimelineId(12), + userDetailId = UserDetailId(post.actorId.id), + name = TimelineName("timeline"), + visibility = TimelineVisibility.PUBLIC, + isSystem = false + ) + ) + ) + + timelineStore.addPost(post) + + argumentCaptor> { + verify(internalTimelineObjectRepository, times(1)).saveAll(capture()) + val timelineObjectList = allValues.first() + + assertThat(timelineObjectList).isEmpty() + } + } + + @Test + fun timelineがpublicでpostがUNLISTEDの時追加されない() = runTest { + val post = TestPostFactory.create(visibility = Visibility.UNLISTED) + whenever(timelineRelationshipRepository.findByActorId(post.actorId)).doReturn( + listOf( + TimelineRelationship( + TimelineRelationshipId(1), + TimelineId(12), + post.actorId, + Visible.PUBLIC + ) + ) + ) + + whenever(timelineRepository.findByIds(listOf(TimelineId(12)))).doReturn( + listOf( + Timeline( + id = TimelineId(12), + userDetailId = UserDetailId(post.actorId.id), + name = TimelineName("timeline"), + visibility = TimelineVisibility.PUBLIC, + isSystem = false + ) + ) + ) + + timelineStore.addPost(post) + + argumentCaptor> { + verify(internalTimelineObjectRepository, times(1)).saveAll(capture()) + val timelineObjectList = allValues.first() + + assertThat(timelineObjectList).isEmpty() + } + } + + @Test + fun timelineがpublicでpostがFOLLOWERSの時追加されない() = runTest { + val post = TestPostFactory.create(visibility = Visibility.FOLLOWERS) + whenever(timelineRelationshipRepository.findByActorId(post.actorId)).doReturn( + listOf( + TimelineRelationship( + TimelineRelationshipId(1), + TimelineId(12), + post.actorId, + Visible.PUBLIC + ) + ) + ) + + whenever(timelineRepository.findByIds(listOf(TimelineId(12)))).doReturn( + listOf( + Timeline( + id = TimelineId(12), + userDetailId = UserDetailId(post.actorId.id), + name = TimelineName("timeline"), + visibility = TimelineVisibility.PUBLIC, + isSystem = false + ) + ) + ) + + timelineStore.addPost(post) + + argumentCaptor> { + verify(internalTimelineObjectRepository, times(1)).saveAll(capture()) + val timelineObjectList = allValues.first() + + assertThat(timelineObjectList).isEmpty() + } + } + + @Test + fun timelineがUNLISTEDでpostがFOLLOWERSの時追加されない() = runTest { + val post = TestPostFactory.create(visibility = Visibility.FOLLOWERS) + whenever(timelineRelationshipRepository.findByActorId(post.actorId)).doReturn( + listOf( + TimelineRelationship( + TimelineRelationshipId(1), + TimelineId(12), + post.actorId, + Visible.PUBLIC + ) + ) + ) + + whenever(timelineRepository.findByIds(listOf(TimelineId(12)))).doReturn( + listOf( + Timeline( + id = TimelineId(12), + userDetailId = UserDetailId(post.actorId.id), + name = TimelineName("timeline"), + visibility = TimelineVisibility.UNLISTED, + isSystem = false + ) + ) + ) + + timelineStore.addPost(post) + + argumentCaptor> { + verify(internalTimelineObjectRepository, times(1)).saveAll(capture()) + val timelineObjectList = allValues.first() + + assertThat(timelineObjectList).isEmpty() + } + } + + @Test + fun timelineがPRIVATEでpostがFOLLOWERSの時追加される() = runTest { + val post = TestPostFactory.create(visibility = Visibility.FOLLOWERS) + whenever(timelineRelationshipRepository.findByActorId(post.actorId)).doReturn( + listOf( + TimelineRelationship( + TimelineRelationshipId(1), + TimelineId(12), + post.actorId, + Visible.PUBLIC + ) + ) + ) + + whenever(timelineRepository.findByIds(listOf(TimelineId(12)))).doReturn( + listOf( + Timeline( + id = TimelineId(12), + userDetailId = UserDetailId(post.actorId.id), + name = TimelineName("timeline"), + visibility = TimelineVisibility.PRIVATE, + isSystem = false + ) + ) + ) + + val filters = listOf( + Filter( + id = FilterId(13), + userDetailId = UserDetailId(post.actorId.id), + name = FilterName("filter"), + filterContext = setOf(FilterContext.HOME), + filterAction = FilterAction.HIDE, + filterKeywords = setOf( + FilterKeyword(FilterKeywordId(14), FilterKeywordKeyword("aa"), FilterMode.NONE) + ) + ) + ) + + whenever(filterRepository.findByUserDetailId(UserDetailId(post.actorId.id))).doReturn(filters) + + whenever(filterDomainService.apply(post, FilterContext.HOME, filters)).doReturn( + FilteredPost( + post, listOf( + FilterResult(filters.first(), "aaa") + ) + ) + ) + + timelineStore.addPost(post) + + argumentCaptor> { + verify(internalTimelineObjectRepository, times(1)).saveAll(capture()) + val timelineObjectList = allValues.first() + + assertThat(timelineObjectList).allSatisfy { + assertThat(it.postId).isEqualTo(post.id) + assertThat(it.postActorId).isEqualTo(post.actorId) + assertThat(it.replyId).isNull() + assertThat(it.replyActorId).isNull() + assertThat(it.repostId).isNull() + assertThat(it.repostActorId).isNull() + + assertThat(it.userDetailId).isEqualTo(UserDetailId(post.actorId.id)) + assertThat(it.timelineId).isEqualTo(TimelineId(12)) + assertThat(it.warnFilters).contains(TimelineObjectWarnFilter(FilterId(13), "aaa")) + } + } + } +} \ No newline at end of file From f7de5b03f1b35abf3a4e3fa2641b928e1faa9db2 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Thu, 8 Aug 2024 17:29:05 +0900 Subject: [PATCH 6/9] =?UTF-8?q?test:=20=E3=81=8A=E5=BC=95=E8=B6=8A?= =?UTF-8?q?=E3=81=97=E3=81=AE=E3=83=86=E3=82=B9=E3=83=88=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/actor/MigrationLocalActor.kt | 3 + .../MigrationLocalActorApplicationService.kt | 58 ++++-- .../RegisterLocalActorApplicationService.kt | 3 +- .../LocalActorMigrationCheckDomainService.kt | 5 +- ...calActorMigrationCheckDomainServiceImpl.kt | 14 +- ...grationLocalActorApplicationServiceTest.kt | 171 ++++++++++++++++++ ...egisterLocalActorApplicationServiceTest.kt | 78 ++++++++ ...ctorMigrationCheckDomainServiceImplTest.kt | 38 +++- 8 files changed, 337 insertions(+), 33 deletions(-) create mode 100644 hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/MigrationLocalActor.kt create mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/actor/MigrationLocalActorApplicationServiceTest.kt create mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/actor/RegisterLocalActorApplicationServiceTest.kt diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/MigrationLocalActor.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/MigrationLocalActor.kt new file mode 100644 index 00000000..71f7fe96 --- /dev/null +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/MigrationLocalActor.kt @@ -0,0 +1,3 @@ +package dev.usbharu.hideout.core.application.actor + +data class MigrationLocalActor(val from: Long, val to: Long) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/MigrationLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/MigrationLocalActorApplicationService.kt index 6d147f4a..9e0a01e6 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/MigrationLocalActorApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/MigrationLocalActorApplicationService.kt @@ -16,39 +16,59 @@ package dev.usbharu.hideout.core.application.actor +import dev.usbharu.hideout.core.application.exception.InternalServerException +import dev.usbharu.hideout.core.application.exception.PermissionDeniedException +import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.actor.ActorRepository +import dev.usbharu.hideout.core.domain.model.support.principal.FromApi +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository import dev.usbharu.hideout.core.domain.service.actor.local.AccountMigrationCheck.* import dev.usbharu.hideout.core.domain.service.actor.local.LocalActorMigrationCheckDomainService +import org.slf4j.LoggerFactory import org.springframework.stereotype.Service @Service class MigrationLocalActorApplicationService( - private val transaction: Transaction, private val actorRepository: ActorRepository, private val localActorMigrationCheckDomainService: LocalActorMigrationCheckDomainService, -) { - suspend fun migration(from: Long, to: Long, executor: ActorId) { - transaction.transaction { - val fromActorId = ActorId(from) - val toActorId = ActorId(to) + transaction: Transaction, + private val userDetailRepository: UserDetailRepository, +) : LocalUserAbstractApplicationService(transaction, logger) { - val fromActor = actorRepository.findById(fromActorId)!! - val toActor = actorRepository.findById(toActorId)!! + override suspend fun internalExecute(command: MigrationLocalActor, principal: FromApi) { + if (command.from != principal.actorId.id) { + throw PermissionDeniedException() + } - val canAccountMigration = localActorMigrationCheckDomainService.canAccountMigration(fromActor, toActor) - when (canAccountMigration) { - is AlreadyMoved -> TODO() - is CanAccountMigration -> { - fromActor.moveTo = toActorId - actorRepository.save(fromActor) - } + val userDetail = userDetailRepository.findById(principal.userDetailId) + ?: throw InternalServerException("User detail ${principal.userDetailId} not found.") - is CircularReferences -> TODO() - is SelfReferences -> TODO() - is AlsoKnownAsNotFound -> TODO() - } + val fromActorId = ActorId(command.from) + val toActorId = ActorId(command.to) + + val fromActor = + actorRepository.findById(fromActorId) ?: throw IllegalArgumentException("Actor ${command.from} not found.") + val toActor = + actorRepository.findById(toActorId) ?: throw IllegalArgumentException("Actor ${command.to} not found.") + + val canAccountMigration = + localActorMigrationCheckDomainService.canAccountMigration(userDetail, fromActor, toActor) + if (canAccountMigration.canMigration) { + fromActor.moveTo = toActorId + actorRepository.save(fromActor) + } else when (canAccountMigration) { + is AlreadyMoved -> throw IllegalArgumentException(canAccountMigration.message) + is CanAccountMigration -> throw InternalServerException() + is CircularReferences -> throw IllegalArgumentException(canAccountMigration.message) + is SelfReferences -> throw IllegalArgumentException("Self references are not supported") + is AlsoKnownAsNotFound -> throw IllegalArgumentException(canAccountMigration.message) + is MigrationCoolDown -> throw IllegalArgumentException(canAccountMigration.message) } } + + companion object { + private val logger = LoggerFactory.getLogger(MigrationLocalActorApplicationService::class.java) + } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/RegisterLocalActorApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/RegisterLocalActorApplicationService.kt index 202c82e8..27adac58 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/RegisterLocalActorApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/actor/RegisterLocalActorApplicationService.kt @@ -49,7 +49,6 @@ class RegisterLocalActorApplicationService( override suspend fun internalExecute(command: RegisterLocalActor, principal: Principal): URI { if (actorDomainService.usernameAlreadyUse(command.name)) { - // todo 適切な例外を考える throw IllegalArgumentException("Username already exists") } val instance = instanceRepository.findByUrl(applicationConfig.url.toURI()) @@ -74,6 +73,6 @@ class RegisterLocalActorApplicationService( } companion object { - val logger = LoggerFactory.getLogger(RegisterLocalActorApplicationService::class.java) + private val logger = LoggerFactory.getLogger(RegisterLocalActorApplicationService::class.java) } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainService.kt index 3c1adf00..c9eb3338 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainService.kt @@ -17,9 +17,10 @@ package dev.usbharu.hideout.core.domain.service.actor.local import dev.usbharu.hideout.core.domain.model.actor.Actor +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail interface LocalActorMigrationCheckDomainService { - suspend fun canAccountMigration(from: Actor, to: Actor): AccountMigrationCheck + suspend fun canAccountMigration(userDetail: UserDetail, from: Actor, to: Actor): AccountMigrationCheck } sealed class AccountMigrationCheck( @@ -34,4 +35,6 @@ sealed class AccountMigrationCheck( class AlreadyMoved(val message: String) : AccountMigrationCheck(false) class AlsoKnownAsNotFound(val message: String) : AccountMigrationCheck(false) + + class MigrationCoolDown(val message: String) : AccountMigrationCheck(false) } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainServiceImpl.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainServiceImpl.kt index 933f7201..4b295484 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainServiceImpl.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainServiceImpl.kt @@ -17,11 +17,23 @@ package dev.usbharu.hideout.core.domain.service.actor.local import dev.usbharu.hideout.core.domain.model.actor.Actor +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail import org.springframework.stereotype.Service +import java.time.Instant +import kotlin.time.Duration.Companion.days +import kotlin.time.toJavaDuration @Service class LocalActorMigrationCheckDomainServiceImpl : LocalActorMigrationCheckDomainService { - override suspend fun canAccountMigration(from: Actor, to: Actor): AccountMigrationCheck { + override suspend fun canAccountMigration(userDetail: UserDetail, from: Actor, to: Actor): AccountMigrationCheck { + val lastMigration = userDetail.lastMigration + if (lastMigration != null) { + val instant = lastMigration.plus(30.days.toJavaDuration()) + if (instant.isAfter(Instant.now())) { + return AccountMigrationCheck.MigrationCoolDown("You can migration at $instant.") + } + } + if (to == from) { return AccountMigrationCheck.SelfReferences() } diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/actor/MigrationLocalActorApplicationServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/actor/MigrationLocalActorApplicationServiceTest.kt new file mode 100644 index 00000000..3148980d --- /dev/null +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/actor/MigrationLocalActorApplicationServiceTest.kt @@ -0,0 +1,171 @@ +package dev.usbharu.hideout.core.application.actor + +import dev.usbharu.hideout.core.application.exception.InternalServerException +import dev.usbharu.hideout.core.application.exception.PermissionDeniedException +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.actor.ActorRepository +import dev.usbharu.hideout.core.domain.model.actor.TestActorFactory +import dev.usbharu.hideout.core.domain.model.support.acct.Acct +import dev.usbharu.hideout.core.domain.model.support.principal.FromApi +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailHashedPassword +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository +import dev.usbharu.hideout.core.domain.service.actor.local.AccountMigrationCheck +import dev.usbharu.hideout.core.domain.service.actor.local.LocalActorMigrationCheckDomainService +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +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.TestTransaction + +@ExtendWith(MockitoExtension::class) +class MigrationLocalActorApplicationServiceTest { + @InjectMocks + lateinit var service: MigrationLocalActorApplicationService + + @Mock + lateinit var actorRepository: ActorRepository + + @Mock + lateinit var localActorMigrationCheckDomainService: LocalActorMigrationCheckDomainService + + @Mock + lateinit var userDetailRepository: UserDetailRepository + + @Spy + val transaction = TestTransaction + + @Test + fun pricinpalのactorとfromのactorが違うと失敗() = runTest { + assertThrows { + service.execute( + MigrationLocalActor(1, 2), + FromApi(ActorId(3), UserDetailId(3), Acct("test", "example.com")) + ) + } + } + + @Test + fun fromのactorが見つからなかったら失敗() = runTest { + val userDetail = UserDetail.create( + UserDetailId(1), + ActorId(1), UserDetailHashedPassword + ("") + ) + whenever(userDetailRepository.findById(UserDetailId(1))).doReturn(userDetail) + assertThrows { + service.execute( + MigrationLocalActor(1, 2), + FromApi(ActorId(1), UserDetailId(1), Acct("test", "example.com")) + ) + } + } + + @Test + fun toのactorが見つからなかったら失敗() = runTest { + val userDetail = UserDetail.create( + UserDetailId(1), + ActorId(1), UserDetailHashedPassword + ("") + ) + whenever(actorRepository.findById(ActorId(1))).doReturn(TestActorFactory.create(1)) + whenever(userDetailRepository.findById(UserDetailId(1))).doReturn(userDetail) + assertThrows { + service.execute( + MigrationLocalActor(1, 2), + FromApi(ActorId(1), UserDetailId(1), Acct("test", "example.com")) + ) + } + } + + @Test + fun userDetailが見つからなかったら失敗() = runTest { + assertThrows { + service.execute( + MigrationLocalActor(1, 2), + FromApi(ActorId(1), UserDetailId(1), Acct("test", "example.com")) + ) + } + } + + @Test + fun canMigrationがtrueならmoveToを書き込む() = runTest { + val from = TestActorFactory.create(1) + val to = TestActorFactory.create(2) + val userDetail = UserDetail.create( + UserDetailId(1), + ActorId(1), UserDetailHashedPassword + ("") + ) + whenever(actorRepository.findById(ActorId(1))).doReturn(from) + whenever(actorRepository.findById(ActorId(2))).doReturn(to) + whenever(userDetailRepository.findById(UserDetailId(1))).doReturn(userDetail) + + whenever( + localActorMigrationCheckDomainService.canAccountMigration( + userDetail, + from, + to + ) + ).doReturn(AccountMigrationCheck.CanAccountMigration()) + + service.execute( + MigrationLocalActor(1, 2), + FromApi(ActorId(1), UserDetailId(1), Acct("test", "example.com")) + ) + + argumentCaptor { + verify(actorRepository, times(1)).save(capture()) + val first = allValues.first() + + assertEquals(first.moveTo, to.id) + } + } + + @Test + fun canMigrationがfalseなら例外() = runTest { + val from = TestActorFactory.create(1) + val to = TestActorFactory.create(2) + val userDetail = UserDetail.create( + UserDetailId(1), + ActorId(1), UserDetailHashedPassword + ("") + ) + + whenever(actorRepository.findById(ActorId(1))).doReturn(from) + whenever(actorRepository.findById(ActorId(2))).doReturn(to) + whenever(userDetailRepository.findById(UserDetailId(1))).doReturn(userDetail) + whenever( + localActorMigrationCheckDomainService.canAccountMigration( + userDetail, + from, + to + ) + ).doReturn( + AccountMigrationCheck.AlreadyMoved("Message"), + AccountMigrationCheck.CircularReferences("Message"), + AccountMigrationCheck.SelfReferences(), + AccountMigrationCheck.AlsoKnownAsNotFound("Message"), + AccountMigrationCheck.MigrationCoolDown("Message") + ) + + repeat(5) { + assertThrows { + service.execute( + MigrationLocalActor(1, 2), + FromApi(ActorId(1), UserDetailId(1), Acct("test", "example.com")) + ) + } + } + + verify(actorRepository, never()).save(any()) + } +} \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/actor/RegisterLocalActorApplicationServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/actor/RegisterLocalActorApplicationServiceTest.kt new file mode 100644 index 00000000..e0087535 --- /dev/null +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/actor/RegisterLocalActorApplicationServiceTest.kt @@ -0,0 +1,78 @@ +package dev.usbharu.hideout.core.application.actor + +import dev.usbharu.hideout.core.application.exception.InternalServerException +import dev.usbharu.hideout.core.config.ApplicationConfig +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository +import dev.usbharu.hideout.core.domain.model.instance.InstanceRepository +import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository +import dev.usbharu.hideout.core.domain.service.actor.local.LocalActorDomainService +import dev.usbharu.hideout.core.domain.service.userdetail.UserDetailDomainService +import dev.usbharu.hideout.core.infrastructure.factory.ActorFactoryImpl +import dev.usbharu.hideout.core.infrastructure.other.TwitterSnowflakeIdGenerateService +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +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.doReturn +import org.mockito.kotlin.eq +import org.mockito.kotlin.whenever +import utils.TestTransaction +import java.net.URL + +@ExtendWith(MockitoExtension::class) +class RegisterLocalActorApplicationServiceTest { + @InjectMocks + lateinit var service: RegisterLocalActorApplicationService + + @Mock + lateinit var actorDomainService: LocalActorDomainService + + @Mock + lateinit var actorRepository: ActorRepository + + @Mock + lateinit var actorFactoryImpl: ActorFactoryImpl + + @Mock + lateinit var instanceRepository: InstanceRepository + + @Mock + lateinit var userDetailDomainService: UserDetailDomainService + + @Mock + lateinit var userDetailRepository: UserDetailRepository + + @Spy + val transaction = TestTransaction + + @Spy + val applicationConfig = ApplicationConfig(URL("http://example.com")) + + @Spy + val idGenerateService = TwitterSnowflakeIdGenerateService + + @Test + fun usernameがすでに使われていた場合失敗() = runTest { + whenever(actorDomainService.usernameAlreadyUse(eq("test"))).doReturn(true) + + assertThrows { + service.execute(RegisterLocalActor("test", "password"), Anonymous) + } + } + + @Test + fun ローカルインスタンスが見つからない場合失敗() = runTest { + whenever(actorDomainService.usernameAlreadyUse(eq("test"))).doReturn(false) + + assertThrows { + service.execute(RegisterLocalActor("test", "password"), Anonymous) + } + } + + +} \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainServiceImplTest.kt index 7428222c..c44382b9 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainServiceImplTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainServiceImplTest.kt @@ -2,6 +2,9 @@ package dev.usbharu.hideout.core.domain.service.actor.local import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.actor.TestActorFactory +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailHashedPassword +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Assertions.assertInstanceOf import org.junit.jupiter.api.Test @@ -12,10 +15,13 @@ class LocalActorMigrationCheckDomainServiceImplTest { val from = TestActorFactory.create() val to = TestActorFactory.create() - + val userDetail = UserDetail.create( + UserDetailId(1), + ActorId(1), UserDetailHashedPassword("") + ) val localActorMigrationCheckDomainServiceImpl = LocalActorMigrationCheckDomainServiceImpl() - val canAccountMigration = localActorMigrationCheckDomainServiceImpl.canAccountMigration(from, from) + val canAccountMigration = localActorMigrationCheckDomainServiceImpl.canAccountMigration(userDetail, from, from) assertInstanceOf(AccountMigrationCheck.SelfReferences::class.java, canAccountMigration) } @@ -25,10 +31,13 @@ class LocalActorMigrationCheckDomainServiceImplTest { val from = TestActorFactory.create() val to = TestActorFactory.create(moveTo = 100) - + val userDetail = UserDetail.create( + UserDetailId(1), + ActorId(1), UserDetailHashedPassword("") + ) val localActorMigrationCheckDomainServiceImpl = LocalActorMigrationCheckDomainServiceImpl() - val canAccountMigration = localActorMigrationCheckDomainServiceImpl.canAccountMigration(from, to) + val canAccountMigration = localActorMigrationCheckDomainServiceImpl.canAccountMigration(userDetail, from, to) assertInstanceOf(AccountMigrationCheck.AlreadyMoved::class.java, canAccountMigration) } @@ -37,10 +46,13 @@ class LocalActorMigrationCheckDomainServiceImplTest { fun 自分自身が引っ越している場合は引っ越しできない() = runTest { val from = TestActorFactory.create(moveTo = 100) val to = TestActorFactory.create() - + val userDetail = UserDetail.create( + UserDetailId(1), + ActorId(1), UserDetailHashedPassword("") + ) val localActorMigrationCheckDomainServiceImpl = LocalActorMigrationCheckDomainServiceImpl() - val canAccountMigration = localActorMigrationCheckDomainServiceImpl.canAccountMigration(from, to) + val canAccountMigration = localActorMigrationCheckDomainServiceImpl.canAccountMigration(userDetail, from, to) assertInstanceOf(AccountMigrationCheck.AlreadyMoved::class.java, canAccountMigration) } @@ -49,10 +61,13 @@ class LocalActorMigrationCheckDomainServiceImplTest { fun 引越し先のalsoKnownAsに引越し元が含まれてない場合失敗する() = runTest { val from = TestActorFactory.create() val to = TestActorFactory.create(alsoKnownAs = setOf(ActorId(100))) - + val userDetail = UserDetail.create( + UserDetailId(1), + ActorId(1), UserDetailHashedPassword("") + ) val localActorMigrationCheckDomainServiceImpl = LocalActorMigrationCheckDomainServiceImpl() - val canAccountMigration = localActorMigrationCheckDomainServiceImpl.canAccountMigration(from, to) + val canAccountMigration = localActorMigrationCheckDomainServiceImpl.canAccountMigration(userDetail, from, to) assertInstanceOf(AccountMigrationCheck.AlsoKnownAsNotFound::class.java, canAccountMigration) } @@ -61,10 +76,13 @@ class LocalActorMigrationCheckDomainServiceImplTest { fun 正常に設定されている場合は成功する() = runTest { val from = TestActorFactory.create() val to = TestActorFactory.create(alsoKnownAs = setOf(from.id, ActorId(100))) - + val userDetail = UserDetail.create( + UserDetailId(1), + ActorId(1), UserDetailHashedPassword("") + ) val localActorMigrationCheckDomainServiceImpl = LocalActorMigrationCheckDomainServiceImpl() - val canAccountMigration = localActorMigrationCheckDomainServiceImpl.canAccountMigration(from, to) + val canAccountMigration = localActorMigrationCheckDomainServiceImpl.canAccountMigration(userDetail, from, to) assertInstanceOf(AccountMigrationCheck.CanAccountMigration::class.java, canAccountMigration) } From 2cfc8bc0d8109e6a7798d1c060b4657bbf10c66d Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Fri, 9 Aug 2024 00:16:14 +0900 Subject: [PATCH 7/9] =?UTF-8?q?test:=20=E3=83=86=E3=82=B9=E3=83=88?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/application/post/UpdateLocalNote.kt | 5 +- .../post/UpdateLocalNoteApplicationService.kt | 22 ++-- .../DeleteLocalPostApplicationServiceTest.kt | 11 ++ .../UpdateLocalNoteApplicationServiceTest.kt | 109 ++++++++++++++++++ ...LocalUserAbstractApplicationServiceTest.kt | 24 ++++ .../domain/model/support/domain/DomainTest.kt | 14 +++ .../local/LocalActorDomainServiceImplTest.kt | 51 ++++++++ ...ctorMigrationCheckDomainServiceImplTest.kt | 37 ++++++ .../oauth2/UserDetailsServiceImplTest.kt | 91 +++++++++++++++ 9 files changed, 353 insertions(+), 11 deletions(-) create mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/post/UpdateLocalNoteApplicationServiceTest.kt create mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/shared/LocalUserAbstractApplicationServiceTest.kt create mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/support/domain/DomainTest.kt create mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorDomainServiceImplTest.kt create mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsServiceImplTest.kt diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/UpdateLocalNote.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/UpdateLocalNote.kt index 49546b6d..64ce9831 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/UpdateLocalNote.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/UpdateLocalNote.kt @@ -16,13 +16,10 @@ package dev.usbharu.hideout.core.application.post -import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId - data class UpdateLocalNote( val postId: Long, val overview: String?, val content: String, val sensitive: Boolean, - val mediaIds: List, - val userDetailId: UserDetailId + val mediaIds: List ) diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/UpdateLocalNoteApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/UpdateLocalNoteApplicationService.kt index 6a24184c..17bafb70 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/UpdateLocalNoteApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/UpdateLocalNoteApplicationService.kt @@ -16,14 +16,16 @@ package dev.usbharu.hideout.core.application.post -import dev.usbharu.hideout.core.application.shared.AbstractApplicationService +import dev.usbharu.hideout.core.application.exception.InternalServerException +import dev.usbharu.hideout.core.application.exception.PermissionDeniedException +import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.media.MediaId import dev.usbharu.hideout.core.domain.model.post.PostId import dev.usbharu.hideout.core.domain.model.post.PostOverview import dev.usbharu.hideout.core.domain.model.post.PostRepository -import dev.usbharu.hideout.core.domain.model.support.principal.Principal +import dev.usbharu.hideout.core.domain.model.support.principal.FromApi import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository import dev.usbharu.hideout.core.infrastructure.factory.PostContentFactoryImpl import org.slf4j.LoggerFactory @@ -36,13 +38,19 @@ class UpdateLocalNoteApplicationService( private val postContentFactoryImpl: PostContentFactoryImpl, private val userDetailRepository: UserDetailRepository, private val actorRepository: ActorRepository, -) : AbstractApplicationService(transaction, logger) { +) : LocalUserAbstractApplicationService(transaction, logger) { - override suspend fun internalExecute(command: UpdateLocalNote, principal: Principal) { + override suspend fun internalExecute(command: UpdateLocalNote, principal: FromApi) { + val post = postRepository.findById(PostId(command.postId)) + ?: throw IllegalArgumentException("Post ${command.postId} not found.") + if (post.actorId != principal.actorId) { + throw PermissionDeniedException() + } - val userDetail = userDetailRepository.findById(command.userDetailId)!! - val actor = actorRepository.findById(userDetail.actorId)!! - val post = postRepository.findById(PostId(command.postId))!! + val userDetail = userDetailRepository.findById(principal.userDetailId) + ?: throw InternalServerException("User detail ${principal.userDetailId} not found.") + val actor = actorRepository.findById(userDetail.actorId) + ?: throw InternalServerException("Actor ${principal.actorId} not found.") post.setContent(postContentFactoryImpl.create(command.content), actor) post.setOverview(command.overview?.let { PostOverview(it) }, actor) diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/post/DeleteLocalPostApplicationServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/post/DeleteLocalPostApplicationServiceTest.kt index a52808ee..b4c747ba 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/post/DeleteLocalPostApplicationServiceTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/post/DeleteLocalPostApplicationServiceTest.kt @@ -1,5 +1,6 @@ package dev.usbharu.hideout.core.application.post +import dev.usbharu.hideout.core.application.exception.PermissionDeniedException import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.actor.TestActorFactory @@ -11,6 +12,7 @@ import dev.usbharu.hideout.core.domain.model.support.principal.FromApi import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.extension.ExtendWith import org.mockito.InjectMocks import org.mockito.Mock @@ -41,4 +43,13 @@ class DeleteLocalPostApplicationServiceTest { service.execute(DeleteLocalPost(1), FromApi(ActorId(2), UserDetailId(2), Acct("test", "example.com"))) } + + @Test + fun Post主以外はローカルPostを削除できない() = runTest { + whenever(postRepository.findById(PostId(1))).doReturn(TestPostFactory.create(actorId = 2)) + + assertThrows { + service.execute(DeleteLocalPost(1), FromApi(ActorId(3), UserDetailId(3), Acct("test", "example.com"))) + } + } } \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/post/UpdateLocalNoteApplicationServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/post/UpdateLocalNoteApplicationServiceTest.kt new file mode 100644 index 00000000..e5ad0714 --- /dev/null +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/post/UpdateLocalNoteApplicationServiceTest.kt @@ -0,0 +1,109 @@ +package dev.usbharu.hideout.core.application.post + +import dev.usbharu.hideout.core.application.exception.PermissionDeniedException +import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository +import dev.usbharu.hideout.core.domain.model.actor.TestActorFactory +import dev.usbharu.hideout.core.domain.model.post.* +import dev.usbharu.hideout.core.domain.model.post.Post +import dev.usbharu.hideout.core.domain.model.support.acct.Acct +import dev.usbharu.hideout.core.domain.model.support.principal.FromApi +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailHashedPassword +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository +import dev.usbharu.hideout.core.infrastructure.factory.PostContentFactoryImpl +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +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.TestTransaction + +@ExtendWith(MockitoExtension::class) +class UpdateLocalNoteApplicationServiceTest { + @InjectMocks + lateinit var service: UpdateLocalNoteApplicationService + + @Mock + lateinit var postRepository: PostRepository + + @Mock + lateinit var userDetailRepository: UserDetailRepository + + @Mock + lateinit var actorRepository: ActorRepository + + @Mock + lateinit var postContentFactoryImpl: PostContentFactoryImpl + + @Spy + val transaction = TestTransaction + + + @Test + fun Post主はPostを編集できる() = runTest { + val post = TestPostFactory.create() + + whenever(postRepository.findById(post.id)).doReturn(post) + whenever(userDetailRepository.findById(UserDetailId(1))).doReturn( + UserDetail.create( + UserDetailId(1), post.actorId, + UserDetailHashedPassword("") + ) + ) + whenever(actorRepository.findById(post.actorId)).doReturn(TestActorFactory.create(id = post.actorId.id)) + val content = PostContent("

test

", "test", emptyList()) + whenever(postContentFactoryImpl.create(eq("test"))).doReturn(content) + + service.execute( + UpdateLocalNote(post.id.id, null, "test", false, emptyList()), FromApi( + post.actorId, + UserDetailId(1), + Acct("test", "example.com") + ) + ) + + argumentCaptor { + verify(postRepository, times(1)).save(capture()) + val first = allValues.first() + + assertEquals( + content, first.content + ) + } + } + + @Test + fun postが見つからない場合失敗() = runTest { + assertThrows { + service.execute( + UpdateLocalNote(1, null, "test", false, emptyList()), FromApi( + ActorId(1), + UserDetailId(1), Acct("test", "example.com") + ) + ) + } + } + + @Test + fun post主じゃない場合失敗() = runTest { + whenever(postRepository.findById(PostId(1))).doReturn(TestPostFactory.create(id = 1, actorId = 3)) + + assertThrows { + service.execute( + UpdateLocalNote(1, null, "test", false, emptyList()), FromApi( + ActorId(1), + UserDetailId(1), Acct("test", "example.com") + ) + ) + } + } + + +} \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/shared/LocalUserAbstractApplicationServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/shared/LocalUserAbstractApplicationServiceTest.kt new file mode 100644 index 00000000..a52a29d7 --- /dev/null +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/shared/LocalUserAbstractApplicationServiceTest.kt @@ -0,0 +1,24 @@ +package dev.usbharu.hideout.core.application.shared + +import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous +import dev.usbharu.hideout.core.domain.model.support.principal.FromApi +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.Test +import org.slf4j.LoggerFactory +import utils.TestTransaction + +class LocalUserAbstractApplicationServiceTest { + @Test + fun requireFromAPI() = runTest { + val logger = LoggerFactory.getLogger(javaClass) + val value = object : LocalUserAbstractApplicationService(TestTransaction, logger) { + override suspend fun internalExecute(command: Unit, principal: FromApi) { + + } + } + + org.junit.jupiter.api.assertThrows { + value.execute(Unit, Anonymous) + } + } +} \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/support/domain/DomainTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/support/domain/DomainTest.kt new file mode 100644 index 00000000..861d2857 --- /dev/null +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/model/support/domain/DomainTest.kt @@ -0,0 +1,14 @@ +package dev.usbharu.hideout.core.domain.model.support.domain + +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows + + +class DomainTest { + @Test + fun `1000超過の長さは失敗`() { + assertThrows { + Domain("a".repeat(1001)) + } + } +} \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorDomainServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorDomainServiceImplTest.kt new file mode 100644 index 00000000..a5fcf1a1 --- /dev/null +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorDomainServiceImplTest.kt @@ -0,0 +1,51 @@ +package dev.usbharu.hideout.core.domain.service.actor.local + +import dev.usbharu.hideout.core.config.ApplicationConfig +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository +import dev.usbharu.hideout.core.domain.model.actor.TestActorFactory +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertTrue +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.doReturn +import org.mockito.kotlin.eq +import org.mockito.kotlin.whenever +import java.net.URL + +@ExtendWith(MockitoExtension::class) +class LocalActorDomainServiceImplTest { + @InjectMocks + lateinit var service: LocalActorDomainServiceImpl + + @Mock + lateinit var actorRepository: ActorRepository + + @Spy + val applicationConfig = ApplicationConfig(URL("http://example.com")) + + @Test + fun findByNameAndDomainがnullならfalse() = runTest { + val actual = service.usernameAlreadyUse("test") + + assertFalse(actual) + } + + @Test + fun findByNameAndDomainがnullならtrue() = runTest { + whenever(actorRepository.findByNameAndDomain(eq("test"), eq("example.com"))).doReturn(TestActorFactory.create()) + + val actual = service.usernameAlreadyUse("test") + + assertTrue(actual) + } + + @Test + fun generateKeyPair() = runTest { + service.generateKeyPair() + } +} \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainServiceImplTest.kt index c44382b9..3617fae9 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainServiceImplTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/domain/service/actor/local/LocalActorMigrationCheckDomainServiceImplTest.kt @@ -8,8 +8,29 @@ import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Assertions.assertInstanceOf import org.junit.jupiter.api.Test +import java.time.Instant +import kotlin.time.Duration.Companion.days +import kotlin.time.toJavaDuration class LocalActorMigrationCheckDomainServiceImplTest { + + @Test + fun 最終お引越しから30日以内だと失敗() = runTest { + val from = TestActorFactory.create() + val to = TestActorFactory.create() + val userDetail = UserDetail.create( + UserDetailId(1), + ActorId(1), UserDetailHashedPassword("") + ) + userDetail.lastMigration = Instant.now().minusSeconds(100) + + val localActorMigrationCheckDomainServiceImpl = LocalActorMigrationCheckDomainServiceImpl() + + val canAccountMigration = localActorMigrationCheckDomainServiceImpl.canAccountMigration(userDetail, to, from) + + assertInstanceOf(AccountMigrationCheck.MigrationCoolDown::class.java, canAccountMigration) + } + @Test fun 自分自身に引っ越しできない(): Unit = runTest { @@ -86,4 +107,20 @@ class LocalActorMigrationCheckDomainServiceImplTest { assertInstanceOf(AccountMigrationCheck.CanAccountMigration::class.java, canAccountMigration) } + + @Test + fun お引越し履歴があっても30日以上経っていたら成功する() = runTest { + val from = TestActorFactory.create() + val to = TestActorFactory.create(alsoKnownAs = setOf(from.id, ActorId(100))) + val userDetail = UserDetail.create( + UserDetailId(1), + ActorId(1), UserDetailHashedPassword("") + ) + userDetail.lastMigration = Instant.now().minus(31.days.toJavaDuration()) + val localActorMigrationCheckDomainServiceImpl = LocalActorMigrationCheckDomainServiceImpl() + + val canAccountMigration = localActorMigrationCheckDomainServiceImpl.canAccountMigration(userDetail, from, to) + + assertInstanceOf(AccountMigrationCheck.CanAccountMigration::class.java, canAccountMigration) + } } \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsServiceImplTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsServiceImplTest.kt new file mode 100644 index 00000000..bb253ef6 --- /dev/null +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/oauth2/UserDetailsServiceImplTest.kt @@ -0,0 +1,91 @@ +package dev.usbharu.hideout.core.infrastructure.springframework.oauth2 + +import dev.usbharu.hideout.core.config.ApplicationConfig +import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository +import dev.usbharu.hideout.core.domain.model.actor.TestActorFactory +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailHashedPassword +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +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 org.springframework.security.core.userdetails.UsernameNotFoundException +import utils.TestTransaction +import java.net.URL +import kotlin.test.assertEquals + +@ExtendWith(MockitoExtension::class) +class UserDetailsServiceImplTest { + @InjectMocks + lateinit var service: UserDetailsServiceImpl + + @Mock + lateinit var actorRepository: ActorRepository + + @Mock + lateinit var userDetailRepository: UserDetailRepository + + @Spy + val applicationConfig = ApplicationConfig(URL("http://example.com")) + + @Spy + val transaction = TestTransaction + + @Test + fun usernameがnullなら失敗() = runTest { + assertThrows { + service.loadUserByUsername(null) + } + verify(actorRepository, never()).findByNameAndDomain(any(), any()) + } + + @Test + fun actorが見つからない場合失敗() = runTest { + assertThrows { + service.loadUserByUsername("test") + } + verify(actorRepository, times(1)).findByNameAndDomain(eq("test"), eq("example.com")) + } + + @Test + fun userDetailが見つからない場合失敗() = runTest { + whenever(actorRepository.findByNameAndDomain(eq("test"), eq("example.com"))).doReturn( + TestActorFactory.create( + actorName = "test", id = 1 + ) + ) + assertThrows { + service.loadUserByUsername("test") + } + verify(actorRepository, times(1)).findByNameAndDomain(eq("test"), eq("example.com")) + verify(userDetailRepository, times(1)).findByActorId(eq(1)) + } + + @Test + fun 全部見つかったら成功() = runTest { + whenever( + actorRepository.findByNameAndDomain( + eq("test"), + eq("example.com") + ) + ).doReturn(TestActorFactory.create(id = 1)) + whenever(userDetailRepository.findByActorId(eq(1))).doReturn( + UserDetail.create( + UserDetailId(1), + ActorId(1), UserDetailHashedPassword("") + ) + ) + + val actual = service.loadUserByUsername("test") + + assertEquals(HideoutUserDetails(HashSet(), "", "test-1", 1), actual) + } +} \ No newline at end of file From 7db82462fa131a885270f79c68fdb43117164bb6 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Fri, 9 Aug 2024 10:00:40 +0900 Subject: [PATCH 8/9] wip --- .../RegisterLocalPostApplicationService.kt | 10 +--- ...RegisterLocalPostApplicationServiceTest.kt | 57 +++++++++++++++++++ .../UpdateLocalNoteApplicationServiceTest.kt | 43 ++++++++++++++ 3 files changed, 103 insertions(+), 7 deletions(-) create mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationServiceTest.kt diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationService.kt index d0658179..0cc237da 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationService.kt @@ -16,6 +16,7 @@ package dev.usbharu.hideout.core.application.post +import dev.usbharu.hideout.core.application.exception.InternalServerException import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.domain.model.actor.ActorRepository @@ -24,7 +25,6 @@ import dev.usbharu.hideout.core.domain.model.post.PostId import dev.usbharu.hideout.core.domain.model.post.PostOverview import dev.usbharu.hideout.core.domain.model.post.PostRepository import dev.usbharu.hideout.core.domain.model.support.principal.FromApi -import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository import dev.usbharu.hideout.core.infrastructure.factory.PostFactoryImpl import org.slf4j.Logger import org.slf4j.LoggerFactory @@ -35,17 +35,13 @@ class RegisterLocalPostApplicationService( private val postFactory: PostFactoryImpl, private val actorRepository: ActorRepository, private val postRepository: PostRepository, - private val userDetailRepository: UserDetailRepository, transaction: Transaction, ) : LocalUserAbstractApplicationService(transaction, Companion.logger) { override suspend fun internalExecute(command: RegisterLocalPost, principal: FromApi): Long { - val actorId = ( - userDetailRepository.findById(principal.userDetailId) - ?: throw IllegalStateException("actor not found") - ).actorId + val actorId = principal.actorId - val actor = actorRepository.findById(actorId)!! + val actor = actorRepository.findById(actorId) ?: throw InternalServerException("Actor $actorId not found.") val post = postFactory.createLocal( actor = actor, diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationServiceTest.kt new file mode 100644 index 00000000..9cd8cb4e --- /dev/null +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationServiceTest.kt @@ -0,0 +1,57 @@ +package dev.usbharu.hideout.core.application.post + +import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository +import dev.usbharu.hideout.core.domain.model.actor.TestActorFactory +import dev.usbharu.hideout.core.domain.model.post.PostRepository +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.principal.FromApi +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId +import dev.usbharu.hideout.core.infrastructure.factory.PostFactoryImpl +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.doReturn +import org.mockito.kotlin.eq +import org.mockito.kotlin.whenever +import utils.TestTransaction + +@ExtendWith(MockitoExtension::class) +class RegisterLocalPostApplicationServiceTest { + @InjectMocks + lateinit var service: RegisterLocalPostApplicationService + + @Mock + lateinit var actorRepository: ActorRepository + + @Mock + lateinit var postRepository: PostRepository + + @Mock + lateinit var postFactoryImpl: PostFactoryImpl + + @Spy + val transaction = TestTransaction + + @Test + fun postを作成できる() = runTest { + val actor = TestActorFactory.create(id = 1) + whenever(actorRepository.findById(ActorId(1))) doReturn actor + whenever( + postFactoryImpl.createLocal( + eq(actor), + ) + ) + + service.execute( + RegisterLocalPost("content test", null, Visibility.PUBLIC, null, false, emptyList<>()), FromApi( + ActorId(1), UserDetailId(1), Acct("test", "example.com") + ) + ) + } +} \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/post/UpdateLocalNoteApplicationServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/post/UpdateLocalNoteApplicationServiceTest.kt index e5ad0714..25c8524b 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/post/UpdateLocalNoteApplicationServiceTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/post/UpdateLocalNoteApplicationServiceTest.kt @@ -1,5 +1,6 @@ package dev.usbharu.hideout.core.application.post +import dev.usbharu.hideout.core.application.exception.InternalServerException import dev.usbharu.hideout.core.application.exception.PermissionDeniedException import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.actor.ActorRepository @@ -105,5 +106,47 @@ class UpdateLocalNoteApplicationServiceTest { } } + @Test + fun userDetailが見つからない場合失敗() = runTest { + whenever(postRepository.findById(PostId(1))).doReturn(TestPostFactory.create(id = 1, actorId = 1)) + assertThrows { + service.execute( + UpdateLocalNote(1, null, "test", false, emptyList()), FromApi( + ActorId(1), + UserDetailId(1), Acct("test", "example.com") + ) + ) + } + + verify(userDetailRepository, times(1)).findById(UserDetailId(1)) + verify(actorRepository, never()).findById(any()) + } + + @Test + fun actorが見つからない場合失敗() = runTest { + val post = TestPostFactory.create() + + whenever(postRepository.findById(post.id)).doReturn(post) + whenever(userDetailRepository.findById(UserDetailId(1))).doReturn( + UserDetail.create( + UserDetailId(1), post.actorId, + UserDetailHashedPassword("") + ) + ) + + + assertThrows { + service.execute( + UpdateLocalNote(post.id.id, null, "test", false, emptyList()), FromApi( + post.actorId, + UserDetailId(1), + Acct("test", "example.com") + ) + ) + } + verify(userDetailRepository, times(1)).findById(UserDetailId(1)) + verify(actorRepository, times(1)).findById(ActorId(1)) + verify(postRepository, never()).save(any()) + } } \ No newline at end of file From 3ab86fc5112f93d5239b3bf5c843440d09b06375 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Fri, 9 Aug 2024 23:06:43 +0900 Subject: [PATCH 9/9] =?UTF-8?q?=E3=83=86=E3=82=B9=E3=83=88=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RegisterApplicationApplicationService.kt | 97 ++++++------ .../UserDeleteFilterApplicationService.kt | 15 +- .../filter/UserGetFilterApplicationService.kt | 16 +- ...erAcceptFollowRequestApplicationService.kt | 9 +- .../core/domain/model/filter/Filter.kt | 14 ++ ...gisterApplicationApplicationServiceTest.kt | 84 ++++++++++ .../UserDeleteFilterApplicationServiceTest.kt | 89 +++++++++++ .../UserGetFilterApplicationServiceTest.kt | 86 +++++++++++ ...RegisterLocalPostApplicationServiceTest.kt | 33 +++- ...ceptFollowRequestApplicationServiceTest.kt | 81 ++++++++++ .../timeline/DefaultTimelineStoreTest.kt | 146 ++++++++++++++++++ .../mastodon/interfaces/api/SpringAppApi.kt | 3 +- 12 files changed, 606 insertions(+), 67 deletions(-) create mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/application/RegisterApplicationApplicationServiceTest.kt create mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/filter/UserDeleteFilterApplicationServiceTest.kt create mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/filter/UserGetFilterApplicationServiceTest.kt create mode 100644 hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/relationship/acceptfollowrequest/UserAcceptFollowRequestApplicationServiceTest.kt diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/application/RegisterApplicationApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/application/RegisterApplicationApplicationService.kt index 104cd3e0..10890e85 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/application/RegisterApplicationApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/application/RegisterApplicationApplicationService.kt @@ -16,14 +16,17 @@ package dev.usbharu.hideout.core.application.application +import dev.usbharu.hideout.core.application.shared.AbstractApplicationService import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.domain.model.application.Application import dev.usbharu.hideout.core.domain.model.application.ApplicationId import dev.usbharu.hideout.core.domain.model.application.ApplicationName import dev.usbharu.hideout.core.domain.model.application.ApplicationRepository +import dev.usbharu.hideout.core.domain.model.support.principal.Principal import dev.usbharu.hideout.core.domain.service.userdetail.PasswordEncoder import dev.usbharu.hideout.core.domain.shared.id.IdGenerateService import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.SecureTokenGenerator +import org.slf4j.LoggerFactory import org.springframework.security.oauth2.core.AuthorizationGrantType import org.springframework.security.oauth2.core.ClientAuthenticationMethod import org.springframework.security.oauth2.server.authorization.client.RegisteredClient @@ -39,53 +42,57 @@ class RegisterApplicationApplicationService( private val passwordEncoder: PasswordEncoder, private val secureTokenGenerator: SecureTokenGenerator, private val registeredClientRepository: RegisteredClientRepository, - private val transaction: Transaction, + transaction: Transaction, private val applicationRepository: ApplicationRepository, -) { - suspend fun register(registerApplication: RegisterApplication): RegisteredApplication { - return transaction.transaction { - val id = idGenerateService.generateId() - val clientSecret = secureTokenGenerator.generate() - val registeredClient = RegisteredClient - .withId(id.toString()) - .clientId(id.toString()) - .clientSecret(passwordEncoder.encode(clientSecret)) - .clientName(registerApplication.name) - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST) - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT) - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) - .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) - .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS) - .apply { - if (registerApplication.useRefreshToken) { - authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN) - } else { - tokenSettings( - TokenSettings - .builder() - .accessTokenTimeToLive(Duration.ofSeconds(31536000000)) - .build() - ) - } - } - .redirectUris { set -> - set.addAll(registerApplication.redirectUris.map { it.toString() }) - } - .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()) - .scopes { it.addAll(registerApplication.scopes) } - .build() - registeredClientRepository.save(registeredClient) +) : AbstractApplicationService(transaction, logger) { - val application = Application(ApplicationId(id), ApplicationName(registerApplication.name)) + override suspend fun internalExecute(command: RegisterApplication, principal: Principal): RegisteredApplication { + val id = idGenerateService.generateId() + val clientSecret = secureTokenGenerator.generate() + val registeredClient = RegisteredClient + .withId(id.toString()) + .clientId(id.toString()) + .clientSecret(passwordEncoder.encode(clientSecret)) + .clientName(command.name) + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST) + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT) + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) + .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) + .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS) + .apply { + if (command.useRefreshToken) { + authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN) + } else { + tokenSettings( + TokenSettings + .builder() + .accessTokenTimeToLive(Duration.ofSeconds(31536000000)) + .build() + ) + } + } + .redirectUris { set -> + set.addAll(command.redirectUris.map { it.toString() }) + } + .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()) + .scopes { it.addAll(command.scopes) } + .build() + registeredClientRepository.save(registeredClient) - applicationRepository.save(application) - RegisteredApplication( - id = id, - name = registerApplication.name, - clientSecret = clientSecret, - clientId = id.toString(), - redirectUris = registerApplication.redirectUris - ) - } + val application = Application(ApplicationId(id), ApplicationName(command.name)) + + applicationRepository.save(application) + return RegisteredApplication( + id = id, + name = command.name, + clientSecret = clientSecret, + clientId = id.toString(), + redirectUris = command.redirectUris + ) + } + + + companion object { + private val logger = LoggerFactory.getLogger(RegisterApplicationApplicationService::class.java) } } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserDeleteFilterApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserDeleteFilterApplicationService.kt index 1d6dbb28..9333ec83 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserDeleteFilterApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserDeleteFilterApplicationService.kt @@ -16,22 +16,27 @@ package dev.usbharu.hideout.core.application.filter -import dev.usbharu.hideout.core.application.shared.AbstractApplicationService +import dev.usbharu.hideout.core.application.exception.PermissionDeniedException +import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.domain.model.filter.FilterId import dev.usbharu.hideout.core.domain.model.filter.FilterRepository -import dev.usbharu.hideout.core.domain.model.support.principal.Principal +import dev.usbharu.hideout.core.domain.model.support.principal.FromApi import org.slf4j.LoggerFactory import org.springframework.stereotype.Service @Service class UserDeleteFilterApplicationService(private val filterRepository: FilterRepository, transaction: Transaction) : - AbstractApplicationService( + LocalUserAbstractApplicationService( transaction, logger ) { - override suspend fun internalExecute(command: DeleteFilter, principal: Principal) { - val filter = filterRepository.findByFilterId(FilterId(command.filterId)) ?: throw Exception("not found") + override suspend fun internalExecute(command: DeleteFilter, principal: FromApi) { + val filter = + filterRepository.findByFilterId(FilterId(command.filterId)) ?: throw IllegalArgumentException("not found") + if (filter.userDetailId != principal.userDetailId) { + throw PermissionDeniedException() + } filterRepository.delete(filter) } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserGetFilterApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserGetFilterApplicationService.kt index fc7ea255..cecb5a5d 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserGetFilterApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/filter/UserGetFilterApplicationService.kt @@ -16,23 +16,27 @@ package dev.usbharu.hideout.core.application.filter -import dev.usbharu.hideout.core.application.shared.AbstractApplicationService +import dev.usbharu.hideout.core.application.exception.PermissionDeniedException +import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService import dev.usbharu.hideout.core.application.shared.Transaction import dev.usbharu.hideout.core.domain.model.filter.FilterId import dev.usbharu.hideout.core.domain.model.filter.FilterRepository -import dev.usbharu.hideout.core.domain.model.support.principal.Principal +import dev.usbharu.hideout.core.domain.model.support.principal.FromApi import org.slf4j.LoggerFactory import org.springframework.stereotype.Service @Service class UserGetFilterApplicationService(private val filterRepository: FilterRepository, transaction: Transaction) : - AbstractApplicationService( + LocalUserAbstractApplicationService( transaction, logger ) { - override suspend fun internalExecute(command: GetFilter, principal: Principal): Filter { - val filter = filterRepository.findByFilterId(FilterId(command.filterId)) ?: throw Exception("Not Found") - + override suspend fun internalExecute(command: GetFilter, principal: FromApi): Filter { + val filter = + filterRepository.findByFilterId(FilterId(command.filterId)) ?: throw IllegalArgumentException("Not Found") + if (filter.userDetailId != principal.userDetailId) { + throw PermissionDeniedException() + } return Filter.of(filter) } diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/acceptfollowrequest/UserAcceptFollowRequestApplicationService.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/acceptfollowrequest/UserAcceptFollowRequestApplicationService.kt index a80497e6..1f17a0df 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/acceptfollowrequest/UserAcceptFollowRequestApplicationService.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/application/relationship/acceptfollowrequest/UserAcceptFollowRequestApplicationService.kt @@ -16,6 +16,7 @@ package dev.usbharu.hideout.core.application.relationship.acceptfollowrequest +import dev.usbharu.hideout.core.application.exception.InternalServerException import dev.usbharu.hideout.core.application.relationship.block.UserBlockApplicationService import dev.usbharu.hideout.core.application.shared.LocalUserAbstractApplicationService import dev.usbharu.hideout.core.application.shared.Transaction @@ -23,7 +24,6 @@ import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository import dev.usbharu.hideout.core.domain.model.support.principal.FromApi -import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository import org.slf4j.LoggerFactory import org.springframework.stereotype.Service @@ -32,18 +32,17 @@ class UserAcceptFollowRequestApplicationService( private val relationshipRepository: RelationshipRepository, transaction: Transaction, private val actorRepository: ActorRepository, - private val userDetailRepository: UserDetailRepository, ) : LocalUserAbstractApplicationService(transaction, logger) { override suspend fun internalExecute(command: AcceptFollowRequest, principal: FromApi) { - val userDetail = userDetailRepository.findById(principal.userDetailId)!! - val actor = actorRepository.findById(userDetail.actorId)!! + val actor = actorRepository.findById(principal.actorId) + ?: throw InternalServerException("Actor ${principal.actorId} not found") val targetId = ActorId(command.sourceActorId) val relationship = relationshipRepository.findByActorIdAndTargetId(targetId, actor.id) - ?: throw Exception("Follow request not found") + ?: throw InternalServerException("Follow request not found") relationship.acceptFollowRequest() diff --git a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/Filter.kt b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/Filter.kt index dc26a715..9a41fa9e 100644 --- a/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/Filter.kt +++ b/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/domain/model/filter/Filter.kt @@ -56,6 +56,20 @@ class Filter( ) } + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as Filter + + return id == other.id + } + + override fun hashCode(): Int { + return id.hashCode() + } + + companion object { fun isAllow(user: UserDetail, action: Action, resource: Filter): Boolean { return when (action) { diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/application/RegisterApplicationApplicationServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/application/RegisterApplicationApplicationServiceTest.kt new file mode 100644 index 00000000..e1f73b83 --- /dev/null +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/application/RegisterApplicationApplicationServiceTest.kt @@ -0,0 +1,84 @@ +package dev.usbharu.hideout.core.application.application + +import dev.usbharu.hideout.core.domain.model.application.ApplicationRepository +import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous +import dev.usbharu.hideout.core.infrastructure.other.TwitterSnowflakeIdGenerateService +import dev.usbharu.hideout.core.infrastructure.springframework.SpringSecurityPasswordEncoder +import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.SecureTokenGenerator +import kotlinx.coroutines.test.runTest +import org.assertj.core.api.Assertions.assertThat +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.argumentCaptor +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder +import org.springframework.security.oauth2.core.AuthorizationGrantType +import org.springframework.security.oauth2.server.authorization.client.RegisteredClient +import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository +import utils.TestTransaction +import java.net.URI +import java.time.Duration + +@ExtendWith(MockitoExtension::class) +class RegisterApplicationApplicationServiceTest { + @InjectMocks + lateinit var service: RegisterApplicationApplicationService + + @Mock + lateinit var secureTokenGenerator: SecureTokenGenerator + + @Mock + lateinit var registeredClientRepository: RegisteredClientRepository + + @Mock + lateinit var applicationRepository: ApplicationRepository + + @Spy + val idGenerateService = TwitterSnowflakeIdGenerateService + + @Spy + val passwordEncoder = SpringSecurityPasswordEncoder(BCryptPasswordEncoder()) + + @Spy + val transaction = TestTransaction + + @Test + fun applicationを作成できる() = runTest { + whenever(secureTokenGenerator.generate()).doReturn("very-secure-token") + + service.execute( + RegisterApplication("test", setOf(URI.create("https://example.com")), false, setOf("write")), + Anonymous + ) + + argumentCaptor { + verify(registeredClientRepository).save(capture()) + val first = allValues.first() + assertThat(first.tokenSettings.accessTokenTimeToLive).isGreaterThanOrEqualTo(Duration.ofSeconds(31536000000)) + + } + } + + @Test + fun refreshTokenを有効化してapplicationを作成できる() = runTest { + whenever(secureTokenGenerator.generate()).doReturn("very-secure-token") + + service.execute( + RegisterApplication("test", setOf(URI.create("https://example.com")), true, setOf("write")), + Anonymous + ) + + argumentCaptor { + verify(registeredClientRepository).save(capture()) + val first = allValues.first() + assertThat(first.authorizationGrantTypes).contains(AuthorizationGrantType.REFRESH_TOKEN) + + } + } +} \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/filter/UserDeleteFilterApplicationServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/filter/UserDeleteFilterApplicationServiceTest.kt new file mode 100644 index 00000000..3928ea45 --- /dev/null +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/filter/UserDeleteFilterApplicationServiceTest.kt @@ -0,0 +1,89 @@ +package dev.usbharu.hideout.core.application.filter + +import dev.usbharu.hideout.core.application.exception.PermissionDeniedException +import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.model.filter.* +import dev.usbharu.hideout.core.domain.model.filter.Filter +import dev.usbharu.hideout.core.domain.model.filter.FilterKeyword +import dev.usbharu.hideout.core.domain.model.support.acct.Acct +import dev.usbharu.hideout.core.domain.model.support.principal.FromApi +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +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.TestTransaction + +@ExtendWith(MockitoExtension::class) +class UserDeleteFilterApplicationServiceTest { + + @InjectMocks + lateinit var service: UserDeleteFilterApplicationService + + @Spy + val transaction = TestTransaction + + @Mock + lateinit var filterRepository: FilterRepository + + @Test + fun フィルターを削除できる() = runTest { + val filter = Filter( + FilterId(1), UserDetailId(1), FilterName("filter"), setOf(FilterContext.HOME), FilterAction.HIDE, setOf( + FilterKeyword( + FilterKeywordId(1), FilterKeywordKeyword("aaa"), FilterMode.NONE + ) + ) + ) + whenever(filterRepository.findByFilterId(FilterId(1))).doReturn(filter) + + service.execute( + DeleteFilter(1), FromApi( + ActorId(1), UserDetailId(1), + Acct("test", "example.com") + ) + ) + + verify(filterRepository, times(1)).delete(eq(filter)) + } + + @Test + fun フィルターが見つからない場合失敗() = runTest { + assertThrows { + service.execute( + DeleteFilter(1), FromApi( + ActorId(1), UserDetailId(1), + Acct("test", "example.com") + ) + ) + } + } + + @Test + fun フィルターのオーナー以外は失敗() = runTest { + val filter = Filter( + FilterId(1), UserDetailId(1), FilterName("filter"), setOf(FilterContext.HOME), FilterAction.HIDE, setOf( + FilterKeyword( + FilterKeywordId(1), FilterKeywordKeyword("aaa"), FilterMode.NONE + ) + ) + ) + whenever(filterRepository.findByFilterId(FilterId(1))).doReturn(filter) + + assertThrows { + service.execute( + DeleteFilter(1), FromApi( + ActorId(3), UserDetailId(3), + Acct("test", "example.com") + ) + ) + } + + verify(filterRepository, never()).delete(any()) + } +} \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/filter/UserGetFilterApplicationServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/filter/UserGetFilterApplicationServiceTest.kt new file mode 100644 index 00000000..b3b2db82 --- /dev/null +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/filter/UserGetFilterApplicationServiceTest.kt @@ -0,0 +1,86 @@ +package dev.usbharu.hideout.core.application.filter + +import dev.usbharu.hideout.core.application.exception.PermissionDeniedException +import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.model.filter.* +import dev.usbharu.hideout.core.domain.model.filter.Filter +import dev.usbharu.hideout.core.domain.model.filter.FilterKeyword +import dev.usbharu.hideout.core.domain.model.support.acct.Acct +import dev.usbharu.hideout.core.domain.model.support.principal.FromApi +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +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.doReturn +import org.mockito.kotlin.whenever +import utils.TestTransaction + +@ExtendWith(MockitoExtension::class) +class UserGetFilterApplicationServiceTest { + @InjectMocks + lateinit var service: UserGetFilterApplicationService + + @Mock + lateinit var filterRepository: FilterRepository + + @Spy + val transaction = TestTransaction + + @Test + fun オーナーのみ取得できる() = runTest { + val filter = Filter( + FilterId(1), UserDetailId(1), FilterName("filter"), setOf(FilterContext.HOME), FilterAction.HIDE, setOf( + FilterKeyword( + FilterKeywordId(1), FilterKeywordKeyword("aaa"), FilterMode.NONE + ) + ) + ) + whenever(filterRepository.findByFilterId(FilterId(1))).doReturn(filter) + + service.execute( + GetFilter(1), FromApi( + ActorId(1), UserDetailId(1), + Acct("test", "example.com") + ) + ) + } + + @Test + fun オーナー以外は失敗() = runTest { + val filter = Filter( + FilterId(1), UserDetailId(1), FilterName("filter"), setOf(FilterContext.HOME), FilterAction.HIDE, setOf( + FilterKeyword( + FilterKeywordId(1), FilterKeywordKeyword("aaa"), FilterMode.NONE + ) + ) + ) + whenever(filterRepository.findByFilterId(FilterId(1))).doReturn(filter) + + + assertThrows { + service.execute( + GetFilter(1), FromApi( + ActorId(3), UserDetailId(3), + Acct("test", "example.com") + ) + ) + } + } + + @Test + fun フィルターが見つからない場合失敗() = runTest { + assertThrows { + service.execute( + GetFilter(1), FromApi( + ActorId(3), UserDetailId(3), + Acct("test", "example.com") + ) + ) + } + } +} \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationServiceTest.kt index 9cd8cb4e..40cc7162 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationServiceTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/post/RegisterLocalPostApplicationServiceTest.kt @@ -1,9 +1,11 @@ package dev.usbharu.hideout.core.application.post +import dev.usbharu.hideout.core.application.exception.InternalServerException import dev.usbharu.hideout.core.domain.model.actor.ActorId import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.actor.TestActorFactory import dev.usbharu.hideout.core.domain.model.post.PostRepository +import dev.usbharu.hideout.core.domain.model.post.TestPostFactory 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.principal.FromApi @@ -11,14 +13,13 @@ import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId import dev.usbharu.hideout.core.infrastructure.factory.PostFactoryImpl import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows 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.doReturn -import org.mockito.kotlin.eq -import org.mockito.kotlin.whenever +import org.mockito.kotlin.* import utils.TestTransaction @ExtendWith(MockitoExtension::class) @@ -41,17 +42,39 @@ class RegisterLocalPostApplicationServiceTest { @Test fun postを作成できる() = runTest { val actor = TestActorFactory.create(id = 1) + val post = TestPostFactory.create() whenever(actorRepository.findById(ActorId(1))) doReturn actor whenever( postFactoryImpl.createLocal( eq(actor), + anyValueClass(), + isNull(), + eq("content test"), + eq(Visibility.PUBLIC), + isNull(), + isNull(), + eq(false), + eq(emptyList()) ) - ) + ).doReturn(post) service.execute( - RegisterLocalPost("content test", null, Visibility.PUBLIC, null, false, emptyList<>()), FromApi( + RegisterLocalPost("content test", null, Visibility.PUBLIC, null, null, false, emptyList()), FromApi( ActorId(1), UserDetailId(1), Acct("test", "example.com") ) ) + + verify(postRepository, times(1)).save(eq(post)) + } + + @Test + fun actorが見つからないと失敗() = runTest { + assertThrows { + service.execute( + RegisterLocalPost("content test", null, Visibility.PUBLIC, null, null, false, emptyList()), FromApi( + ActorId(1), UserDetailId(1), Acct("test", "example.com") + ) + ) + } } } \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/relationship/acceptfollowrequest/UserAcceptFollowRequestApplicationServiceTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/relationship/acceptfollowrequest/UserAcceptFollowRequestApplicationServiceTest.kt new file mode 100644 index 00000000..8f47c41f --- /dev/null +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/application/relationship/acceptfollowrequest/UserAcceptFollowRequestApplicationServiceTest.kt @@ -0,0 +1,81 @@ +package dev.usbharu.hideout.core.application.relationship.acceptfollowrequest + +import dev.usbharu.hideout.core.application.exception.InternalServerException +import dev.usbharu.hideout.core.domain.model.actor.ActorId +import dev.usbharu.hideout.core.domain.model.actor.ActorRepository +import dev.usbharu.hideout.core.domain.model.actor.TestActorFactory +import dev.usbharu.hideout.core.domain.model.relationship.Relationship +import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository +import dev.usbharu.hideout.core.domain.model.support.acct.Acct +import dev.usbharu.hideout.core.domain.model.support.principal.FromApi +import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailId +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +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.argumentCaptor +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever +import utils.TestTransaction + +@ExtendWith(MockitoExtension::class) +class UserAcceptFollowRequestApplicationServiceTest { + @InjectMocks + lateinit var service: UserAcceptFollowRequestApplicationService + + @Mock + lateinit var relationshipRepository: RelationshipRepository + + @Mock + lateinit var actorRepository: ActorRepository + + @Spy + val transaction = TestTransaction + + @Test + fun actorが見つからない場合失敗() = runTest { + assertThrows { + service.execute(AcceptFollowRequest(1), FromApi(ActorId(2), UserDetailId(2), Acct("test", "example.com"))) + } + } + + @Test + fun relationshipが見つからない場合失敗() = runTest { + whenever(actorRepository.findById(ActorId(2))).doReturn(TestActorFactory.create(id = 2)) + + assertThrows { + service.execute(AcceptFollowRequest(1), FromApi(ActorId(2), UserDetailId(2), Acct("test", "example.com"))) + } + } + + @Test + fun フォローリクエストを承認できる() = runTest { + whenever(actorRepository.findById(ActorId(2))).doReturn(TestActorFactory.create(id = 2)) + whenever(relationshipRepository.findByActorIdAndTargetId(ActorId(1), ActorId(2))).doReturn( + Relationship( + actorId = ActorId(1), targetActorId = ActorId + (2), + following = false, + blocking = false, + muting = false, + followRequesting = true, mutingFollowRequest = false + ) + ) + service.execute(AcceptFollowRequest(1), FromApi(ActorId(2), UserDetailId(2), Acct("test", "example.com"))) + + argumentCaptor { + verify(relationshipRepository).save(capture()) + val first = allValues.first() + + assertFalse(first.followRequesting) + assertTrue(first.following) + } + } +} \ No newline at end of file diff --git a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/DefaultTimelineStoreTest.kt b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/DefaultTimelineStoreTest.kt index 66eeb1a0..ca243c63 100644 --- a/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/DefaultTimelineStoreTest.kt +++ b/hideout-core/src/test/kotlin/dev/usbharu/hideout/core/infrastructure/timeline/DefaultTimelineStoreTest.kt @@ -345,4 +345,150 @@ class DefaultTimelineStoreTest { } } } + + @Test + fun repostがあるときはRepostを含めたTimelineObjectが作成される() = runTest { + val repost = TestPostFactory.create() + val post = TestPostFactory.create(repostId = repost.id.id) + + whenever(postRepository.findById(repost.id)).doReturn(repost) + whenever(timelineRelationshipRepository.findByActorId(post.actorId)).doReturn( + listOf( + TimelineRelationship( + TimelineRelationshipId(1), + TimelineId(12), + post.actorId, + Visible.PUBLIC + ) + ) + ) + + whenever(timelineRepository.findByIds(listOf(TimelineId(12)))).doReturn( + listOf( + Timeline( + id = TimelineId(12), + userDetailId = UserDetailId(post.actorId.id), + name = TimelineName("timeline"), + visibility = TimelineVisibility.PUBLIC, + isSystem = false + ) + ) + ) + + val filters = listOf( + Filter( + id = FilterId(13), + userDetailId = UserDetailId(post.actorId.id), + name = FilterName("filter"), + filterContext = setOf(FilterContext.HOME), + filterAction = FilterAction.HIDE, + filterKeywords = setOf( + FilterKeyword(FilterKeywordId(14), FilterKeywordKeyword("aa"), FilterMode.NONE) + ) + ) + ) + + whenever(filterRepository.findByUserDetailId(UserDetailId(post.actorId.id))).doReturn(filters) + + whenever(filterDomainService.apply(post, FilterContext.HOME, filters)).doReturn( + FilteredPost( + post, listOf( + FilterResult(filters.first(), "aaa") + ) + ) + ) + + timelineStore.addPost(post) + + argumentCaptor> { + verify(internalTimelineObjectRepository, times(1)).saveAll(capture()) + val timelineObjectList = allValues.first() + + assertThat(timelineObjectList).allSatisfy { + assertThat(it.postId).isEqualTo(post.id) + assertThat(it.postActorId).isEqualTo(post.actorId) + assertThat(it.replyId).isNull() + assertThat(it.replyActorId).isNull() + assertThat(it.repostId).isEqualTo(repost.id) + assertThat(it.repostActorId).isEqualTo(repost.actorId) + + assertThat(it.userDetailId).isEqualTo(UserDetailId(post.actorId.id)) + assertThat(it.timelineId).isEqualTo(TimelineId(12)) + assertThat(it.warnFilters).contains(TimelineObjectWarnFilter(FilterId(13), "aaa")) + } + } + } + + @Test + fun replyがあるときはReplyを含めたTimeineObjectが作成される() = runTest { + val reply = TestPostFactory.create() + val post = TestPostFactory.create(replyId = reply.id.id) + + whenever(postRepository.findById(reply.id)).doReturn(reply) + whenever(timelineRelationshipRepository.findByActorId(post.actorId)).doReturn( + listOf( + TimelineRelationship( + TimelineRelationshipId(1), + TimelineId(12), + post.actorId, + Visible.PUBLIC + ) + ) + ) + + whenever(timelineRepository.findByIds(listOf(TimelineId(12)))).doReturn( + listOf( + Timeline( + id = TimelineId(12), + userDetailId = UserDetailId(post.actorId.id), + name = TimelineName("timeline"), + visibility = TimelineVisibility.PUBLIC, + isSystem = false + ) + ) + ) + + val filters = listOf( + Filter( + id = FilterId(13), + userDetailId = UserDetailId(post.actorId.id), + name = FilterName("filter"), + filterContext = setOf(FilterContext.HOME), + filterAction = FilterAction.HIDE, + filterKeywords = setOf( + FilterKeyword(FilterKeywordId(14), FilterKeywordKeyword("aa"), FilterMode.NONE) + ) + ) + ) + + whenever(filterRepository.findByUserDetailId(UserDetailId(post.actorId.id))).doReturn(filters) + + whenever(filterDomainService.apply(post, FilterContext.HOME, filters)).doReturn( + FilteredPost( + post, listOf( + FilterResult(filters.first(), "aaa") + ) + ) + ) + + timelineStore.addPost(post) + + argumentCaptor> { + verify(internalTimelineObjectRepository, times(1)).saveAll(capture()) + val timelineObjectList = allValues.first() + + assertThat(timelineObjectList).allSatisfy { + assertThat(it.postId).isEqualTo(post.id) + assertThat(it.postActorId).isEqualTo(post.actorId) + assertThat(it.repostId).isNull() + assertThat(it.repostActorId).isNull() + assertThat(it.replyId).isEqualTo(reply.id) + assertThat(it.replyActorId).isEqualTo(reply.actorId) + + assertThat(it.userDetailId).isEqualTo(UserDetailId(post.actorId.id)) + assertThat(it.timelineId).isEqualTo(TimelineId(12)) + assertThat(it.warnFilters).contains(TimelineObjectWarnFilter(FilterId(13), "aaa")) + } + } + } } \ No newline at end of file diff --git a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAppApi.kt b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAppApi.kt index cc870055..af17a724 100644 --- a/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAppApi.kt +++ b/hideout-mastodon/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/SpringAppApi.kt @@ -18,6 +18,7 @@ package dev.usbharu.hideout.mastodon.interfaces.api import dev.usbharu.hideout.core.application.application.RegisterApplication import dev.usbharu.hideout.core.application.application.RegisterApplicationApplicationService +import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous import dev.usbharu.hideout.mastodon.interfaces.api.generated.AppApi import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.Application import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.AppsRequest @@ -35,7 +36,7 @@ class SpringAppApi(private val registerApplicationApplicationService: RegisterAp false, appsRequest.scopes?.split(" ").orEmpty().toSet().ifEmpty { setOf("read") } ) - val registeredApplication = registerApplicationApplicationService.register(registerApplication) + val registeredApplication = registerApplicationApplicationService.execute(registerApplication, Anonymous) return ResponseEntity.ok( Application( registeredApplication.name,