Merge pull request #516 from usbharu/auth2

認可処理とテストを追加
This commit is contained in:
usbharu 2024-08-10 00:34:30 +09:00 committed by GitHub
commit e9f263bab1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
90 changed files with 2668 additions and 512 deletions

View File

@ -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)

View File

@ -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)
}
}
}

View File

@ -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<GetUserDetail, UserDetail>(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 })

View File

@ -0,0 +1,3 @@
package dev.usbharu.hideout.core.application.actor
data class MigrationLocalActor(val from: Long, val to: Long)

View File

@ -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<Unit> {
val fromActorId = ActorId(from)
val toActorId = ActorId(to)
transaction: Transaction,
private val userDetailRepository: UserDetailRepository,
) : LocalUserAbstractApplicationService<MigrationLocalActor, Unit>(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)
}
}

View File

@ -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
@ -48,10 +49,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,
@ -72,6 +73,6 @@ class RegisterLocalActorApplicationService(
}
companion object {
val logger = LoggerFactory.getLogger(RegisterLocalActorApplicationService::class.java)
private val logger = LoggerFactory.getLogger(RegisterLocalActorApplicationService::class.java)
}
}

View File

@ -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<DeleteLocalActor, Unit>(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)
}
}

View File

@ -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
}
}
}

View File

@ -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<RegisterApplication, RegisteredApplication>(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)
}
}

View File

@ -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
)
}

View File

@ -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
)
}

View File

@ -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<FilterContext>,
val filterAction: FilterAction,
val filterKeywords: Set<RegisterFilterKeyword>,
val userDetailId: UserDetailId
)

View File

@ -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<DeleteFilter, Unit>(
LocalUserAbstractApplicationService<DeleteFilter, Unit>(
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)
}

View File

@ -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<GetFilter, Filter>(
LocalUserAbstractApplicationService<GetFilter, Filter>(
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)
}

View File

@ -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<RegisterFilter, Filter>(
LocalUserAbstractApplicationService<RegisterFilter, Filter>(
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,

View File

@ -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<UploadMedia, Media>(
) : LocalUserAbstractApplicationService<UploadMedia, Media>(
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) {

View File

@ -0,0 +1,3 @@
package dev.usbharu.hideout.core.application.post
data class DeleteLocalPost(val postId: Long)

View File

@ -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<DeleteLocalPost, Unit>(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)
}
}

View File

@ -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<GetPost, Post>(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)
}

View File

@ -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,

View File

@ -16,16 +16,15 @@
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.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.userdetails.UserDetailRepository
import dev.usbharu.hideout.core.domain.model.support.principal.FromApi
import dev.usbharu.hideout.core.infrastructure.factory.PostFactoryImpl
import org.slf4j.Logger
import org.slf4j.LoggerFactory
@ -36,17 +35,13 @@ class RegisterLocalPostApplicationService(
private val postFactory: PostFactoryImpl,
private val actorRepository: ActorRepository,
private val postRepository: PostRepository,
private val userDetailRepository: UserDetailRepository,
transaction: Transaction,
) : AbstractApplicationService<RegisterLocalPost, Long>(transaction, Companion.logger) {
) : LocalUserAbstractApplicationService<RegisterLocalPost, Long>(transaction, Companion.logger) {
override suspend fun internalExecute(command: RegisterLocalPost, principal: Principal): Long {
val actorId = (
userDetailRepository.findById(UserDetailId(command.userDetailId))
?: throw IllegalStateException("actor not found")
).actorId
override suspend fun internalExecute(command: RegisterLocalPost, principal: FromApi): Long {
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,

View File

@ -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<Long>,
val userDetailId: UserDetailId
val mediaIds: List<Long>
)

View File

@ -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<UpdateLocalNote, Unit>(transaction, logger) {
) : LocalUserAbstractApplicationService<UpdateLocalNote, Unit>(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)

View File

@ -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)

View File

@ -16,14 +16,14 @@
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.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.userdetails.UserDetailRepository
import dev.usbharu.hideout.core.domain.model.support.principal.FromApi
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,
) :
AbstractApplicationService<AcceptFollowRequest, Unit>(transaction, logger) {
override suspend fun internalExecute(command: AcceptFollowRequest, principal: Principal) {
LocalUserAbstractApplicationService<AcceptFollowRequest, Unit>(transaction, logger) {
override suspend fun internalExecute(command: AcceptFollowRequest, principal: FromApi) {
val userDetail = userDetailRepository.findById(command.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()

View File

@ -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)

View File

@ -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<Block, Unit>(transaction, logger) {
override suspend fun internalExecute(command: Block, principal: Principal) {
LocalUserAbstractApplicationService<Block, Unit>(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)

View File

@ -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)

View File

@ -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<FollowRequest, Unit>(
) : LocalUserAbstractApplicationService<FollowRequest, Unit>(
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)

View File

@ -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)

View File

@ -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<GetRelationship, Relationship>(
LocalUserAbstractApplicationService<GetRelationship, Relationship>(
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)!!

View File

@ -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)

View File

@ -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<Mute, Unit>(transaction, logger) {
override suspend fun internalExecute(command: Mute, principal: Principal) {
LocalUserAbstractApplicationService<Mute, Unit>(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)

View File

@ -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)

View File

@ -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<RejectFollowRequest, Unit>(transaction, logger) {
override suspend fun internalExecute(command: RejectFollowRequest, principal: Principal) {
LocalUserAbstractApplicationService<RejectFollowRequest, Unit>(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)

View File

@ -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)

View File

@ -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<RemoveFromFollowers, Unit>(transaction, logger) {
override suspend fun internalExecute(command: RemoveFromFollowers, principal: Principal) {
LocalUserAbstractApplicationService<RemoveFromFollowers, Unit>(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)

View File

@ -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)

View File

@ -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<Unblock, Unit>(transaction, logger) {
override suspend fun internalExecute(command: Unblock, principal: Principal) {
LocalUserAbstractApplicationService<Unblock, Unit>(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)

View File

@ -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)

View File

@ -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<Unfollow, Unit>(transaction, logger) {
override suspend fun internalExecute(command: Unfollow, principal: Principal) {
LocalUserAbstractApplicationService<Unfollow, Unit>(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)

View File

@ -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)

View File

@ -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<Unmute, Unit>(transaction, logger) {
LocalUserAbstractApplicationService<Unmute, Unit>(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)

View File

@ -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

View File

@ -11,5 +11,5 @@ abstract class LocalUserAbstractApplicationService<T : Any, R>(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
}

View File

@ -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) {

View File

@ -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
)

View File

@ -0,0 +1,5 @@
package dev.usbharu.hideout.core.domain.model.support.principal
interface PrincipalContextHolder {
suspend fun getPrincipal(): Principal
}

View File

@ -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)

View File

@ -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)
}

View File

@ -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()
}

View File

@ -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
}
}

View File

@ -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)
}
}

View File

@ -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
@ -69,14 +71,31 @@ 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)
val document1 = Document("")
document1.outputSettings().syntax(Document.OutputSettings.Syntax.xml)
document1.insertChildren(0, elements)
return FormattedPostContent(elements.outerHtml().replace("\n", ""), printHtml(elements))
}

View File

@ -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()
}
}

View File

@ -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')"
}

View File

@ -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())
}
}

View File

@ -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

View File

@ -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<String>("uid").toLong()
)
}
}

View File

@ -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<String>("uid").toLong()
val userDetail = principalQueryService.findByUserDetailId(UserDetailId(id))
return FromApi(
userDetail.actorId,
userDetail.userDetailId,
Acct(userDetail.username, userDetail.host)
)
}
}

View File

@ -263,7 +263,7 @@ abstract class AbstractTimelineStore(private val idGenerateService: IdGenerateSe
)
}
abstract suspend fun getActors(actorIds: List<ActorId>): Map<ActorId, Actor>
protected abstract suspend fun getActors(actorIds: List<ActorId>): Map<ActorId, Actor>
abstract suspend fun getUserDetails(userDetailIdList: List<UserDetailId>): Map<UserDetailId, UserDetail>
protected abstract suspend fun getUserDetails(userDetailIdList: List<UserDetailId>): Map<UserDetailId, UserDetail>
}

View File

@ -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")

View File

@ -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)

View File

@ -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
}

View File

@ -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<IllegalArgumentException> {
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<InternalServerException> {
service.execute(GetUserDetail(2), Anonymous)
}
}
}

View File

@ -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<PermissionDeniedException> {
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<IllegalArgumentException> {
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<IllegalArgumentException> {
service.execute(
MigrationLocalActor(1, 2),
FromApi(ActorId(1), UserDetailId(1), Acct("test", "example.com"))
)
}
}
@Test
fun userDetailが見つからなかったら失敗() = runTest {
assertThrows<InternalServerException> {
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<Actor> {
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<IllegalArgumentException> {
service.execute(
MigrationLocalActor(1, 2),
FromApi(ActorId(1), UserDetailId(1), Acct("test", "example.com"))
)
}
}
verify(actorRepository, never()).save(any())
}
}

View File

@ -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<IllegalArgumentException> {
service.execute(RegisterLocalActor("test", "password"), Anonymous)
}
}
@Test
fun ローカルインスタンスが見つからない場合失敗() = runTest {
whenever(actorDomainService.usernameAlreadyUse(eq("test"))).doReturn(false)
assertThrows<InternalServerException> {
service.execute(RegisterLocalActor("test", "password"), Anonymous)
}
}
}

View File

@ -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<PermissionDeniedException> {
service.execute(
DeleteLocalActor(ActorId(2)),
FromApi(ActorId(1), UserDetailId((1)), Acct("test", "example.com"))
)
}
}
@Test
fun ユーザーが存在しない場合失敗() = runTest {
assertThrows<InternalServerException> {
service.execute(
DeleteLocalActor(ActorId(1)),
FromApi(ActorId(1), UserDetailId((1)), Acct("test", "example.com"))
)
}
}
}

View File

@ -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<RegisteredClient> {
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<RegisteredClient> {
verify(registeredClientRepository).save(capture())
val first = allValues.first()
assertThat(first.authorizationGrantTypes).contains(AuthorizationGrantType.REFRESH_TOKEN)
}
}
}

View File

@ -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<IllegalArgumentException> {
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<PermissionDeniedException> {
service.execute(
DeleteFilter(1), FromApi(
ActorId(3), UserDetailId(3),
Acct("test", "example.com")
)
)
}
verify(filterRepository, never()).delete(any())
}
}

View File

@ -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<PermissionDeniedException> {
service.execute(
GetFilter(1), FromApi(
ActorId(3), UserDetailId(3),
Acct("test", "example.com")
)
)
}
}
@Test
fun フィルターが見つからない場合失敗() = runTest {
assertThrows<IllegalArgumentException> {
service.execute(
GetFilter(1), FromApi(
ActorId(3), UserDetailId(3),
Acct("test", "example.com")
)
)
}
}
}

View File

@ -0,0 +1,55 @@
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.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.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 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")))
}
@Test
fun Post主以外はローカルPostを削除できない() = runTest {
whenever(postRepository.findById(PostId(1))).doReturn(TestPostFactory.create(actorId = 2))
assertThrows<PermissionDeniedException> {
service.execute(DeleteLocalPost(1), FromApi(ActorId(3), UserDetailId(3), Acct("test", "example.com")))
}
}
}

View File

@ -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<IllegalArgumentException> {
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<PermissionDeniedException> {
service.execute(GetPost(1), Anonymous)
}
}
}

View File

@ -0,0 +1,80 @@
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
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.*
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)
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, null, false, emptyList()), FromApi(
ActorId(1), UserDetailId(1), Acct("test", "example.com")
)
)
verify(postRepository, times(1)).save(eq(post))
}
@Test
fun actorが見つからないと失敗() = runTest {
assertThrows<InternalServerException> {
service.execute(
RegisterLocalPost("content test", null, Visibility.PUBLIC, null, null, false, emptyList()), FromApi(
ActorId(1), UserDetailId(1), Acct("test", "example.com")
)
)
}
}
}

View File

@ -0,0 +1,152 @@
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
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("<p>test</p>", "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<Post> {
verify(postRepository, times(1)).save(capture())
val first = allValues.first()
assertEquals(
content, first.content
)
}
}
@Test
fun postが見つからない場合失敗() = runTest {
assertThrows<IllegalArgumentException> {
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<PermissionDeniedException> {
service.execute(
UpdateLocalNote(1, null, "test", false, emptyList()), FromApi(
ActorId(1),
UserDetailId(1), Acct("test", "example.com")
)
)
}
}
@Test
fun userDetailが見つからない場合失敗() = runTest {
whenever(postRepository.findById(PostId(1))).doReturn(TestPostFactory.create(id = 1, actorId = 1))
assertThrows<InternalServerException> {
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<InternalServerException> {
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())
}
}

View File

@ -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<InternalServerException> {
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<InternalServerException> {
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<Relationship> {
verify(relationshipRepository).save(capture())
val first = allValues.first()
assertFalse(first.followRequesting)
assertTrue(first.following)
}
}
}

View File

@ -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<Unit, Unit>(TestTransaction, logger) {
override suspend fun internalExecute(command: Unit, principal: FromApi) {
}
}
org.junit.jupiter.api.assertThrows<IllegalArgumentException> {
value.execute(Unit, Anonymous)
}
}
}

View File

@ -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<IllegalArgumentException> {
Domain("a".repeat(1001))
}
}
}

View File

@ -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()
}
}

View File

@ -2,20 +2,47 @@ 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
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 {
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 +52,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 +67,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 +82,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 +97,29 @@ 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)
}
@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)
}

View File

@ -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)
}
}

View File

@ -0,0 +1,75 @@
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 {
@InjectMocks
lateinit var formatter: DefaultPostContentFormatter
@Spy
val policyFactory = HtmlSanitizeConfig().policy()
@Test
fun 文字だけのHTMLをPで囲む() {
val actual = formatter.format("a")
assertEquals(FormattedPostContent("<p>a</p>", "a"), actual)
}
@Test
fun エレメントはそのまま() {
val actual = formatter.format("<p>a</p>")
assertEquals(FormattedPostContent("<p>a</p>", "a"), actual)
}
@Test
fun コメントは無視() {
val actual = formatter.format("<!-- aa -->")
assertEquals(FormattedPostContent("", ""), actual)
}
@Test
fun brタグを改行に() {
val actual = formatter.format("<p>a<br></p>")
assertEquals(FormattedPostContent("<p>a<br /></p>", "a\n"), actual)
}
@Test
fun brタグ2連続を段落に() {
val format = formatter.format("<p>a<br><br>a</p>")
assertEquals(FormattedPostContent("<p>a</p><p>a</p>", "a\n\na"), format)
}
@Test
fun brタグ3連続以上を段落にして改行2つに変換() {
val format = formatter.format("<p>a<br><br><br>a</p>")
assertEquals(FormattedPostContent("<p>a</p><p>a</p>", "a\n\na"), format)
}
@Test
fun aタグは許可される() {
val format = formatter.format("<a href=\"https://example.com\">p</a>")
assertEquals(FormattedPostContent("<a href=\"https://example.com\">p</a>", "p"), format)
}
@Test
fun pの中のaタグも許可される() {
val actual = formatter.format("<p><a href=\"https://example.com\">a</a></p>")
assertEquals(FormattedPostContent("<p><a href=\"https://example.com\">a</a></p>", "a"), actual)
}
}

View File

@ -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<Long>()
coroutineScope {
repeat(500000) {
launch(Dispatchers.IO) {
val id = TwitterSnowflakeIdGenerateService.generateId()
mutex.withLock {
mutableListOf.add(id)
}
}
}
}
assertEquals(0, mutableListOf.size - mutableListOf.toSet().size)
}
}

View File

@ -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<UsernameNotFoundException> {
service.loadUserByUsername(null)
}
verify(actorRepository, never()).findByNameAndDomain(any(), any())
}
@Test
fun actorが見つからない場合失敗() = runTest {
assertThrows<UsernameNotFoundException> {
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<UsernameNotFoundException> {
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)
}
}

View File

@ -0,0 +1,494 @@
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<List<TimelineObject>> {
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<List<TimelineObject>> {
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<List<TimelineObject>> {
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<List<TimelineObject>> {
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<List<TimelineObject>> {
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<List<TimelineObject>> {
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 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<List<TimelineObject>> {
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<List<TimelineObject>> {
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"))
}
}
}
}

View File

@ -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<Relationship> {
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<Relationship> {
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<Relationship> {
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<Account> {
return ResponseEntity.ok(
getAccountApplicationService.execute(
GetAccount(id)
GetAccount(id), principalContextHolder.getPrincipal()
)
)
}
override suspend fun apiV1AccountsIdMutePost(id: String): ResponseEntity<Relationship> {
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<Relationship> {
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<Relationship> {
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<Relationship> {
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<Relationship> {
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<Unit> {
@ -163,9 +158,9 @@ class SpringAccountApi(
}
override suspend fun apiV1AccountsVerifyCredentialsGet(): ResponseEntity<CredentialAccount> {
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<Relationship> {
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<Relationship> {
val executor = oauth2CommandExecutorFactory.getCommandExecutor()
userRejectFollowRequestApplicationService.execute(
RejectFollowRequest(accountId.toLong())
RejectFollowRequest(accountId.toLong()), principalContextHolder.getPrincipal()
)
return fetchRelationship(accountId, executor)
return fetchRelationship(accountId)
}
}

View File

@ -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,

View File

@ -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<Any> {
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<V1Filter> {
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<V1Filter> {
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<Any> {
userDeleteFilterApplicationService.execute(
DeleteFilter(id.toLong())
DeleteFilter(id.toLong()), principalContextHolder.getPrincipal()
)
return ResponseEntity.ok(Unit)
}
override suspend fun apiV2FiltersIdGet(id: String): ResponseEntity<Filter> {
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<Filter> {
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))
}

View File

@ -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(

View File

@ -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<Status> {
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<Status> {
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
)